From 9ec1715a900e682a11d311787227d9afaa18267c Mon Sep 17 00:00:00 2001 From: John Marks Date: Tue, 10 Sep 2013 17:00:18 +0100 Subject: [PATCH 001/333] RefCount work in progress --- .../rxjava-core/rx/operators/README.txt | 5 ++ .../rxjava-core/rx/operators/package.html | 23 +++++ classes/test/rxjava-core/README.md | 8 ++ .../test/rxjava-core/rx/operators/README.txt | 5 ++ .../rxjava-core/rx/operators/package.html | 23 +++++ rxjava-core/src/main/java/rx/Observable.java | 2 +- .../rx/observables/ConnectableObservable.java | 20 +++++ .../java/rx/operators/OperationRefCount.java | 86 +++++++++++++++++++ 8 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 classes/production/rxjava-core/rx/operators/README.txt create mode 100644 classes/production/rxjava-core/rx/operators/package.html create mode 100644 classes/test/rxjava-core/README.md create mode 100644 classes/test/rxjava-core/rx/operators/README.txt create mode 100644 classes/test/rxjava-core/rx/operators/package.html create mode 100644 rxjava-core/src/main/java/rx/operators/OperationRefCount.java diff --git a/classes/production/rxjava-core/rx/operators/README.txt b/classes/production/rxjava-core/rx/operators/README.txt new file mode 100644 index 0000000000..c2d441a10c --- /dev/null +++ b/classes/production/rxjava-core/rx/operators/README.txt @@ -0,0 +1,5 @@ +This package "rx.operators" is for internal implementation details and can change at any time. + +It is excluded from the public Javadocs (http://netflix.github.io/RxJava/javadoc/) and should not be relied upon by any code. + +In short, changes to public signatures of these classes will not be accounted for in the versioning of RxJava. \ No newline at end of file diff --git a/classes/production/rxjava-core/rx/operators/package.html b/classes/production/rxjava-core/rx/operators/package.html new file mode 100644 index 0000000000..80ba7542bf --- /dev/null +++ b/classes/production/rxjava-core/rx/operators/package.html @@ -0,0 +1,23 @@ + + +

Operators that allow composing Observables to transform and + manipulate data in an asynchronous, functional and thread-safe manner.

+

The operators are all exposed via the ObservableExtensions class

+ + \ No newline at end of file diff --git a/classes/test/rxjava-core/README.md b/classes/test/rxjava-core/README.md new file mode 100644 index 0000000000..74b6c91536 --- /dev/null +++ b/classes/test/rxjava-core/README.md @@ -0,0 +1,8 @@ +This test folder only contains performance and functional/integration style tests. + +The unit tests themselves are embedded as inner classes of the Java code (such as here: [rxjava-core/src/main/java/rx/operators](https://github.com/Netflix/RxJava/tree/master/rxjava-core/src/main/java/rx/operators)). + +* For an explanation of this design choice see +Ben J. Christensen's [JUnit Tests as Inner Classes](http://benjchristensen.com/2011/10/23/junit-tests-as-inner-classes/). + +Also, each of the language adaptors has a /src/test/ folder which further testing (see Groovy for an example: [language-adaptors/rxjava-groovy/src/test](https://github.com/Netflix/RxJava/tree/master/language-adaptors/rxjava-groovy/src/test)). diff --git a/classes/test/rxjava-core/rx/operators/README.txt b/classes/test/rxjava-core/rx/operators/README.txt new file mode 100644 index 0000000000..c2d441a10c --- /dev/null +++ b/classes/test/rxjava-core/rx/operators/README.txt @@ -0,0 +1,5 @@ +This package "rx.operators" is for internal implementation details and can change at any time. + +It is excluded from the public Javadocs (http://netflix.github.io/RxJava/javadoc/) and should not be relied upon by any code. + +In short, changes to public signatures of these classes will not be accounted for in the versioning of RxJava. \ No newline at end of file diff --git a/classes/test/rxjava-core/rx/operators/package.html b/classes/test/rxjava-core/rx/operators/package.html new file mode 100644 index 0000000000..80ba7542bf --- /dev/null +++ b/classes/test/rxjava-core/rx/operators/package.html @@ -0,0 +1,23 @@ + + +

Operators that allow composing Observables to transform and + manipulate data in an asynchronous, functional and thread-safe manner.

+

The operators are all exposed via the ObservableExtensions class

+ + \ No newline at end of file diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index f6431926e9..5b4444ceb0 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4392,7 +4392,7 @@ public BlockingObservable toBlockingObservable() { * * NOTE: If strong reasons for not depending on package names comes up then the implementation of this method can change to looking for a marker interface. * - * @param f + * @param o * @return {@code true} if the given function is an internal implementation, and {@code false} otherwise. */ private boolean isInternalImplementation(Object o) { diff --git a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java index de0fdba643..56f904ea7e 100644 --- a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java +++ b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java @@ -18,6 +18,7 @@ import rx.Observable; import rx.Observer; import rx.Subscription; +import rx.operators.OperationRefCount; import rx.util.functions.Func1; /** @@ -47,4 +48,23 @@ protected ConnectableObservable(Func1, Subscription> onSubscribe) { */ public abstract Subscription connect(); + /** + * Returns an observable sequence that stays connected to the source as long + * as there is at least one subscription to the observable sequence. + * @return a {@link Observable} + */ + public Observable refCount() { + return refCount(this); + } + + /** + * Returns an observable sequence that stays connected to the source as long + * as there is at least one subscription to the observable sequence. + * @param that + * a {@link ConnectableObservable} + * @return a {@link Observable} + */ + public static Observable refCount(ConnectableObservable that) { + return Observable.create(OperationRefCount.refCount(that)); + } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationRefCount.java b/rxjava-core/src/main/java/rx/operators/OperationRefCount.java new file mode 100644 index 0000000000..11410557ea --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationRefCount.java @@ -0,0 +1,86 @@ +/** + * Copyright 2013 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.operators; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.observables.ConnectableObservable; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Func1; + +import static org.mockito.Mockito.*; + +/** + * Returns an observable sequence that stays connected to the source as long + * as there is at least one subscription to the observable sequence. + */ +public final class OperationRefCount { + public static Func1, Subscription> refCount(ConnectableObservable connectableObservable) { + return new RefCount(connectableObservable); + } + + private static class RefCount implements Func1, Subscription> { + private final ConnectableObservable innerConnectableObservable; + + public RefCount(ConnectableObservable innerConnectableObservable) { + this.innerConnectableObservable = innerConnectableObservable; + } + + @Override + public Subscription call(Observer observer) { + throw new UnsupportedOperationException(); + } + } + + public static class UnitTest { + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void subscriptionToUnderlyingOnFirstSubscription() { + ConnectableObservable connectable = mock(ConnectableObservable.class); + Observable refCounted = Observable.create(refCount(connectable)); + Observer observer = mock(Observer.class); + when(connectable.subscribe(observer)).thenReturn(Subscriptions.empty()); + when(connectable.connect()).thenReturn(Subscriptions.empty()); + refCounted.subscribe(observer); + verify(connectable, times(1)).subscribe(observer); + verify(connectable, times(1)).connect(); + } + + @Test + public void noSubscriptionToUnderlyingOnSecondSubscription() { + + } + + @Test + public void unsubscriptionFromUnderlyingOnLastUnsubscription() { + + } + + @Test + public void noUnsubscriptionFromUnderlyingOnFirstUnsubscription() { + + } + } +} From d3d27577c7f9b20f7b543f16f025c33eb31c2fc7 Mon Sep 17 00:00:00 2001 From: John Marks Date: Tue, 10 Sep 2013 18:04:50 +0100 Subject: [PATCH 002/333] RefCount work in progress --- .../java/rx/operators/OperationRefCount.java | 70 +++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationRefCount.java b/rxjava-core/src/main/java/rx/operators/OperationRefCount.java index 11410557ea..3c64397094 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationRefCount.java +++ b/rxjava-core/src/main/java/rx/operators/OperationRefCount.java @@ -17,12 +17,15 @@ import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; import org.mockito.MockitoAnnotations; import rx.Observable; import rx.Observer; import rx.Subscription; import rx.observables.ConnectableObservable; import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; import rx.util.functions.Func1; import static org.mockito.Mockito.*; @@ -38,6 +41,9 @@ public static Func1, Subscription> refCount(ConnectableObservabl private static class RefCount implements Func1, Subscription> { private final ConnectableObservable innerConnectableObservable; + private final Object gate = new Object(); + private int count = 0; + private Subscription connection = null; public RefCount(ConnectableObservable innerConnectableObservable) { this.innerConnectableObservable = innerConnectableObservable; @@ -45,10 +51,28 @@ public RefCount(ConnectableObservable innerConnectableObservable) { @Override public Subscription call(Observer observer) { - throw new UnsupportedOperationException(); + final Subscription subscription = innerConnectableObservable.subscribe(observer); + synchronized (gate) { + if (count++ == 0) { + connection = innerConnectableObservable.connect(); + } + } + return Subscriptions.create(new Action0() { + @Override + public void call() { + synchronized (gate) { + if (--count == 0) { + connection.unsubscribe(); + connection = null; + } + } + subscription.unsubscribe(); + } + }); } } + @RunWith(JUnit4.class) public static class UnitTest { @Before @@ -58,8 +82,10 @@ public void setUp() { @Test public void subscriptionToUnderlyingOnFirstSubscription() { + @SuppressWarnings("unchecked") ConnectableObservable connectable = mock(ConnectableObservable.class); Observable refCounted = Observable.create(refCount(connectable)); + @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); when(connectable.subscribe(observer)).thenReturn(Subscriptions.empty()); when(connectable.connect()).thenReturn(Subscriptions.empty()); @@ -70,17 +96,53 @@ public void subscriptionToUnderlyingOnFirstSubscription() { @Test public void noSubscriptionToUnderlyingOnSecondSubscription() { - + @SuppressWarnings("unchecked") + ConnectableObservable connectable = mock(ConnectableObservable.class); + Observable refCounted = Observable.create(refCount(connectable)); + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + when(connectable.subscribe(observer)).thenReturn(Subscriptions.empty()); + when(connectable.connect()).thenReturn(Subscriptions.empty()); + refCounted.subscribe(observer); + refCounted.subscribe(observer); + verify(connectable, times(2)).subscribe(observer); + verify(connectable, times(1)).connect(); } @Test public void unsubscriptionFromUnderlyingOnLastUnsubscription() { - + @SuppressWarnings("unchecked") + ConnectableObservable connectable = mock(ConnectableObservable.class); + Observable refCounted = Observable.create(refCount(connectable)); + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Subscription underlying = mock(Subscription.class); + when(connectable.subscribe(observer)).thenReturn(underlying); + Subscription connection = mock(Subscription.class); + when(connectable.connect()).thenReturn(connection); + Subscription first = refCounted.subscribe(observer); + first.unsubscribe(); + verify(underlying, times(1)).unsubscribe(); + verify(connection, times(1)).unsubscribe(); } @Test public void noUnsubscriptionFromUnderlyingOnFirstUnsubscription() { - + @SuppressWarnings("unchecked") + ConnectableObservable connectable = mock(ConnectableObservable.class); + Observable refCounted = Observable.create(refCount(connectable)); + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Subscription underlying = mock(Subscription.class); + when(connectable.subscribe(observer)).thenReturn(underlying); + Subscription connection = mock(Subscription.class); + when(connectable.connect()).thenReturn(connection); + Subscription first = refCounted.subscribe(observer); + Subscription second = refCounted.subscribe(observer); + first.unsubscribe(); + second.unsubscribe(); + verify(underlying, times(2)).unsubscribe(); + verify(connection, times(1)).unsubscribe(); } } } From 531ff06861db3d7d0bf602a0a1ea446fbbefb579 Mon Sep 17 00:00:00 2001 From: John Marks Date: Wed, 11 Sep 2013 17:46:24 +0100 Subject: [PATCH 003/333] Merge and work on RefCount --- .../java/rx/observables/ConnectableObservable.java | 13 +------------ .../main/java/rx/operators/OperationRefCount.java | 1 - 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java index d5c503edec..1595c31195 100644 --- a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java +++ b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java @@ -54,17 +54,6 @@ protected ConnectableObservable(OnSubscribeFunc onSubscribe) { * @return a {@link Observable} */ public Observable refCount() { - return refCount(this); - } - - /** - * Returns an observable sequence that stays connected to the source as long - * as there is at least one subscription to the observable sequence. - * @param that - * a {@link ConnectableObservable} - * @return a {@link Observable} - */ - public static Observable refCount(ConnectableObservable that) { - return Observable.create(OperationRefCount.refCount(that)); + return Observable.create(OperationRefCount.refCount(this)); } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationRefCount.java b/rxjava-core/src/main/java/rx/operators/OperationRefCount.java index b814512a11..97cb8f738f 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationRefCount.java +++ b/rxjava-core/src/main/java/rx/operators/OperationRefCount.java @@ -70,7 +70,6 @@ public void call() { }); } - @RunWith(JUnit4.class) public static class UnitTest { @Before From b407e6e26bd0708881e3bce14e4afc9721da0bd1 Mon Sep 17 00:00:00 2001 From: John Marks Date: Wed, 11 Sep 2013 18:22:23 +0100 Subject: [PATCH 004/333] Merge and work on RefCount --- .../src/main/java/rx/Observable.java.orig | 3914 ----------------- .../rx/observables/ConnectableObservable.java | 11 +- .../java/rx/operators/OperationRefCount.java | 80 - .../src/test/java/rx/RefCountTests.java | 82 + 4 files changed, 92 insertions(+), 3995 deletions(-) delete mode 100644 rxjava-core/src/main/java/rx/Observable.java.orig create mode 100644 rxjava-core/src/test/java/rx/RefCountTests.java diff --git a/rxjava-core/src/main/java/rx/Observable.java.orig b/rxjava-core/src/main/java/rx/Observable.java.orig deleted file mode 100644 index ff79c47636..0000000000 --- a/rxjava-core/src/main/java/rx/Observable.java.orig +++ /dev/null @@ -1,3914 +0,0 @@ -/** - * Copyright 2013 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 rx.util.functions.Functions.not; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import rx.concurrency.Schedulers; -import rx.observables.BlockingObservable; -import rx.observables.ConnectableObservable; -import rx.observables.GroupedObservable; -import rx.operators.OperationAll; -import rx.operators.OperationAverage; -import rx.operators.OperationBuffer; -import rx.operators.OperationCache; -import rx.operators.OperationCombineLatest; -import rx.operators.OperationConcat; -import rx.operators.OperationDefer; -import rx.operators.OperationDematerialize; -import rx.operators.OperationFilter; -import rx.operators.OperationFinally; -import rx.operators.OperationFirstOrDefault; -import rx.operators.OperationGroupBy; -import rx.operators.OperationInterval; -import rx.operators.OperationMap; -import rx.operators.OperationMaterialize; -import rx.operators.OperationMerge; -import rx.operators.OperationMergeDelayError; -import rx.operators.OperationMulticast; -import rx.operators.OperationObserveOn; -import rx.operators.OperationOnErrorResumeNextViaFunction; -import rx.operators.OperationOnErrorResumeNextViaObservable; -import rx.operators.OperationOnErrorReturn; -import rx.operators.OperationOnExceptionResumeNextViaObservable; -import rx.operators.OperationRetry; -import rx.operators.OperationSample; -import rx.operators.OperationScan; -import rx.operators.OperationSkip; -import rx.operators.OperationSkipWhile; -import rx.operators.OperationSubscribeOn; -import rx.operators.OperationSum; -import rx.operators.OperationSwitch; -import rx.operators.OperationSynchronize; -import rx.operators.OperationTake; -import rx.operators.OperationTakeLast; -import rx.operators.OperationTakeUntil; -import rx.operators.OperationTakeWhile; -import rx.operators.OperationTimestamp; -import rx.operators.OperationToObservableFuture; -import rx.operators.OperationToObservableIterable; -import rx.operators.OperationToObservableList; -import rx.operators.OperationToObservableSortedList; -import rx.operators.OperationWindow; -import rx.operators.OperationZip; -import rx.operators.SafeObservableSubscription; -import rx.operators.SafeObserver; -import rx.plugins.RxJavaErrorHandler; -import rx.plugins.RxJavaObservableExecutionHook; -import rx.plugins.RxJavaPlugins; -import rx.subjects.PublishSubject; -import rx.subjects.ReplaySubject; -import rx.subjects.Subject; -import rx.subscriptions.Subscriptions; -import rx.util.Closing; -import rx.util.OnErrorNotImplementedException; -import rx.util.Opening; -import rx.util.Range; -import rx.util.Timestamped; -import rx.util.functions.Action0; -import rx.util.functions.Action1; -import rx.util.functions.Func0; -import rx.util.functions.Func1; -import rx.util.functions.Func2; -import rx.util.functions.Func3; -import rx.util.functions.Func4; -import rx.util.functions.Func5; -import rx.util.functions.Func6; -import rx.util.functions.Func7; -import rx.util.functions.Func8; -import rx.util.functions.Func9; -import rx.util.functions.FuncN; -import rx.util.functions.Function; - -/** - * The Observable interface that implements the Reactive Pattern. - *

- * This interface provides overloaded methods for subscribing as well as delegate methods to the - * various operators. - *

- * The documentation for this interface makes use of marble diagrams. The following legend explains - * these diagrams: - *

- * - *

- * For more information see the RxJava Wiki - * - * @param - */ -public class Observable { - - /** - * Executed when 'subscribe' is invoked. - */ - private final OnSubscribeFunc onSubscribe; - - /** - * Function interface for work to be performed when an {@link Observable} is subscribed to via {@link Observable#subscribe(Observer)} - * - * @param - */ - public static interface OnSubscribeFunc extends Function { - - public Subscription onSubscribe(Observer t1); - - } - - /** - * Observable with Function to execute when subscribed to. - *

- * NOTE: Use {@link #create(OnSubscribeFunc)} to create an Observable instead of this constructor unless you - * specifically have a need for inheritance. - * - * @param onSubscribe - * {@link OnSubscribeFunc} to be executed when {@link #subscribe(Observer)} is called. - */ - protected Observable(OnSubscribeFunc onSubscribe) { - this.onSubscribe = onSubscribe; - } - - private final static RxJavaObservableExecutionHook hook = RxJavaPlugins.getInstance().getObservableExecutionHook(); - - /** - * An {@link Observer} must call an Observable's {@code subscribe} method in order to - * receive items and notifications from the Observable. - * - *

A typical implementation of {@code subscribe} does the following: - *

- * It stores a reference to the Observer in a collection object, such as a {@code List} object. - *

- * It returns a reference to the {@link Subscription} interface. This enables Observers to - * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's {@link Observer#onCompleted onCompleted} method. - *

- * An Observable<T> instance is responsible for accepting all subscriptions - * and notifying all Observers. Unless the documentation for a particular - * Observable<T> implementation indicates otherwise, Observers should make no - * assumptions about the order in which multiple Observers will receive their notifications. - *

- * For more information see the - * RxJava Wiki - * - * @param observer - * the observer - * @return a {@link Subscription} reference with which the {@link Observer} can stop receiving items - * before the Observable has finished sending them - * @throws IllegalArgumentException - * if the {@link Observer} provided as the argument to {@code subscribe()} is {@code null} - */ - public Subscription subscribe(Observer observer) { - // allow the hook to intercept and/or decorate - OnSubscribeFunc onSubscribeFunction = hook.onSubscribeStart(this, onSubscribe); - // validate and proceed - if (observer == null) { - throw new IllegalArgumentException("observer can not be null"); - } - if (onSubscribeFunction == null) { - throw new IllegalStateException("onSubscribe function can not be null."); - // the subscribe function can also be overridden but generally that's not the appropriate approach so I won't mention that in the exception - } - try { - /** - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - if (isInternalImplementation(observer)) { - Subscription s = onSubscribeFunction.onSubscribe(observer); - if (s == null) { - // this generally shouldn't be the case on a 'trusted' onSubscribe but in case it happens - // we want to gracefully handle it the same as AtomicObservableSubscription does - return hook.onSubscribeReturn(this, Subscriptions.empty()); - } else { - return hook.onSubscribeReturn(this, s); - } - } else { - SafeObservableSubscription subscription = new SafeObservableSubscription(); - subscription.wrap(onSubscribeFunction.onSubscribe(new SafeObserver(subscription, observer))); - return hook.onSubscribeReturn(this, subscription); - } - } catch (OnErrorNotImplementedException e) { - // special handling when onError is not implemented ... we just rethrow - throw e; - } catch (Throwable e) { - // if an unhandled error occurs executing the onSubscribe we will propagate it - try { - observer.onError(hook.onSubscribeError(this, e)); - } catch (OnErrorNotImplementedException e2) { - // special handling when onError is not implemented ... we just rethrow - throw e2; - } catch (Throwable e2) { - // if this happens it means the onError itself failed (perhaps an invalid function implementation) - // so we are unable to propagate the error correctly and will just throw - RuntimeException r = new RuntimeException("Error occurred attempting to subscribe [" + e.getMessage() + "] and then again while trying to pass to onError.", e2); - hook.onSubscribeError(this, r); - throw r; - } - return Subscriptions.empty(); - } - } - - /** - * An {@link Observer} must call an Observable's {@code subscribe} method in order to - * receive items and notifications from the Observable. - * - *

A typical implementation of {@code subscribe} does the following: - *

- * It stores a reference to the Observer in a collection object, such as a {@code List} object. - *

- * It returns a reference to the {@link Subscription} interface. This enables Observers to - * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's {@link Observer#onCompleted onCompleted} method. - *

- * An {@code Observable} instance is responsible for accepting all subscriptions - * and notifying all Observers. Unless the documentation for a particular {@code Observable} implementation indicates otherwise, Observers should make no - * assumptions about the order in which multiple Observers will receive their notifications. - *

- * For more information see the - * RxJava Wiki - * - * @param observer - * the observer - * @param scheduler - * the {@link Scheduler} on which Observers subscribe to the Observable - * @return a {@link Subscription} reference with which Observers can stop receiving items and - * notifications before the Observable has finished sending them - * @throws IllegalArgumentException - * if an argument to {@code subscribe()} is {@code null} - */ - public Subscription subscribe(Observer observer, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(observer); - } - - /** - * Used for protecting against errors being thrown from Observer implementations and ensuring onNext/onError/onCompleted contract compliance. - *

- * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - private Subscription protectivelyWrapAndSubscribe(Observer o) { - SafeObservableSubscription subscription = new SafeObservableSubscription(); - return subscription.wrap(subscribe(new SafeObserver(subscription, o))); - } - - public Subscription subscribe(final Action1 onNext) { - if (onNext == null) { - throw new IllegalArgumentException("onNext can not be null"); - } - - /** - * Wrapping since raw functions provided by the user are being invoked. - * - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - return protectivelyWrapAndSubscribe(new Observer() { - - @Override - public void onCompleted() { - // do nothing - } - - @Override - public void onError(Throwable e) { - handleError(e); - throw new OnErrorNotImplementedException(e); - } - - @Override - public void onNext(T args) { - onNext.call(args); - } - - }); - } - - public Subscription subscribe(final Action1 onNext, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(onNext); - } - - public Subscription subscribe(final Action1 onNext, final Action1 onError) { - if (onNext == null) { - throw new IllegalArgumentException("onNext can not be null"); - } - if (onError == null) { - throw new IllegalArgumentException("onError can not be null"); - } - - /** - * Wrapping since raw functions provided by the user are being invoked. - * - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - return protectivelyWrapAndSubscribe(new Observer() { - - @Override - public void onCompleted() { - // do nothing - } - - @Override - public void onError(Throwable e) { - handleError(e); - onError.call(e); - } - - @Override - public void onNext(T args) { - onNext.call(args); - } - - }); - } - - public Subscription subscribe(final Action1 onNext, final Action1 onError, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(onNext, onError); - } - - public Subscription subscribe(final Action1 onNext, final Action1 onError, final Action0 onComplete) { - if (onNext == null) { - throw new IllegalArgumentException("onNext can not be null"); - } - if (onError == null) { - throw new IllegalArgumentException("onError can not be null"); - } - if (onComplete == null) { - throw new IllegalArgumentException("onComplete can not be null"); - } - - /** - * Wrapping since raw functions provided by the user are being invoked. - * - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - return protectivelyWrapAndSubscribe(new Observer() { - - @Override - public void onCompleted() { - onComplete.call(); - } - - @Override - public void onError(Throwable e) { - handleError(e); - onError.call(e); - } - - @Override - public void onNext(T args) { - onNext.call(args); - } - - }); - } - - public Subscription subscribe(final Action1 onNext, final Action1 onError, final Action0 onComplete, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(onNext, onError, onComplete); - } - - /** - * Returns a {@link ConnectableObservable} that upon connection causes the source Observable to - * push results into the specified subject. - * - * @param subject - * the {@link Subject} for the {@link ConnectableObservable} to push source items - * into - * @param - * result type - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * push results into the specified {@link Subject} - */ - public ConnectableObservable multicast(Subject subject) { - return OperationMulticast.multicast(this, subject); - } - - /** - * Allow the {@link RxJavaErrorHandler} to receive the exception from onError. - * - * @param e - */ - private void handleError(Throwable e) { - // onError should be rare so we'll only fetch when needed - RxJavaPlugins.getInstance().getErrorHandler().handleError(e); - } - - /** - * An Observable that never sends any information to an {@link Observer}. - * - * This Observable is useful primarily for testing purposes. - * - * @param - * the type of item emitted by the Observable - */ - private static class NeverObservable extends Observable { - public NeverObservable() { - super(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(Observer t1) { - return Subscriptions.empty(); - } - - }); - } - } - - /** - * an Observable that invokes {@link Observer#onError onError} when the {@link Observer} subscribes to it. - * - * @param - * the type of item emitted by the Observable - */ - private static class ThrowObservable extends Observable { - - public ThrowObservable(final Throwable exception) { - super(new OnSubscribeFunc() { - - /** - * Accepts an {@link Observer} and calls its {@link Observer#onError onError} method. - * - * @param observer - * an {@link Observer} of this Observable - * @return a reference to the subscription - */ - @Override - public Subscription onSubscribe(Observer observer) { - observer.onError(exception); - return Subscriptions.empty(); - } - - }); - } - - } - - /** - * Creates an Observable that will execute the given function when an {@link Observer} subscribes to it. - *

- * - *

- * Write the function you pass to create so that it behaves as an Observable: It - * should invoke the Observer's {@link Observer#onNext onNext}, {@link Observer#onError onError}, and {@link Observer#onCompleted onCompleted} methods - * appropriately. - *

- * A well-formed Observable must invoke either the Observer's onCompleted method - * exactly once or its onError method exactly once. - *

- * See Rx Design Guidelines (PDF) - * for detailed information. - * - * @param - * the type of the items that this Observable emits - * @param func - * a function that accepts an {@code Observer}, invokes its {@code onNext}, {@code onError}, and {@code onCompleted} methods - * as appropriate, and returns a {@link Subscription} to allow the Observer to - * canceling the subscription - * @return an Observable that, when an {@link Observer} subscribes to it, will execute the given - * function - */ - public static Observable create(OnSubscribeFunc func) { - return new Observable(func); - } - - /** - * Returns an Observable that emits no data to the {@link Observer} and immediately invokes - * its {@link Observer#onCompleted onCompleted} method. - *

- * - * - * @param - * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that returns no data to the {@link Observer} and immediately invokes - * the {@link Observer}'s {@link Observer#onCompleted() onCompleted} method - */ - public static Observable empty() { - return from(new ArrayList()); - } - - /** - * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it - *

- * - * - * @param exception - * the particular error to report - * @param - * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it - */ - public static Observable error(Throwable exception) { - return new ThrowObservable(exception); - } - - /** - * Converts an {@link Iterable} sequence into an Observable. - *

- * - * - *

Implementation note: the entire iterable sequence will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param iterable - * the source {@link Iterable} sequence - * @param - * the type of items in the {@link Iterable} sequence and the type of items to be - * emitted by the resulting Observable - * @return an Observable that emits each item in the source {@link Iterable} sequence - */ - public static Observable from(Iterable iterable) { - return create(OperationToObservableIterable.toObservableIterable(iterable)); - } - - /** - * Converts an Array into an Observable. - *

- * - * - *

Implementation note: the entire iterable sequence will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param array - * the source sequence - * @param - * the type of items in the {@link Iterable} sequence and the type of items to be - * emitted by the resulting Observable - * @return an Observable that emits each item in the source {@link Iterable} sequence - */ - public static Observable from(T[] items) { - return create(OperationToObservableIterable.toObservableIterable(Arrays.asList(items))); - } - - /** - * Converts a series of items into an Observable. - *

- * - * - *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1) { - return from(Arrays.asList(t1)); - } - - /** - * Converts a series of items into an Observable. - *

- * - * - *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2) { - return from(Arrays.asList(t1, t2)); - } - - /** - * Converts a series of items into an Observable. - *

- * - * - *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3) { - return from(Arrays.asList(t1, t2, t3)); - } - - /** - * Converts a series of items into an Observable. - *

- * - * - *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4) { - return from(Arrays.asList(t1, t2, t3, t4)); - } - - /** - * Converts a series of items into an Observable. - *

- * - * - *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5) { - return from(Arrays.asList(t1, t2, t3, t4, t5)); - } - - /** - * Converts a series of items into an Observable. - *

- * - * - *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param t6 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6) { - return from(Arrays.asList(t1, t2, t3, t4, t5, t6)); - } - - /** - * Converts a series of items into an Observable. - *

- * - * - *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param t6 - * item - * @param t7 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { - return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7)); - } - - /** - * Converts a series of items into an Observable. - *

- * - * - *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param t6 - * item - * @param t7 - * item - * @param t8 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { - return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8)); - } - - /** - * Converts a series of items into an Observable. - *

- * - * - *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param t6 - * item - * @param t7 - * item - * @param t8 - * item - * @param t9 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { - return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8, t9)); - } - - /** - * Converts a series of items into an Observable. - *

- * - * - *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param t6 - * item - * @param t7 - * item - * @param t8 - * item - * @param t10 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9, T t10) { - return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)); - } - - /** - * Generates an Observable that emits a sequence of integers within a specified range. - *

- * - * - *

Implementation note: the entire range will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param start - * the value of the first integer in the sequence - * @param count - * the number of sequential integers to generate - * - * @return an Observable that emits a range of sequential integers - * - * @see Observable.Range Method (Int32, Int32) - */ - public static Observable range(int start, int count) { - return from(Range.createWithCount(start, count)); - } - - /** - * Returns an Observable that calls an Observable factory to create its Observable for each - * new Observer that subscribes. That is, for each subscriber, the actuall Observable is determined - * by the factory function. - * - *

- * - *

- * The defer operator allows you to defer or delay emitting items from an Observable until such - * time as an Observer subscribes to the Observable. This allows an {@link Observer} to easily - * obtain updates or a refreshed version of the sequence. - * - * @param observableFactory - * the Observable factory function to invoke for each {@link Observer} that - * subscribes to the resulting Observable - * @param - * the type of the items emitted by the Observable - * @return an Observable whose {@link Observer}s trigger an invocation of the given Observable - * factory function - */ - public static Observable defer(Func0> observableFactory) { - return create(OperationDefer.defer(observableFactory)); - } - - /** - * Returns an Observable that emits a single item and then completes. - *

- * - *

- * To convert any object into an Observable that emits that object, pass that object into the - * just method. - *

- * This is similar to the {@link #from(java.lang.Object[])} method, except that - * from() will convert an {@link Iterable} object into an Observable that emits - * each of the items in the Iterable, one at a time, while the just() method - * converts an Iterable into an Observable that emits the entire Iterable as a single item. - * - * @param value - * the item to pass to the {@link Observer}'s {@link Observer#onNext onNext} method - * @param - * the type of that item - * @return an Observable that emits a single item and then completes - */ - public static Observable just(T value) { - List list = new ArrayList(); - list.add(value); - - return from(list); - } - - /** - * Flattens a sequence of Observables emitted by an Observable into one Observable, without any - * transformation. - *

- * - *

- * You can combine the items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param source - * an Observable that emits Observables - * @return an Observable that emits items that are the result of flattening the items emitted - * by the Observables emitted by the {@code source} Observable - * @see MSDN: Observable.Merge Method - */ - public static Observable merge(Observable> source) { - return create(OperationMerge.merge(source)); - } - - /** - * Flattens a series of Observables into one Observable, without any transformation. - *

- * - *

- * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2) { - return create(OperationMerge.merge(t1, t2)); - } - - /** - * Flattens a series of Observables into one Observable, without any transformation. - *

- * - *

- * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3) { - return create(OperationMerge.merge(t1, t2, t3)); - } - - /** - * Flattens a series of Observables into one Observable, without any transformation. - *

- * - *

- * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4) { - return create(OperationMerge.merge(t1, t2, t3, t4)); - } - - /** - * Flattens a series of Observables into one Observable, without any transformation. - *

- * - *

- * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { - return create(OperationMerge.merge(t1, t2, t3, t4, t5)); - } - - /** - * Flattens a series of Observables into one Observable, without any transformation. - *

- * - *

- * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { - return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6)); - } - - /** - * Flattens a series of Observables into one Observable, without any transformation. - *

- * - *

- * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { - return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6, t7)); - } - - /** - * Flattens a series of Observables into one Observable, without any transformation. - *

- * - *

- * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @param t8 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { - return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6, t7, t8)); - } - - /** - * Flattens a series of Observables into one Observable, without any transformation. - *

- * - *

- * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @param t8 - * an Observable to be merged - * @param t9 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8, Observable t9) { - return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6, t7, t8, t9)); - } - - /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the - * other. - *

- * - * - * @param observables - * an Observable of Observables - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method - */ - public static Observable concat(Observable> observables) { - return create(OperationConcat.concat(observables)); - } - - /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the - * other. - *

- * - * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2) { - return create(OperationConcat.concat(t1, t2)); - } - - /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the - * other. - *

- * - * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3) { - return create(OperationConcat.concat(t1, t2, t3)); - } - - /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the - * other. - *

- * - * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4) { - return create(OperationConcat.concat(t1, t2, t3, t4)); - } - - /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the - * other. - *

- * - * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @param t5 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { - return create(OperationConcat.concat(t1, t2, t3, t4, t5)); - } - - /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the - * other. - *

- * - * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @param t5 - * an Observable to be concatenated - * @param t6 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { - return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6)); - } - - /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the - * other. - *

- * - * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @param t5 - * an Observable to be concatenated - * @param t6 - * an Observable to be concatenated - * @param t7 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { - return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6, t7)); - } - - /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the - * other. - *

- * - * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @param t5 - * an Observable to be concatenated - * @param t6 - * an Observable to be concatenated - * @param t7 - * an Observable to be concatenated - * @param t8 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { - return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6, t7, t8)); - } - - /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the - * other. - *

- * - * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @param t5 - * an Observable to be concatenated - * @param t6 - * an Observable to be concatenated - * @param t7 - * an Observable to be concatenated - * @param t8 - * an Observable to be concatenated - * @param t9 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8, Observable t9) { - return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6, t7, t8, t9)); - } - - /** - * This behaves like {@link #merge(Observable)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param source - * an Observable that emits Observables - * @return an Observable that emits items that are the result of flattening the items emitted by - * the Observables emitted by the {@code source} Observable - * @see MSDN: Observable.Merge Method - */ - public static Observable mergeDelayError(Observable> source) { - return create(OperationMergeDelayError.mergeDelayError(source)); - } - - /** - * This behaves like {@link #merge(Observable...)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2)); - } - - /** - * This behaves like {@link #merge(Observable...)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3)); - } - - /** - * This behaves like {@link #merge(Observable...)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4)); - } - - /** - * This behaves like {@link #merge(Observable...)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5)); - } - - /** - * This behaves like {@link #merge(Observable...)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6)); - } - - /** - * This behaves like {@link #merge(Observable...)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6, t7)); - } - - /** - * This behaves like {@link #merge(Observable...)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @param t8 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6, t7, t8)); - } - - /** - * This behaves like {@link #merge(Observable...)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @param t8 - * an Observable to be merged - * @param t9 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8, Observable t9) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6, t7, t8, t9)); - } - - /** - * Returns an Observable that never sends any items or notifications to an {@link Observer}. - *

- * - *

- * This Observable is useful primarily for testing purposes. - * - * @param - * the type of items (not) emitted by the Observable - * @return an Observable that never sends any items or notifications to an {@link Observer} - */ - public static Observable never() { - return new NeverObservable(); - } - - /** - * Given an Observable that emits Observables, creates a single Observable that - * emits the items emitted by the most recently published of those Observables. - *

- * - * - * @param sequenceOfSequences - * the source Observable that emits Observables - * @return an Observable that emits only the items emitted by the most recently published - * Observable - * @deprecated Being renamed to {@link #switchOnNext} - */ - @Deprecated - public static Observable switchDo(Observable> sequenceOfSequences) { - return create(OperationSwitch.switchDo(sequenceOfSequences)); - } - - /** - * Given an Observable that emits Observables, creates a single Observable that - * emits the items emitted by the most recently published of those Observables. - *

- * - * - * @param sequenceOfSequences - * the source Observable that emits Observables - * @return an Observable that emits only the items emitted by the most recently published - * Observable - */ - public static Observable switchOnNext(Observable> sequenceOfSequences) { - return create(OperationSwitch.switchDo(sequenceOfSequences)); - } - - - /** - * Accepts an Observable and wraps it in another Observable that ensures that the resulting - * Observable is chronologically well-behaved. - *

- * - *

- * A well-behaved Observable does not interleave its invocations of the {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, and {@link Observer#onError onError} methods of - * its {@link Observer}s; it invokes {@code onCompleted} or {@code onError} only once; and it never invokes {@code onNext} after invoking either {@code onCompleted} or {@code onError}. - * {@code synchronize} enforces this, and the Observable it returns invokes {@code onNext} and {@code onCompleted} or {@code onError} synchronously. - * - * @param observable - * the source Observable - * @param - * the type of item emitted by the source Observable - * @return an Observable that is a chronologically well-behaved version of the source - * Observable, and that synchronously notifies its {@link Observer}s - */ - public static Observable synchronize(Observable observable) { - return create(OperationSynchronize.synchronize(observable)); - } - - - /** - * Emits an item each time interval (containing a sequential number). - * @param interval - * Interval size in time units (see below). - * @param unit - * Time units to use for the interval size. - * @return An Observable that emits an item each time interval. - * @see MSDN: Observable.Interval - */ - public static Observable interval(long interval, TimeUnit unit) { - return create(OperationInterval.interval(interval, unit)); - } - - /** - * Emits an item each time interval (containing a sequential number). - * @param interval - * Interval size in time units (see below). - * @param unit - * Time units to use for the interval size. - * @param scheduler - * The scheduler to use for scheduling the items. - * @return An Observable that emits an item each time interval. - * @see MSDN: Observable.Interval - */ - public static Observable interval(long interval, TimeUnit unit, Scheduler scheduler) { - return create(OperationInterval.interval(interval, unit, scheduler)); - } - - /** - * Wraps each item emitted by a source Observable in a {@link Timestamped} object. - *

- * - * - * @return an Observable that emits timestamped items from the source Observable - */ - public Observable> timestamp() { - return create(OperationTimestamp.timestamp(this)); - } - - /** - * Converts a {@link Future} into an Observable. - *

- * - *

- * You can convert any object that supports the {@link Future} interface into an Observable that - * emits the return value of the {@link Future#get} method of that object, by passing the - * object into the {@code from} method. - *

- * Important note: This Observable is blocking; you cannot unsubscribe from it. - * - * @param future - * the source {@link Future} - * @param - * the type of object that the {@link Future} returns, and also the type of item to - * be emitted by the resulting Observable - * @return an Observable that emits the item from the source Future - */ - public static Observable from(Future future) { - return create(OperationToObservableFuture.toObservableFuture(future)); - } - - /** - * Converts a {@link Future} into an Observable. - *

- * - *

- * You can convert any object that supports the {@link Future} interface into an Observable that - * emits the return value of the {@link Future#get} method of that object, by passing the - * object into the {@code from} method. - *

- * - * @param future - * the source {@link Future} - * @param scheduler - * the {@link Scheduler} to wait for the Future on. Use a Scheduler such as {@link Schedulers#threadPoolForIO()} that can block and wait on the future. - * @param - * the type of object that the {@link Future} returns, and also the type of item to - * be emitted by the resulting Observable - * @return an Observable that emits the item from the source Future - */ - public static Observable from(Future future, Scheduler scheduler) { - return create(OperationToObservableFuture.toObservableFuture(future)).subscribeOn(scheduler); - } - - /** - * Converts a {@link Future} into an Observable with timeout. - *

- * - *

- * You can convert any object that supports the {@link Future} interface into an Observable that - * emits the return value of the {link Future#get} method of that object, by passing the - * object into the {@code from} method. - *

- * Important note: This Observable is blocking; you cannot unsubscribe from it. - * - * @param future - * the source {@link Future} - * @param timeout - * the maximum time to wait before calling get() - * @param unit - * the {@link TimeUnit} of the time argument - * @param - * the type of object that the {@link Future} returns, and also the type of item to - * be emitted by the resulting Observable - * @return an Observable that emits the item from the source {@link Future} - */ - public static Observable from(Future future, long timeout, TimeUnit unit) { - return create(OperationToObservableFuture.toObservableFuture(future, timeout, unit)); - } - - /** - * Returns an Observable that emits Boolean values that indicate whether the pairs of items - * emitted by two source Observables are equal. - *

- * - * - * @param first - * one Observable to compare - * @param second - * the second Observable to compare - * @param - * the type of items emitted by each Observable - * @return an Observable that emits Booleans that indicate whether the corresponding items - * emitted by the source Observables are equal - */ - public static Observable sequenceEqual(Observable first, Observable second) { - return sequenceEqual(first, second, new Func2() { - @Override - public Boolean call(T first, T second) { - return first.equals(second); - } - }); - } - - /** - * Returns an Observable that emits Boolean values that indicate whether the pairs of items - * emitted by two source Observables are equal based on the results of a specified equality - * function. - *

- * - * - * @param first - * one Observable to compare - * @param second - * the second Observable to compare - * @param equality - * a function used to compare items emitted by both Observables - * @param - * the type of items emitted by each Observable - * @return an Observable that emits Booleans that indicate whether the corresponding items - * emitted by the source Observables are equal - */ - public static Observable sequenceEqual(Observable first, Observable second, Func2 equality) { - return zip(first, second, equality); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of two items emitted, in sequence, by two other Observables. - *

- * - *

{@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0} and the first item emitted by {@code w1}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by {@code w0} and the second item emitted by {@code w1}; and so forth. - *

- * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * another source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable o1, Observable o2, Func2 zipFunction) { - return create(OperationZip.zip(o1, o2, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of three items emitted, in sequence, by three other Observables. - *

- * - *

{@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, and the first item emitted by {@code w2}; the second - * item emitted by the new Observable will be the result of the - * function applied to the second item emitted by {@code w0}, the second item emitted by {@code w1}, and the second item emitted by {@code w2}; and so forth. - *

- * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Func3 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of four items emitted, in sequence, by four other Observables. - *

- * - *

{@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. - *

- * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Func4 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of five items emitted, in sequence, by five other Observables. - *

- * - *

{@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. - *

- * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param o5 - * a fifth source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, o5, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of six items emitted, in sequence, by six other Observables. - *

- * - *

{@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. - *

- * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param o5 - * a fifth source Observable - * @param o6 - * a sixth source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, - Func6 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of seven items emitted, in sequence, by seven other Observables. - *

- * - *

{@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. - *

- * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param o5 - * a fifth source Observable - * @param o6 - * a sixth source Observable - * @param o7 - * a seventh source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, - Func7 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, o7, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of eight items emitted, in sequence, by eight other Observables. - *

- * - *

{@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. - *

- * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param o5 - * a fifth source Observable - * @param o6 - * a sixth source Observable - * @param o7 - * a seventh source Observable - * @param o8 - * an eighth source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, - Func8 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, o7, o8, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of nine items emitted, in sequence, by nine other Observables. - *

- * - *

{@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. - *

- * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param o5 - * a fifth source Observable - * @param o6 - * a sixth source Observable - * @param o7 - * a seventh source Observable - * @param o8 - * an eighth source Observable - * @param o9 - * a ninth source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, - Observable o9, Func9 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, o7, o8, o9, zipFunction)); - } - - /** - * Combines the given observables, emitting an event containing an aggregation of the latest values of each of the source observables - * each time an event is received from one of the source observables, where the aggregation is defined by the given function. - *

- * - * - * @param o1 - * The first source observable. - * @param o2 - * The second source observable. - * @param combineFunction - * The aggregation function used to combine the source observable values. - * @return An Observable that combines the source Observables with the given combine function - */ - public static Observable combineLatest(Observable o1, Observable o2, Func2 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, combineFunction)); - } - - /** - * @see #combineLatest(Observable, Observable, Func2) - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Func3 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, combineFunction)); - } - - /** - * @see #combineLatest(Observable, Observable, Func2) - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, - Func4 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, combineFunction)); - } - - /** - * @see #combineLatest(Observable, Observable, Func2) - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, - Func5 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, combineFunction)); - } - - /** - * @see #combineLatest(Observable, Observable, Func2) - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, - Func6 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, combineFunction)); - } - - /** - * @see #combineLatest(Observable, Observable, Func2) - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, - Func7 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, o7, combineFunction)); - } - - /** - * @see #combineLatest(Observable, Observable, Func2) - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, - Func8 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, o7, o8, combineFunction)); - } - - /** - * @see #combineLatest(Observable, Observable, Func2) - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9, - Func9 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, o7, o8, o9, combineFunction)); - } - - /** - * Creates an Observable which produces buffers of collected values. - * - *

This Observable produces connected non-overlapping buffers. The current buffer is - * emitted and replaced with a new buffer when the Observable produced by the specified {@link Func0} produces a {@link rx.util.Closing} object. The * {@link Func0} will then - * be used to create a new Observable to listen for the end of the next buffer. - * - * @param bufferClosingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every buffer created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated buffer - * is emitted and replaced with a new one. - * @return - * An {@link Observable} which produces connected non-overlapping buffers, which are emitted - * when the current {@link Observable} created with the {@link Func0} argument produces a {@link rx.util.Closing} object. - */ - public Observable> buffer(Func0> bufferClosingSelector) { - return create(OperationBuffer.buffer(this, bufferClosingSelector)); - } - - /** - * Creates an Observable which produces buffers of collected values. - * - *

This Observable produces buffers. Buffers are created when the specified "bufferOpenings" - * Observable produces a {@link rx.util.Opening} object. Additionally the {@link Func0} argument - * is used to create an Observable which produces {@link rx.util.Closing} objects. When this - * Observable produces such an object, the associated buffer is emitted. - * - * @param bufferOpenings - * The {@link Observable} which, when it produces a {@link rx.util.Opening} object, will cause - * another buffer to be created. - * @param bufferClosingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every buffer created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated buffer - * is emitted. - * @return - * An {@link Observable} which produces buffers which are created and emitted when the specified {@link Observable}s publish certain objects. - */ - public Observable> buffer(Observable bufferOpenings, Func1> bufferClosingSelector) { - return create(OperationBuffer.buffer(this, bufferOpenings, bufferClosingSelector)); - } - - /** - * Creates an Observable which produces buffers of collected values. - * - *

This Observable produces connected non-overlapping buffers, each containing "count" - * elements. When the source Observable completes or encounters an error, the current - * buffer is emitted, and the event is propagated. - * - * @param count - * The maximum size of each buffer before it should be emitted. - * @return - * An {@link Observable} which produces connected non-overlapping buffers containing at most - * "count" produced values. - */ - public Observable> buffer(int count) { - return create(OperationBuffer.buffer(this, count)); - } - - /** - * Creates an Observable which produces buffers of collected values. - * - *

This Observable produces buffers every "skip" values, each containing "count" - * elements. When the source Observable completes or encounters an error, the current - * buffer is emitted, and the event is propagated. - * - * @param count - * The maximum size of each buffer before it should be emitted. - * @param skip - * How many produced values need to be skipped before starting a new buffer. Note that when "skip" and - * "count" are equals that this is the same operation as {@link Observable#buffer(int)}. - * @return - * An {@link Observable} which produces buffers every "skipped" values containing at most - * "count" produced values. - */ - public Observable> buffer(int count, int skip) { - return create(OperationBuffer.buffer(this, count, skip)); - } - - /** - * Creates an Observable which produces buffers of collected values. - * - *

This Observable produces connected non-overlapping buffers, each of a fixed duration - * specified by the "timespan" argument. When the source Observable completes or encounters - * an error, the current buffer is emitted and the event is propagated. - * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @return - * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. - */ - public Observable> buffer(long timespan, TimeUnit unit) { - return create(OperationBuffer.buffer(this, timespan, unit)); - } - - /** - * Creates an Observable which produces buffers of collected values. - * - *

This Observable produces connected non-overlapping buffers, each of a fixed duration - * specified by the "timespan" argument. When the source Observable completes or encounters - * an error, the current buffer is emitted and the event is propagated. - * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. - * @return - * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. - */ - public Observable> buffer(long timespan, TimeUnit unit, Scheduler scheduler) { - return create(OperationBuffer.buffer(this, timespan, unit, scheduler)); - } - - /** - * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes - * or encounters an error, the current buffer is emitted and the event is propagated. - * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param count - * The maximum size of each buffer before it should be emitted. - * @return - * An {@link Observable} which produces connected non-overlapping buffers which are emitted after - * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). - */ - public Observable> buffer(long timespan, TimeUnit unit, int count) { - return create(OperationBuffer.buffer(this, timespan, unit, count)); - } - - /** - * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes - * or encounters an error, the current buffer is emitted and the event is propagated. - * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param count - * The maximum size of each buffer before it should be emitted. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. - * @return - * An {@link Observable} which produces connected non-overlapping buffers which are emitted after - * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). - */ - public Observable> buffer(long timespan, TimeUnit unit, int count, Scheduler scheduler) { - return create(OperationBuffer.buffer(this, timespan, unit, count, scheduler)); - } - - /** - * Creates an Observable which produces buffers of collected values. This Observable starts a new buffer - * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the - * current buffer is emitted and the event is propagated. - * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new buffer will be created. - * @param unit - * The unit of time which applies to the "timespan" and "timeshift" argument. - * @return - * An {@link Observable} which produces new buffers periodically, and these are emitted after - * a fixed timespan has elapsed. - */ - public Observable> buffer(long timespan, long timeshift, TimeUnit unit) { - return create(OperationBuffer.buffer(this, timespan, timeshift, unit)); - } - - /** - * Creates an Observable which produces buffers of collected values. This Observable starts a new buffer - * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the - * current buffer is emitted and the event is propagated. - * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new buffer will be created. - * @param unit - * The unit of time which applies to the "timespan" and "timeshift" argument. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. - * @return - * An {@link Observable} which produces new buffers periodically, and these are emitted after - * a fixed timespan has elapsed. - */ - public Observable> buffer(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { - return create(OperationBuffer.buffer(this, timespan, timeshift, unit, scheduler)); - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows. The current window is emitted and replaced with a new window when the - * Observable produced by the specified {@link Func0} produces a {@link rx.util.Closing} object. The {@link Func0} will then be used to create a new Observable to listen for the end of the next - * window. - * - * @param closingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every window created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated window - * is emitted and replaced with a new one. - * @return - * An {@link Observable} which produces connected non-overlapping windows, which are emitted - * when the current {@link Observable} created with the {@link Func0} argument produces a {@link rx.util.Closing} object. - */ - public Observable> window(Func0> closingSelector) { - return create(OperationWindow.window(this, closingSelector)); - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces windows. - * Chunks are created when the specified "windowOpenings" Observable produces a {@link rx.util.Opening} object. - * Additionally the {@link Func0} argument is used to create an Observable which produces {@link rx.util.Closing} objects. When this Observable produces such an object, the associated window is - * emitted. - * - * @param windowOpenings - * The {@link Observable} which when it produces a {@link rx.util.Opening} object, will cause - * another window to be created. - * @param closingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every window created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated window - * is emitted. - * @return - * An {@link Observable} which produces windows which are created and emitted when the specified {@link Observable}s publish certain objects. - */ - public Observable> window(Observable windowOpenings, Func1> closingSelector) { - return create(OperationWindow.window(this, windowOpenings, closingSelector)); - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each containing "count" elements. When the source Observable completes or - * encounters an error, the current window is emitted, and the event is propagated. - * - * @param count - * The maximum size of each window before it should be emitted. - * @return - * An {@link Observable} which produces connected non-overlapping windows containing at most - * "count" produced values. - */ - public Observable> window(int count) { - return create(OperationWindow.window(this, count)); - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces windows every - * "skip" values, each containing "count" elements. When the source Observable completes or encounters an error, - * the current window is emitted and the event is propagated. - * - * @param count - * The maximum size of each window before it should be emitted. - * @param skip - * How many produced values need to be skipped before starting a new window. Note that when "skip" and - * "count" are equals that this is the same operation as {@link Observable#window(Observable, int)}. - * @return - * An {@link Observable} which produces windows every "skipped" values containing at most - * "count" produced values. - */ - public Observable> window(int count, int skip) { - return create(OperationWindow.window(this, count, skip)); - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument. When the source - * Observable completes or encounters an error, the current window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @return - * An {@link Observable} which produces connected non-overlapping windows with a fixed duration. - */ - public Observable> window(long timespan, TimeUnit unit) { - return create(OperationWindow.window(this, timespan, unit)); - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument. When the source - * Observable completes or encounters an error, the current window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a window. - * @return - * An {@link Observable} which produces connected non-overlapping windows with a fixed duration. - */ - public Observable> window(long timespan, TimeUnit unit, Scheduler scheduler) { - return create(OperationWindow.window(this, timespan, unit, scheduler)); - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes - * or encounters an error, the current window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param count - * The maximum size of each window before it should be emitted. - * @return - * An {@link Observable} which produces connected non-overlapping windows which are emitted after - * a fixed duration or when the window has reached maximum capacity (which ever occurs first). - */ - public Observable> window(long timespan, TimeUnit unit, int count) { - return create(OperationWindow.window(this, timespan, unit, count)); - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes - * or encounters an error, the current window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param count - * The maximum size of each window before it should be emitted. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a window. - * @return - * An {@link Observable} which produces connected non-overlapping windows which are emitted after - * a fixed duration or when the window has reached maximum capacity (which ever occurs first). - */ - public Observable> window(long timespan, TimeUnit unit, int count, Scheduler scheduler) { - return create(OperationWindow.window(this, timespan, unit, count, scheduler)); - } - - /** - * Creates an Observable which produces windows of collected values. This Observable starts a new window - * periodically, which is determined by the "timeshift" argument. Each window is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the - * current window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new window will be created. - * @param unit - * The unit of time which applies to the "timespan" and "timeshift" argument. - * @return - * An {@link Observable} which produces new windows periodically, and these are emitted after - * a fixed timespan has elapsed. - */ - public Observable> window(long timespan, long timeshift, TimeUnit unit) { - return create(OperationWindow.window(this, timespan, timeshift, unit)); - } - - /** - * Creates an Observable which produces windows of collected values. This Observable starts a new window - * periodically, which is determined by the "timeshift" argument. Each window is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the - * current window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new window will be created. - * @param unit - * The unit of time which applies to the "timespan" and "timeshift" argument. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a window. - * @return - * An {@link Observable} which produces new windows periodically, and these are emitted after - * a fixed timespan has elapsed. - */ - public Observable> window(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { - return create(OperationWindow.window(this, timespan, timeshift, unit, scheduler)); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of N items emitted, in sequence, by N other Observables as provided by an Iterable. - * - *

{@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by - * all of the Observalbes; the second item emitted by the new Observable will be the result of - * the function applied to the second item emitted by each of those Observables; and so forth. - *

- * The resulting {@code Observable} returned from {@code zip} will invoke {@code onNext} as many times as the number of {@code onNext} invokations of the - * source Observable that emits the fewest items. - *

- * - * - * @param ws - * An Observable of source Observables - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable> ws, final FuncN zipFunction) { - return ws.toList().mapMany(new Func1>, Observable>() { - @Override - public Observable call(List> wsList) { - return create(OperationZip.zip(wsList, zipFunction)); - } - }); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of four items emitted, in sequence, by four other Observables. - *

{@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by - * all of the Observalbes; the second item emitted by the new Observable will be the result of - * the function applied to the second item emitted by each of those Observables; and so forth. - *

- * The resulting {@code Observable} returned from {@code zip} will invoke {@code onNext} as many times as the number of {@code onNext} invokations of the - * source Observable that emits the fewest items. - *

- * - * - * @param ws - * A collection of source Observables - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Iterable> ws, FuncN zipFunction) { - return create(OperationZip.zip(ws, zipFunction)); - } - - /** - *

- * - * - * @param predicate - * a function that evaluates the items emitted by the source Observable, returning {@code true} if they pass the filter - * @return an Observable that emits only those items in the original Observable that the filter - * evaluates as {@code true} - */ - public Observable filter(Func1 predicate) { - return create(OperationFilter.filter(this, predicate)); - } - - /** - * Registers an {@link Action0} to be called when this Observable invokes {@link Observer#onCompleted onCompleted} or {@link Observer#onError onError}. - *

- * - * - * @param action - * an {@link Action0} to be invoked when the source Observable finishes - * @return an Observable that emits the same items as the source Observable, then invokes the {@link Action0} - * @see MSDN: Observable.Finally Method - */ - public Observable finallyDo(Action0 action) { - return create(OperationFinally.finallyDo(this, action)); - } - - /** - * Creates a new Observable by applying a function that you supply to each item emitted by - * the source Observable, where that function returns an Observable, and then merging those - * resulting Observables and emitting the results of this merger. - *

- * - *

- * Note: {@code mapMany} and {@code flatMap} are equivalent. - * - * @param func - * a function that, when applied to an item emitted by the source Observable, returns - * an Observable - * @return an Observable that emits the result of applying the transformation function to each - * item emitted by the source Observable and merging the results of the Observables - * obtained from this transformation. - * @see #mapMany(Func1) - */ - public Observable flatMap(Func1> func) { - return mapMany(func); - } - - /** - *

- * - * - * @param predicate - * a function that evaluates an item emitted by the source Observable, returning {@code true} if it passes the filter - * @return an Observable that emits only those items in the original Observable that the filter - * evaluates as {@code true} - * @see #filter(Func1) - */ - public Observable where(Func1 predicate) { - return filter(predicate); - } - - /** - * Returns an Observable that applies the given function to each item emitted by an - * Observable and emits the result. - *

- * - * - * @param func - * a function to apply to each item emitted by the Observable - * @return an Observable that emits the items from the source Observable, transformed by the - * given function - */ - public Observable map(Func1 func) { - return create(OperationMap.map(this, func)); - } - - /** - * Creates a new Observable by applying a function that you supply to each item emitted by - * the source Observable, where that function returns an Observable, and then merging those - * resulting Observables and emitting the results of this merger. - *

- * - *

- * Note: mapMany and flatMap are equivalent. - * - * @param func - * a function that, when applied to an item emitted by the source Observable, returns - * an Observable - * @return an Observable that emits the result of applying the transformation function to each - * item emitted by the source Observable and merging the results of the Observables - * obtained from this transformation. - * @see #flatMap(Func1) - */ - public Observable mapMany(Func1> func) { - return create(OperationMap.mapMany(this, func)); - } - - /** - * Turns all of the notifications from a source Observable into {@link Observer#onNext onNext} emissions, and marks them with their original notification types within {@link Notification} objects. - *

- * - * - * @return an Observable whose items are the result of materializing the items and - * notifications of the source Observable - * @see MSDN: Observable.materialize - */ - public Observable> materialize() { - return create(OperationMaterialize.materialize(this)); - } - - /** - * Asynchronously subscribes and unsubscribes Observers on the specified {@link Scheduler}. - *

- * - * - * @param scheduler - * the {@link Scheduler} to perform subscription and unsubscription actions on - * @return the source Observable modified so that its subscriptions and unsubscriptions happen - * on the specified {@link Scheduler} - */ - public Observable subscribeOn(Scheduler scheduler) { - return create(OperationSubscribeOn.subscribeOn(this, scheduler)); - } - - /** - * Asynchronously notify {@link Observer}s on the specified {@link Scheduler}. - *

- * - * - * @param scheduler - * the {@link Scheduler} to notify {@link Observer}s on - * @return the source Observable modified so that its {@link Observer}s are notified on the - * specified {@link Scheduler} - */ - public Observable observeOn(Scheduler scheduler) { - return create(OperationObserveOn.observeOn(this, scheduler)); - } - - /** - * Returns an Observable that reverses the effect of {@link #materialize materialize} by - * transforming the {@link Notification} objects emitted by the source Observable into the items - * or notifications they represent. - *

- * - * - * @return an Observable that emits the items and notifications embedded in the {@link Notification} objects emitted by the source Observable - * @see MSDN: Observable.dematerialize - * @throws Throwable - * if the source Observable is not of type {@code Observable>}. - */ - @SuppressWarnings("unchecked") - public Observable dematerialize() { - return create(OperationDematerialize.dematerialize((Observable>) this)); - } - - /** - * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error. - *

- * - *

- * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorResumeNext method changes this behavior. If you pass a - * function that returns an Observable (resumeFunction) to - * onErrorResumeNext, if the original Observable encounters an error, instead of - * invoking its Observer's onError method, it will instead relinquish control to - * the Observable returned from resumeFunction, which will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no - * Observable necessarily invokes onError, the Observer may never know that an - * error happened. - *

- * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param resumeFunction - * a function that returns an Observable that will take over if the source Observable - * encounters an error - * @return the original Observable, with appropriately modified behavior - */ - public Observable onErrorResumeNext(final Func1> resumeFunction) { - return create(OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction(this, resumeFunction)); - } - - /** - * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error. - *

- * - *

- * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorResumeNext method changes this behavior. If you pass - * another Observable (resumeSequence) to an Observable's - * onErrorResumeNext method, if the original Observable encounters an error, - * instead of invoking its Observer's onError method, it will instead relinquish - * control to resumeSequence which will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no - * Observable necessarily invokes onError, the Observer may never know that an - * error happened. - *

- * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param resumeSequence - * a function that returns an Observable that will take over if the source Observable - * encounters an error - * @return the original Observable, with appropriately modified behavior - */ - public Observable onErrorResumeNext(final Observable resumeSequence) { - return create(OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable(this, resumeSequence)); - } - - /** - * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error of type {@link java.lang.Exception}. - *

- * This differs from {@link #onErrorResumeNext} in that this one does not handle {@link java.lang.Throwable} or {@link java.lang.Error} but lets those continue through. - *

- * - *

- * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorResumeNext method changes this behavior. If you pass - * another Observable (resumeSequence) to an Observable's - * onErrorResumeNext method, if the original Observable encounters an error, - * instead of invoking its Observer's onError method, it will instead relinquish - * control to resumeSequence which will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no - * Observable necessarily invokes onError, the Observer may never know that an - * error happened. - *

- * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param resumeSequence - * a function that returns an Observable that will take over if the source Observable - * encounters an error - * @return the original Observable, with appropriately modified behavior - */ - public Observable onExceptionResumeNext(final Observable resumeSequence) { - return create(OperationOnExceptionResumeNextViaObservable.onExceptionResumeNextViaObservable(this, resumeSequence)); - } - - /** - * Instruct an Observable to emit an item (returned by a specified function) rather than - * invoking {@link Observer#onError onError} if it encounters an error. - *

- * - *

- * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorReturn method changes this behavior. If you pass a function - * (resumeFunction) to an Observable's onErrorReturn method, if the - * original Observable encounters an error, instead of invoking its Observer's - * onError method, it will instead pass the return value of - * resumeFunction to the Observer's {@link Observer#onNext onNext} method. - *

- * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param resumeFunction - * a function that returns an item that the new Observable will emit if the source - * Observable encounters an error - * @return the original Observable with appropriately modified behavior - */ - public Observable onErrorReturn(Func1 resumeFunction) { - return create(OperationOnErrorReturn.onErrorReturn(this, resumeFunction)); - } - - /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by the source Observable into the same function, and so on until all items have been emitted - * by the source Observable, and emits the final result from the final call to your function as - * its sole item. - *

- * - *

- * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," - * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, - * has an inject method that does a similar operation on lists. - * - * @param accumulator - * An accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be used in the next accumulator call - * @return an Observable that emits a single item that is the result of accumulating the - * output from the source Observable - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) - */ - public Observable reduce(Func2 accumulator) { - return create(OperationScan.scan(this, accumulator)).takeLast(1); - } - - /** - * Returns an Observable that counts the total number of elements in the source Observable. - * @return an Observable emitting the number of counted elements of the source Observable - * as its single item. - * @see MSDN: Observable.Count - */ - public Observable count() { - return reduce(0, new Func2() { - @Override - public Integer call(Integer t1, T t2) { - return t1 + 1; - } - }); - } - - /** - * Returns an Observable that sums up the elements in the source Observable. - * @param source - * Source observable to compute the sum of. - * @return an Observable emitting the sum of all the elements of the source Observable - * as its single item. - * @see MSDN: Observable.Sum - */ - public static Observable sum(Observable source) { - return OperationSum.sum(source); - } - - /** - * @see #sum(Observable) - * @see MSDN: Observable.Sum - */ - public static Observable sumLongs(Observable source) { - return OperationSum.sumLongs(source); - } - - /** - * @see #sum(Observable) - * @see MSDN: Observable.Sum - */ - public static Observable sumFloats(Observable source) { - return OperationSum.sumFloats(source); - } - - /** - * @see #sum(Observable) - * @see MSDN: Observable.Sum - */ - public static Observable sumDoubles(Observable source) { - return OperationSum.sumDoubles(source); - } - - /** - * Returns an Observable that computes the average of all elements in the source Observable. - * For an empty source, it causes an ArithmeticException. - * @param source - * Source observable to compute the average of. - * @return an Observable emitting the averageof all the elements of the source Observable - * as its single item. - * @see MSDN: Observable.Average - */ - public static Observable average(Observable source) { - return OperationAverage.average(source); - } - - /** - * @see #average(Observable) - * @see MSDN: Observable.Average - */ - public static Observable averageLongs(Observable source) { - return OperationAverage.averageLongs(source); - } - - /** - * @see #average(Observable) - * @see MSDN: Observable.Average - */ - public static Observable averageFloats(Observable source) { - return OperationAverage.averageFloats(source); - } - - /** - * @see #average(Observable) - * @see MSDN: Observable.Average - */ - public static Observable averageDoubles(Observable source) { - return OperationAverage.averageDoubles(source); - } - - /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the underlying - * Observable that will replay all of its items and notifications to any future {@link Observer}. - *

- * - * - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * emit items to its {@link Observer}s - */ - public ConnectableObservable replay() { - return OperationMulticast.multicast(this, ReplaySubject. create()); - } - - /** - * Retry subscription to origin Observable upto given retry count. - *

- * If {@link Observer#onError} is invoked the source Observable will be re-subscribed to as many times as defined by retryCount. - *

- * Any {@link Observer#onNext} calls received on each attempt will be emitted and concatenated together. - *

- * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and - * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. - * - * @param retryCount - * Number of retry attempts before failing. - * @return Observable with retry logic. - */ - public Observable retry(int retryCount) { - return create(OperationRetry.retry(this, retryCount)); - } - - /** - * Retry subscription to origin Observable whenever onError is called (infinite retry count). - *

- * If {@link Observer#onError} is invoked the source Observable will be re-subscribed to. - *

- * Any {@link Observer#onNext} calls received on each attempt will be emitted and concatenated together. - *

- * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and - * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. - * - * @param retryCount - * Number of retry attempts before failing. - * @return Observable with retry logic. - */ - public Observable retry() { - return create(OperationRetry.retry(this)); - } - - /** - * This method has similar behavior to {@link #replay} except that this auto-subscribes to - * the source Observable rather than returning a {@link ConnectableObservable}. - *

- * - *

- * This is useful when you want an Observable to cache responses and you can't control the - * subscribe/unsubscribe behavior of all the {@link Observer}s. - *

- * NOTE: You sacrifice the ability to unsubscribe from the origin when you use the - * cache() operator so be careful not to use this operator on Observables that - * emit an infinite or very large number of items that will use up memory. - * - * @return an Observable that when first subscribed to, caches all of its notifications for - * the benefit of subsequent subscribers. - */ - public Observable cache() { - return create(OperationCache.cache(this)); - } - - /** - * Returns a {@link ConnectableObservable}, which waits until its {@link ConnectableObservable#connect connect} method is called before it begins emitting - * items to those {@link Observer}s that have subscribed to it. - *

- * - * - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * emit items to its {@link Observer}s - */ - public ConnectableObservable publish() { - return OperationMulticast.multicast(this, PublishSubject. create()); - } - - /** - * Synonymous with reduce(). - *

- * - * - * @see #reduce(Func2) - */ - public Observable aggregate(Func2 accumulator) { - return reduce(accumulator); - } - - /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by an Observable into the same function, and so on until all items have been emitted by the - * source Observable, emitting the final result from the final call to your function as its sole - * item. - *

- * - *

- * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," - * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, - * has an inject method that does a similar operation on lists. - * - * @param initialValue - * the initial (seed) accumulator value - * @param accumulator - * an accumulator function to be invoked on each item emitted by the source - * Observable, the result of which will be used in the next accumulator call - * @return an Observable that emits a single item that is the result of accumulating the output - * from the items emitted by the source Observable - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) - */ - public Observable reduce(R initialValue, Func2 accumulator) { - return create(OperationScan.scan(this, initialValue, accumulator)).takeLast(1); - } - - /** - * Synonymous with reduce(). - *

- * - * - * @see #reduce(Object, Func2) - */ - public Observable aggregate(R initialValue, Func2 accumulator) { - return reduce(initialValue, accumulator); - } - - /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by an Observable into the same function, and so on until all items have been emitted by the - * source Observable, emitting the result of each of these iterations. - *

- * - *

- * This sort of function is sometimes called an accumulator. - *

- * Note that when you pass a seed to scan() the resulting Observable will emit - * that seed as its first emitted item. - * - * @param accumulator - * an accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be emitted to {@link Observer}s via {@link Observer#onNext onNext} and used in the next accumulator call. - * @return an Observable that emits the results of each call to the accumulator function - * @see MSDN: Observable.Scan - */ - public Observable scan(Func2 accumulator) { - return create(OperationScan.scan(this, accumulator)); - } - - /** - * Returns an Observable that emits the results of sampling the items emitted by the source - * Observable at a specified time interval. - *

- * - * - * @param period - * the sampling rate - * @param unit - * the {@link TimeUnit} in which period is defined - * @return an Observable that emits the results of sampling the items emitted by the source - * Observable at the specified time interval - */ - public Observable sample(long period, TimeUnit unit) { - return create(OperationSample.sample(this, period, unit)); - } - - /** - * Returns an Observable that emits the results of sampling the items emitted by the source - * Observable at a specified time interval. - *

- * - * - * @param period - * the sampling rate - * @param unit - * the {@link TimeUnit} in which period is defined - * @param scheduler - * the {@link Scheduler} to use when sampling - * @return an Observable that emits the results of sampling the items emitted by the source - * Observable at the specified time interval - */ - public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { - return create(OperationSample.sample(this, period, unit, scheduler)); - } - - /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by an Observable into the same function, and so on until all items have been emitted by the - * source Observable, emitting the result of each of these iterations. - *

- * - *

- * This sort of function is sometimes called an accumulator. - *

- * Note that when you pass a seed to scan() the resulting Observable will emit - * that seed as its first emitted item. - * - * @param initialValue - * the initial (seed) accumulator value - * @param accumulator - * an accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be emitted to {@link Observer}s via {@link Observer#onNext onNext} and used in the next accumulator call. - * @return an Observable that emits the results of each call to the accumulator function - * @see MSDN: Observable.Scan - */ - public Observable scan(R initialValue, Func2 accumulator) { - return create(OperationScan.scan(this, initialValue, accumulator)); - } - - /** - * Returns an Observable that emits a Boolean that indicates whether all of the items emitted by - * the source Observable satisfy a condition. - *

- * - * - * @param predicate - * a function that evaluates an item and returns a Boolean - * @return an Observable that emits true if all items emitted by the source - * Observable satisfy the predicate; otherwise, false - */ - public Observable all(Func1 predicate) { - return create(OperationAll.all(this, predicate)); - } - - /** - * Returns an Observable that skips the first num items emitted by the source - * Observable and emits the remainder. - *

- * - *

- * You can ignore the first num items emitted by an Observable and attend only to - * those items that come after, by modifying the Observable with the skip method. - * - * @param num - * the number of items to skip - * @return an Observable that is identical to the source Observable except that it does not - * emit the first num items that the source emits - */ - public Observable skip(int num) { - return create(OperationSkip.skip(this, num)); - } - - /** - * Returns an Observable that emits only the very first item emitted by the source Observable. - * - * @return an Observable that emits only the very first item from the source, or none if the - * source Observable completes without emitting a single item. - * @see MSDN: Observable.First - */ - public Observable first() { - return take(1); - } - - /** - * Returns an Observable that emits only the very first item emitted by the source Observable - * that satisfies a given condition. - * - * @param predicate - * The condition any source emitted item has to satisfy. - * @return an Observable that emits only the very first item satisfying the given condition from the source, - * or none if the source Observable completes without emitting a single matching item. - * @see MSDN: Observable.First - */ - public Observable first(Func1 predicate) { - return skipWhile(not(predicate)).take(1); - } - - /** - * Returns an Observable that emits only the very first item emitted by the source Observable, or - * a default value. - * - * @param defaultValue - * The default value to emit if the source Observable doesn't emit anything. - * @return an Observable that emits only the very first item from the source, or a default value - * if the source Observable completes without emitting a single item. - * @see MSDN: Observable.FirstOrDefault - */ - public Observable firstOrDefault(T defaultValue) { - return create(OperationFirstOrDefault.firstOrDefault(this, defaultValue)); - } - - /** - * Returns an Observable that emits only the very first item emitted by the source Observable - * that satisfies a given condition, or a default value otherwise. - * - * @param predicate - * The condition any source emitted item has to satisfy. - * @param defaultValue - * The default value to emit if the source Observable doesn't emit anything that - * satisfies the given condition. - * @return an Observable that emits only the very first item from the source that satisfies the - * given condition, or a default value otherwise. - * @see MSDN: Observable.FirstOrDefault - */ - public Observable firstOrDefault(Func1 predicate, T defaultValue) { - return create(OperationFirstOrDefault.firstOrDefault(this, predicate, defaultValue)); - } - - - /** - * Returns an Observable that emits only the first num items emitted by the source - * Observable. - *

- * - *

- * This method returns an Observable that will invoke a subscribing {@link Observer}'s {@link Observer#onNext onNext} function a maximum of num times before invoking - * {@link Observer#onCompleted onCompleted}. - * - * @param num - * the number of items to take - * @return an Observable that emits only the first num items from the source - * Observable, or all of the items from the source Observable if that Observable emits - * fewer than num items - */ - public Observable take(final int num) { - return create(OperationTake.take(this, num)); - } - - /** - * Returns an Observable that emits items emitted by the source Observable so long as a - * specified condition is true. - *

- * - * - * @param predicate - * a function that evaluates an item emitted by the source Observable and returns a - * Boolean - * @return an Observable that emits the items from the source Observable so long as each item - * satisfies the condition defined by predicate - */ - public Observable takeWhile(final Func1 predicate) { - return create(OperationTakeWhile.takeWhile(this, predicate)); - } - - /** - * Returns an Observable that emits the items emitted by a source Observable so long as a given - * predicate remains true, where the predicate can operate on both the item and its index - * relative to the complete sequence. - *

- * - * - * @param predicate - * a function to test each item emitted by the source Observable for a condition; - * the second parameter of the function represents the index of the source item - * @return an Observable that emits items from the source Observable so long as the predicate - * continues to return true for each item, then completes - */ - public Observable takeWhileWithIndex(final Func2 predicate) { - return create(OperationTakeWhile.takeWhileWithIndex(this, predicate)); - } - - /** - * Returns an Observable that emits only the very first item emitted by the source Observable. - * - * @return an Observable that emits only the very first item from the source, or none if the - * source Observable completes without emitting a single item. - * @see MSDN: Observable.First - * @see {@link #first()} - */ - public Observable takeFirst() { - return first(); - } - - /** - * Returns an Observable that emits only the very first item emitted by the source Observable - * that satisfies a given condition. - * - * @param predicate - * The condition any source emitted item has to satisfy. - * @return an Observable that emits only the very first item satisfying the given condition from the source, - * or none if the source Observable completes without emitting a single matching item. - * @see MSDN: Observable.First - * @see {@link #first(Func1)} - */ - public Observable takeFirst(Func1 predicate) { - return first(predicate); - } - - /** - * Returns an Observable that emits only the last count items emitted by the source - * Observable. - *

- * - * - * @param count - * the number of items to emit from the end of the sequence emitted by the source - * Observable - * @return an Observable that emits only the last count items emitted by the source - * Observable - */ - public Observable takeLast(final int count) { - return create(OperationTakeLast.takeLast(this, count)); - } - - /** - * Returns an Observable that emits the items from the source Observable only until the - * other Observable emits an item. - *

- * - * - * @param other - * the Observable whose first emitted item will cause takeUntil to stop - * emitting items from the source Observable - * @param - * the type of items emitted by other - * @return an Observable that emits the items of the source Observable until such time as - * other emits its first item - */ - public Observable takeUntil(Observable other) { - return OperationTakeUntil.takeUntil(this, other); - } - - /** - * Returns an Observable that bypasses all items from the source Observable as long as the specified - * condition holds true. Emits all further source items as soon as the condition becomes false. - * @param predicate - * A function to test each item emitted from the source Observable for a condition. - * It receives the emitted item as first parameter and the index of the emitted item as - * second parameter. - * @return an Observable that emits all items from the source Observable as soon as the condition - * becomes false. - * @see MSDN: Observable.SkipWhile - */ - public Observable skipWhileWithIndex(Func2 predicate) { - return create(OperationSkipWhile.skipWhileWithIndex(this, predicate)); - } - - /** - * Returns an Observable that bypasses all items from the source Observable as long as the specified - * condition holds true. Emits all further source items as soon as the condition becomes false. - * @param predicate - * A function to test each item emitted from the source Observable for a condition. - * @return an Observable that emits all items from the source Observable as soon as the condition - * becomes false. - * @see MSDN: Observable.SkipWhile - */ - public Observable skipWhile(Func1 predicate) { - return create(OperationSkipWhile.skipWhile(this, predicate)); - } - - /** - * Returns an Observable that emits a single item, a list composed of all the items emitted by - * the source Observable. - *

- * - *

- * Normally, an Observable that returns multiple items will do so by invoking its {@link Observer}'s {@link Observer#onNext onNext} method for each such item. You can change - * this behavior, instructing the Observable to compose a list of all of these items and then to - * invoke the Observer's onNext function once, passing it the entire list, by - * calling the Observable's toList method prior to calling its {@link #subscribe} method. - *

- * Be careful not to use this operator on Observables that emit infinite or very large numbers - * of items, as you do not have the option to unsubscribe. - * - * @return an Observable that emits a single item: a List containing all of the items emitted by - * the source Observable. - */ - public Observable> toList() { - return create(OperationToObservableList.toObservableList(this)); - } - - /** - * Return an Observable that emits the items emitted by the source Observable, in a sorted - * order (each item emitted by the Observable must implement {@link Comparable} with respect to - * all other items in the sequence). - *

- * - * - * @throws ClassCastException - * if any item emitted by the Observable does not implement {@link Comparable} with - * respect to all other items emitted by the Observable - * @return an Observable that emits the items from the source Observable in sorted order - */ - public Observable> toSortedList() { - return create(OperationToObservableSortedList.toSortedList(this)); - } - - /** - * Return an Observable that emits the items emitted by the source Observable, in a sorted - * order based on a specified comparison function - *

- * - * - * @param sortFunction - * a function that compares two items emitted by the source Observable and returns - * an Integer that indicates their sort order - * @return an Observable that emits the items from the source Observable in sorted order - */ - public Observable> toSortedList(Func2 sortFunction) { - return create(OperationToObservableSortedList.toSortedList(this, sortFunction)); - } - - /** - * Emit a specified set of items before beginning to emit items from the source Observable. - *

- * - * - * @param values - * Iterable of the items you want the modified Observable to emit first - * @return an Observable that exhibits the modified behavior - */ - public Observable startWith(Iterable values) { - return concat(Observable. from(values), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the source Observable. - *

- * - * - * @param t1 - * item to include - * @param values - * Iterable of the items you want the modified Observable to emit first - * @return an Observable that exhibits the modified behavior - */ - public Observable startWith(T t1) { - return concat(Observable. from(t1), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the source Observable. - *

- * - * - * @param t1 - * item to include - * @param t2 - * item to include - * @param values - * Iterable of the items you want the modified Observable to emit first - * @return an Observable that exhibits the modified behavior - */ - public Observable startWith(T t1, T t2) { - return concat(Observable. from(t1, t2), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the source Observable. - *

- * - * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param values - * Iterable of the items you want the modified Observable to emit first - * @return an Observable that exhibits the modified behavior - */ - public Observable startWith(T t1, T t2, T t3) { - return concat(Observable. from(t1, t2, t3), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the source Observable. - *

- * - * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include - * @param values - * Iterable of the items you want the modified Observable to emit first - * @return an Observable that exhibits the modified behavior - */ - public Observable startWith(T t1, T t2, T t3, T t4) { - return concat(Observable. from(t1, t2, t3, t4), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the source Observable. - *

- * - * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include - * @param t5 - * item to include - * @param values - * Iterable of the items you want the modified Observable to emit first - * @return an Observable that exhibits the modified behavior - */ - public Observable startWith(T t1, T t2, T t3, T t4, T t5) { - return concat(Observable. from(t1, t2, t3, t4, t5), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the source Observable. - *

- * - * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include - * @param t5 - * item to include - * @param t6 - * item to include - * @param values - * Iterable of the items you want the modified Observable to emit first - * @return an Observable that exhibits the modified behavior - */ - public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { - return concat(Observable. from(t1, t2, t3, t4, t5, t6), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the source Observable. - *

- * - * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include - * @param t5 - * item to include - * @param t6 - * item to include - * @param t7 - * item to include - * @param values - * Iterable of the items you want the modified Observable to emit first - * @return an Observable that exhibits the modified behavior - */ - public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { - return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the source Observable. - *

- * - * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include - * @param t5 - * item to include - * @param t6 - * item to include - * @param t7 - * item to include - * @param t8 - * item to include - * @param values - * Iterable of the items you want the modified Observable to emit first - * @return an Observable that exhibits the modified behavior - */ - public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { - return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the source Observable. - *

- * - * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include - * @param t5 - * item to include - * @param t6 - * item to include - * @param t7 - * item to include - * @param t8 - * item to include - * @param t9 - * item to include - * @param values - * Iterable of the items you want the modified Observable to emit first - * @return an Observable that exhibits the modified behavior - */ - public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { - return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8, t9), this); - } - - /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. - *

- * - * - * @param keySelector - * a function that extracts the key from an item - * @param elementSelector - * a function to map a source item to an item in a {@link GroupedObservable} - * @param - * the key type - * @param - * the type of items emitted by the resulting {@link GroupedObservable}s - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value - */ - public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) { - return create(OperationGroupBy.groupBy(this, keySelector, elementSelector)); - } - - /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. - *

- * - * - * @param keySelector - * a function that extracts the key for each item - * @param - * the key type - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value - */ - public Observable> groupBy(final Func1 keySelector) { - return create(OperationGroupBy.groupBy(this, keySelector)); - } - - /** - * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking - * operators). - * - * @see Blocking Observable Operators - */ - public BlockingObservable toBlockingObservable() { - return BlockingObservable.from(this); - } - - /** - * Whether a given {@link Function} is an internal implementation inside rx.* packages or not. - *

- * For why this is being used see https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - * - * NOTE: If strong reasons for not depending on package names comes up then the implementation of this method can change to looking for a marker interface. -<<<<<<< HEAD - * -======= - * ->>>>>>> 128e598a9b4dafa4f03d65935bfad9c1e5467bfb - * @param o - * @return {@code true} if the given function is an internal implementation, and {@code false} otherwise. - */ - private boolean isInternalImplementation(Object o) { - if (o == null) { - return true; - } - // prevent double-wrapping (yeah it happens) - if (o instanceof SafeObserver) - return true; - // we treat the following package as "internal" and don't wrap it - Package p = o.getClass().getPackage(); // it can be null - return p != null && p.getName().startsWith("rx.operators"); - } - -} diff --git a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java index 1595c31195..c5970435e5 100644 --- a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java +++ b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java @@ -54,6 +54,15 @@ protected ConnectableObservable(OnSubscribeFunc onSubscribe) { * @return a {@link Observable} */ public Observable refCount() { - return Observable.create(OperationRefCount.refCount(this)); + return refCount(this); + } + + /** + * Returns an observable sequence that stays connected to the source as long + * as there is at least one subscription to the observable sequence. + * @return a {@link Observable} + */ + public static Observable refCount(ConnectableObservable that) { + return Observable.create(OperationRefCount.refCount(that)); } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationRefCount.java b/rxjava-core/src/main/java/rx/operators/OperationRefCount.java index 97cb8f738f..4617f3ac40 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationRefCount.java +++ b/rxjava-core/src/main/java/rx/operators/OperationRefCount.java @@ -15,11 +15,6 @@ */ package rx.operators; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.MockitoAnnotations; import rx.Observable; import rx.Observer; import rx.Subscription; @@ -27,8 +22,6 @@ import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; -import static org.mockito.Mockito.*; - /** * Returns an observable sequence that stays connected to the source as long * as there is at least one subscription to the observable sequence. @@ -69,78 +62,5 @@ public void call() { } }); } - - public static class UnitTest { - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void subscriptionToUnderlyingOnFirstSubscription() { - @SuppressWarnings("unchecked") - ConnectableObservable connectable = mock(ConnectableObservable.class); - Observable refCounted = Observable.create(refCount(connectable)); - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - when(connectable.subscribe(observer)).thenReturn(Subscriptions.empty()); - when(connectable.connect()).thenReturn(Subscriptions.empty()); - refCounted.subscribe(observer); - verify(connectable, times(1)).subscribe(observer); - verify(connectable, times(1)).connect(); - } - - @Test - public void noSubscriptionToUnderlyingOnSecondSubscription() { - @SuppressWarnings("unchecked") - ConnectableObservable connectable = mock(ConnectableObservable.class); - Observable refCounted = Observable.create(refCount(connectable)); - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - when(connectable.subscribe(observer)).thenReturn(Subscriptions.empty()); - when(connectable.connect()).thenReturn(Subscriptions.empty()); - refCounted.subscribe(observer); - refCounted.subscribe(observer); - verify(connectable, times(2)).subscribe(observer); - verify(connectable, times(1)).connect(); - } - - @Test - public void unsubscriptionFromUnderlyingOnLastUnsubscription() { - @SuppressWarnings("unchecked") - ConnectableObservable connectable = mock(ConnectableObservable.class); - Observable refCounted = Observable.create(refCount(connectable)); - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - Subscription underlying = mock(Subscription.class); - when(connectable.subscribe(observer)).thenReturn(underlying); - Subscription connection = mock(Subscription.class); - when(connectable.connect()).thenReturn(connection); - Subscription first = refCounted.subscribe(observer); - first.unsubscribe(); - verify(underlying, times(1)).unsubscribe(); - verify(connection, times(1)).unsubscribe(); - } - - @Test - public void noUnsubscriptionFromUnderlyingOnFirstUnsubscription() { - @SuppressWarnings("unchecked") - ConnectableObservable connectable = mock(ConnectableObservable.class); - Observable refCounted = Observable.create(refCount(connectable)); - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - Subscription underlying = mock(Subscription.class); - when(connectable.subscribe(observer)).thenReturn(underlying); - Subscription connection = mock(Subscription.class); - when(connectable.connect()).thenReturn(connection); - Subscription first = refCounted.subscribe(observer); - Subscription second = refCounted.subscribe(observer); - first.unsubscribe(); - second.unsubscribe(); - verify(underlying, times(2)).unsubscribe(); - verify(connection, times(1)).unsubscribe(); - } - } } } \ No newline at end of file diff --git a/rxjava-core/src/test/java/rx/RefCountTests.java b/rxjava-core/src/test/java/rx/RefCountTests.java new file mode 100644 index 0000000000..dae95bf265 --- /dev/null +++ b/rxjava-core/src/test/java/rx/RefCountTests.java @@ -0,0 +1,82 @@ +package rx; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; +import rx.observables.ConnectableObservable; +import rx.subscriptions.Subscriptions; + +import static org.mockito.Mockito.*; + +public class RefCountTests { + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void subscriptionToUnderlyingOnFirstSubscription() { + @SuppressWarnings("unchecked") + ConnectableObservable connectable = mock(ConnectableObservable.class); + Observable refCounted = ConnectableObservable.refCount(connectable); + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + when(connectable.subscribe(any(Observer.class))).thenReturn(Subscriptions.empty()); + when(connectable.connect()).thenReturn(Subscriptions.empty()); + refCounted.subscribe(observer); + verify(connectable, times(1)).subscribe(any(Observer.class)); + verify(connectable, times(1)).connect(); + } + + @Test + public void noSubscriptionToUnderlyingOnSecondSubscription() { + @SuppressWarnings("unchecked") + ConnectableObservable connectable = mock(ConnectableObservable.class); + Observable refCounted = ConnectableObservable.refCount(connectable); + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + when(connectable.subscribe(any(Observer.class))).thenReturn(Subscriptions.empty()); + when(connectable.connect()).thenReturn(Subscriptions.empty()); + refCounted.subscribe(observer); + refCounted.subscribe(observer); + verify(connectable, times(2)).subscribe(any(Observer.class)); + verify(connectable, times(1)).connect(); + } + + @Test + public void unsubscriptionFromUnderlyingOnLastUnsubscription() { + @SuppressWarnings("unchecked") + ConnectableObservable connectable = mock(ConnectableObservable.class); + Observable refCounted = ConnectableObservable.refCount(connectable); + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Subscription underlying = mock(Subscription.class); + when(connectable.subscribe(any(Observer.class))).thenReturn(underlying); + Subscription connection = mock(Subscription.class); + when(connectable.connect()).thenReturn(connection); + Subscription first = refCounted.subscribe(observer); + first.unsubscribe(); + verify(underlying, times(1)).unsubscribe(); + verify(connection, times(1)).unsubscribe(); + } + + @Test + public void noUnsubscriptionFromUnderlyingOnFirstUnsubscription() { + @SuppressWarnings("unchecked") + ConnectableObservable connectable = mock(ConnectableObservable.class); + Observable refCounted = ConnectableObservable.refCount(connectable); + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Subscription underlying = mock(Subscription.class); + when(connectable.subscribe(any(Observer.class))).thenReturn(underlying); + Subscription connection = mock(Subscription.class); + when(connectable.connect()).thenReturn(connection); + Subscription first = refCounted.subscribe(observer); + Subscription second = refCounted.subscribe(observer); + first.unsubscribe(); + second.unsubscribe(); + verify(underlying, times(2)).unsubscribe(); + verify(connection, times(1)).unsubscribe(); + } +} From 8f1072903b634f43e45d23aab74296ff23ec8694 Mon Sep 17 00:00:00 2001 From: Joachim Hofer Date: Fri, 13 Sep 2013 14:00:11 +0200 Subject: [PATCH 005/333] implemented mapWithIndex --- rxjava-core/src/main/java/rx/Observable.java | 18 ++ .../main/java/rx/operators/OperationMap.java | 166 +++++++++++++----- 2 files changed, 144 insertions(+), 40 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index c0ecf6226f..872e3deb16 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -3031,11 +3031,29 @@ public Observable where(Func1 predicate) { * a function to apply to each item emitted by the Observable * @return an Observable that emits the items from the source Observable, transformed by the * given function + * @see MSDN: Observable.Select */ public Observable map(Func1 func) { return create(OperationMap.map(this, func)); } + /** + * Returns an Observable that applies the given function to each item emitted by an + * Observable and emits the result. + *

+ * + * + * @param func + * a function to apply to each item emitted by the Observable. The function takes the + * index of the emitted item as additional parameter. + * @return an Observable that emits the items from the source Observable, transformed by the + * given function + * @see MSDN: Observable.Select + */ + public Observable mapWithIndex(Func2 func) { + return create(OperationMap.mapWithIndex(this, func)); + } + /** * Creates a new Observable by applying a function that you supply to each item emitted by * the source Observable, where that function returns an Observable, and then merging those diff --git a/rxjava-core/src/main/java/rx/operators/OperationMap.java b/rxjava-core/src/main/java/rx/operators/OperationMap.java index fa7fca2758..9eb2520420 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMap.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMap.java @@ -25,6 +25,7 @@ import org.junit.Before; import org.junit.Test; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -33,6 +34,7 @@ import rx.Observer; import rx.Subscription; import rx.util.functions.Func1; +import rx.util.functions.Func2; /** * Applies a function of your choosing to every item emitted by an Observable, and returns this @@ -56,8 +58,42 @@ public final class OperationMap { * the type of the output sequence. * @return a sequence that is the result of applying the transformation function to each item in the input sequence. */ - public static OnSubscribeFunc map(Observable sequence, Func1 func) { - return new MapObservable(sequence, func); + public static OnSubscribeFunc map(final Observable sequence, final Func1 func) { + return new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + return new MapObservable(sequence, new Func2() { + @Override + public R call(T value, @SuppressWarnings("unused") Integer unused) { + return func.call(value); + } + }).onSubscribe(observer); + } + }; + } + + /** + * Accepts a sequence and a transformation function. Returns a sequence that is the result of + * applying the transformation function to each item in the sequence. + * + * @param sequence + * the input sequence. + * @param func + * a function to apply to each item in the sequence. The function gets the index of the emitted item + * as additional parameter. + * @param + * the type of the input sequence. + * @param + * the type of the output sequence. + * @return a sequence that is the result of applying the transformation function to each item in the input sequence. + */ + public static OnSubscribeFunc mapWithIndex(final Observable sequence, final Func2 func) { + return new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + return new MapObservable(sequence, func).onSubscribe(observer); + } + }; } /** @@ -89,56 +125,50 @@ public static OnSubscribeFunc mapMany(Observable sequence * the type of the output sequence. */ private static class MapObservable implements OnSubscribeFunc { - public MapObservable(Observable sequence, Func1 func) { + public MapObservable(Observable sequence, Func2 func) { this.sequence = sequence; this.func = func; } - private Observable sequence; - - private Func1 func; - - public Subscription onSubscribe(Observer observer) { - return sequence.subscribe(new MapObserver(observer, func)); - } - } - - /** - * An observer that applies a transformation function to each item and forwards the result to an inner observer. - * - * @param - * the type of the observer items. - * @param - * the type of the inner observer items. - */ - private static class MapObserver implements Observer { - public MapObserver(Observer observer, Func1 func) { - this.observer = observer; - this.func = func; - } - - Observer observer; - - Func1 func; + private final Observable sequence; + private final Func2 func; + private int index; - public void onNext(T value) { - // let the exception be thrown if func fails as a SafeObserver wrapping this will handle it - observer.onNext(func.call(value)); - } + @Override + public Subscription onSubscribe(final Observer observer) { + return sequence.subscribe(new Observer() { + @Override + public void onNext(T value) { + observer.onNext(func.call(value, index)); + index++; + } - public void onError(Throwable ex) { - observer.onError(ex); - } + @Override + public void onError(Throwable ex) { + observer.onError(ex); + } - public void onCompleted() { - observer.onCompleted(); + @Override + public void onCompleted() { + observer.onCompleted(); + } + }); } } public static class UnitTest { @Mock Observer stringObserver; - + @Mock + Observer stringObserver2; + + final static Func2 APPEND_INDEX = new Func2() { + @Override + public String call(String value, Integer index) { + return value + index; + } + }; + @Before public void before() { MockitoAnnotations.initMocks(this); @@ -164,9 +194,42 @@ public String call(Map map) { verify(stringObserver, times(1)).onNext("OneFirst"); verify(stringObserver, times(1)).onNext("TwoFirst"); verify(stringObserver, times(1)).onCompleted(); + } + @Test + public void testMapWithIndex() { + Observable w = Observable.from("a", "b", "c"); + Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX)); + m.subscribe(stringObserver); + InOrder inOrder = inOrder(stringObserver); + inOrder.verify(stringObserver, times(1)).onNext("a0"); + inOrder.verify(stringObserver, times(1)).onNext("b1"); + inOrder.verify(stringObserver, times(1)).onNext("c2"); + inOrder.verify(stringObserver, times(1)).onCompleted(); + verify(stringObserver, never()).onError(any(Throwable.class)); } + + @Test + public void testMapWithIndexAndMultipleSubscribers() { + Observable w = Observable.from("a", "b", "c"); + Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX)); + m.subscribe(stringObserver); + m.subscribe(stringObserver2); + InOrder inOrder = inOrder(stringObserver); + inOrder.verify(stringObserver, times(1)).onNext("a0"); + inOrder.verify(stringObserver, times(1)).onNext("b1"); + inOrder.verify(stringObserver, times(1)).onNext("c2"); + inOrder.verify(stringObserver, times(1)).onCompleted(); + verify(stringObserver, never()).onError(any(Throwable.class)); + InOrder inOrder2 = inOrder(stringObserver2); + inOrder2.verify(stringObserver2, times(1)).onNext("a0"); + inOrder2.verify(stringObserver2, times(1)).onNext("b1"); + inOrder2.verify(stringObserver2, times(1)).onNext("c2"); + inOrder2.verify(stringObserver2, times(1)).onCompleted(); + verify(stringObserver2, never()).onError(any(Throwable.class)); + } + @Test public void testMapMany() { /* simulate a top-level async call which returns IDs */ @@ -246,12 +309,34 @@ public String call(Map map) { } + @Test + public void testMapWithError() { + Observable w = Observable.from("one", "fail", "two", "three", "fail"); + Observable m = Observable.create(map(w, new Func1() { + @Override + public String call(String s) { + if ("fail".equals(s)) { + throw new RuntimeException("Forced Failure"); + } + return s; + } + })); + + m.subscribe(stringObserver); + verify(stringObserver, times(1)).onNext("one"); + verify(stringObserver, never()).onNext("two"); + verify(stringObserver, never()).onNext("three"); + verify(stringObserver, never()).onCompleted(); + verify(stringObserver, times(1)).onError(any(Throwable.class)); + } + @Test public void testMapWithSynchronousObservableContainingError() { Observable w = Observable.from("one", "fail", "two", "three", "fail"); final AtomicInteger c1 = new AtomicInteger(); final AtomicInteger c2 = new AtomicInteger(); Observable m = Observable.create(map(w, new Func1() { + @Override public String call(String s) { if ("fail".equals(s)) throw new RuntimeException("Forced Failure"); @@ -260,6 +345,7 @@ public String call(String s) { return s; } })).map(new Func1() { + @Override public String call(String s) { System.out.println("SecondMapper:" + s); c2.incrementAndGet(); @@ -280,7 +366,7 @@ public String call(String s) { assertEquals(1, c2.get()); } - private Map getMap(String prefix) { + private static Map getMap(String prefix) { Map m = new HashMap(); m.put("firstName", prefix + "First"); m.put("lastName", prefix + "Last"); From 6833becc7a075c220c99bb2727f50b9b096c797c Mon Sep 17 00:00:00 2001 From: Matt Jacobs Date: Fri, 13 Sep 2013 15:59:40 -0700 Subject: [PATCH 006/333] Removing deprecated RxImplicits from rxjava-scala --- .../scala/rx/lang/scala/RxImplicits.scala | 132 ----- .../rx/lang/scala/RxImplicitsTests.scala | 474 ------------------ 2 files changed, 606 deletions(-) delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/RxImplicits.scala delete mode 100644 language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/RxImplicitsTests.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/RxImplicits.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/RxImplicits.scala deleted file mode 100644 index d0d33954b7..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/RxImplicits.scala +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright 2013 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.lang.scala - -/** - * This is the old Scala adaptor. It is kept here for backwards compatibility. - * The new adaptor is {@code rx.lang.scala.Observable}. - */ -@deprecated("use rx.lang.scala.Observable instead", "0.14") -object RxImplicits { - import java.{ lang => jlang } - import language.implicitConversions - - import rx.{ Observable, Observer, Subscription } - import rx.Observable.OnSubscribeFunc - import rx.observables.BlockingObservable - import rx.util.functions._ - - /** - * Converts 0-arg function to Rx Action0 - */ - implicit def scalaFunction0ProducingUnitToAction0(f: (() => Unit)): Action0 = - new Action0 { - def call(): Unit = f() - } - - /** - * Converts 1-arg function to Rx Action1 - */ - implicit def scalaFunction1ProducingUnitToAction1[A](f: (A => Unit)): Action1[A] = - new Action1[A] { - def call(a: A): Unit = f(a) - } - - /** - * Converts 1-arg predicate to Rx Func1[A, java.lang.Boolean] - */ - implicit def scalaBooleanFunction1ToRxBooleanFunc1[A](f: (A => Boolean)): Func1[A, jlang.Boolean] = - new Func1[A, jlang.Boolean] { - def call(a: A): jlang.Boolean = f(a).booleanValue - } - - /** - * Converts a specific function shape (used in takeWhile) to the equivalent Java types with an Rx Func2 - */ - implicit def convertTakeWhileFuncToRxFunc2[A](f: (A, Int) => Boolean): Func2[A, jlang.Integer, jlang.Boolean] = - new Func2[A, jlang.Integer, jlang.Boolean] { - def call(a: A, b: jlang.Integer): jlang.Boolean = f(a, b).booleanValue - } - - /** - * Converts a function shaped like compareTo into the equivalent Rx Func2 - */ - implicit def convertComparisonFuncToRxFunc2[A](f: (A, A) => Int): Func2[A, A, jlang.Integer] = - new Func2[A, A, jlang.Integer] { - def call(a1: A, a2: A): jlang.Integer = f(a1, a2).intValue - } - - /* - * This implicit allows Scala code to use any exception type and still work - * with invariant Func1 interface - */ - implicit def exceptionFunction1ToRxExceptionFunc1[A <: Exception, B](f: (A => B)): Func1[Exception, B] = - new Func1[Exception, B] { - def call(ex: Exception): B = f(ex.asInstanceOf[A]) - } - - /** - * The following implicits convert functions of different arities into the Rx equivalents - */ - implicit def scalaFunction0ToRxFunc0[A](f: () => A): Func0[A] = - new Func0[A] { - def call(): A = f() - } - - implicit def scalaFunction1ToRxFunc1[A, B](f: (A => B)): Func1[A, B] = - new Func1[A, B] { - def call(a: A): B = f(a) - } - - implicit def scalaFunction2ToRxFunc2[A, B, C](f: (A, B) => C): Func2[A, B, C] = - new Func2[A, B, C] { - def call(a: A, b: B) = f(a, b) - } - - implicit def scalaFunction3ToRxFunc3[A, B, C, D](f: (A, B, C) => D): Func3[A, B, C, D] = - new Func3[A, B, C, D] { - def call(a: A, b: B, c: C) = f(a, b, c) - } - - implicit def scalaFunction4ToRxFunc4[A, B, C, D, E](f: (A, B, C, D) => E): Func4[A, B, C, D, E] = - new Func4[A, B, C, D, E] { - def call(a: A, b: B, c: C, d: D) = f(a, b, c, d) - } - - implicit def onSubscribeFunc[A](f: (Observer[_ >: A]) => Subscription): OnSubscribeFunc[A] = - new OnSubscribeFunc[A] { - override def onSubscribe(a: Observer[_ >: A]) = f(a) - } - - /** - * This implicit class implements all of the methods necessary for including Observables in a - * for-comprehension. Note that return type is always Observable, so that the ScalaObservable - * type never escapes the for-comprehension - */ - implicit class ScalaObservable[A](wrapped: Observable[A]) { - def map[B](f: A => B): Observable[B] = wrapped.map[B](f) - def flatMap[B](f: A => Observable[B]): Observable[B] = wrapped.mapMany(f) - def foreach(f: A => Unit): Unit = wrapped.toBlockingObservable.forEach(f) - def withFilter(p: A => Boolean): WithFilter = new WithFilter(p) - - class WithFilter(p: A => Boolean) { - def map[B](f: A => B): Observable[B] = wrapped.filter(p).map(f) - def flatMap[B](f: A => Observable[B]): Observable[B] = wrapped.filter(p).flatMap(f) - def foreach(f: A => Unit): Unit = wrapped.filter(p).toBlockingObservable.forEach(f) - def withFilter(p: A => Boolean): Observable[A] = wrapped.filter(p) - } - } -} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/RxImplicitsTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/RxImplicitsTests.scala deleted file mode 100644 index c2252d2ee6..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/RxImplicitsTests.scala +++ /dev/null @@ -1,474 +0,0 @@ -/** - * Copyright 2013 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.lang.scala - -import org.scalatest.junit.JUnitSuite - -/** - * This is the test suite for the old Scala adaptor. - */ -class OldUnitTestSuite extends JUnitSuite { - import rx.lang.scala.RxImplicits._ - - import org.junit.{ Before, Test } - import org.junit.Assert._ - import org.mockito.Matchers.any - import org.mockito.Mockito._ - import org.mockito.{ MockitoAnnotations, Mock } - import rx.{ Notification, Observer, Observable, Subscription } - import rx.Observable.OnSubscribeFunc - import rx.observables.GroupedObservable - import rx.subscriptions.Subscriptions - import collection.mutable.ArrayBuffer - import collection.JavaConverters._ - - @Mock private[this] - val observer: Observer[Any] = null - - @Mock private[this] - val subscription: Subscription = null - - val isOdd = (i: Int) => i % 2 == 1 - val isEven = (i: Int) => i % 2 == 0 - - class OnSubscribeWithException(s: Subscription, values: String*) extends OnSubscribeFunc[String] { - var t: Thread = null - - override def onSubscribe(observer: Observer[_ >: String]): Subscription = { - println("ObservableWithException subscribed to ...") - try { - println("running ObservableWithException thread") - values.toList.foreach(v => { - println("ObservableWithException onNext: " + v) - observer.onNext(v) - }) - throw new RuntimeException("Forced Failure") - } catch { - case ex: Exception => observer.onError(ex) - } - s - } - } - - @Before def before { - MockitoAnnotations.initMocks(this) - } - - // tests of static methods - @Test def testSingle { - assertEquals(1, Observable.from(1).toBlockingObservable.single) - } - - @Test def testSinglePredicate { - val found = Observable.from(1, 2, 3).toBlockingObservable.single(isEven) - assertEquals(2, found) - } - - @Test def testSingleOrDefault { - assertEquals(0, Observable.empty[Int]().toBlockingObservable.singleOrDefault(0)) - assertEquals(1, Observable.from(1).toBlockingObservable.singleOrDefault(0)) - try { - Observable.from(1, 2, 3).toBlockingObservable.singleOrDefault(0) - fail("Did not catch any exception, expected IllegalStateException") - } catch { - case ex: IllegalStateException => println("Caught expected IllegalStateException") - case ex: Throwable => fail("Caught unexpected exception " + ex.getCause + ", expected IllegalStateException") - } - } - - @Test def testSingleOrDefaultPredicate { - assertEquals(2, Observable.from(1, 2, 3).toBlockingObservable.singleOrDefault(0, isEven)) - assertEquals(0, Observable.from(1, 3).toBlockingObservable.singleOrDefault(0, isEven)) - try { - Observable.from(1, 2, 3).toBlockingObservable.singleOrDefault(0, isOdd) - fail("Did not catch any exception, expected IllegalStateException") - } catch { - case ex: IllegalStateException => println("Caught expected IllegalStateException") - case ex: Throwable => fail("Caught unexpected exception " + ex.getCause + ", expected IllegalStateException") - } - } - - @Test def testCreateFromOnSubscribeFunc { - val created = Observable.create((o: Observer[_ >: Integer]) => Subscriptions.empty) - //no assertions on subscription, just testing the implicit - } - - @Test def testFromJavaInterop { - val observable = Observable.from(List(1, 2, 3).asJava) - assertSubscribeReceives(observable)(1, 2, 3) - } - - @Test def testSubscribe { - val observable = Observable.from("1", "2", "3") - assertSubscribeReceives(observable)("1", "2", "3") - } - - //should not compile - adapted from https://gist.github.com/jmhofer/5195589 - /*@Test def testSubscribeOnInt() { - val observable = Observable.from("1", "2", "3") - observable.subscribe((arg: Int) => { - println("testSubscribe: arg = " + arg) - }) - }*/ - - @Test def testDefer { - val lazyObservableFactory = () => Observable.from(1, 2) - val observable = Observable.defer(lazyObservableFactory) - assertSubscribeReceives(observable)(1, 2) - } - - @Test def testJust { - val observable = Observable.just("foo") - assertSubscribeReceives(observable)("foo") - } - - @Test def testMerge { - val observable1 = Observable.from(1, 2, 3) - val observable2 = Observable.from(4, 5, 6) - val merged = Observable.merge(observable1, observable2) - assertSubscribeReceives(merged)(1, 2, 3, 4, 5, 6) - } - - @Test def testFlattenMerge { - val observable = Observable.from(Observable.from(1, 2, 3)) - val merged = Observable.merge[Int](observable) - assertSubscribeReceives(merged)(1, 2, 3) - } - - @Test def testSequenceMerge { - val observable1 = Observable.from(1, 2, 3) - val observable2 = Observable.from(4, 5, 6) - val merged = Observable.merge(observable1, observable2) - assertSubscribeReceives(merged)(1, 2, 3, 4, 5, 6) - } - - @Test def testConcat { - val observable1 = Observable.from(1, 2, 3) - val observable2 = Observable.from(4, 5, 6) - val concatenated = Observable.concat(observable1, observable2) - assertSubscribeReceives(concatenated)(1, 2, 3, 4, 5, 6) - } - - @Test def testSynchronize { - val observable = Observable.from(1, 2, 3) - val synchronized = Observable.synchronize(observable) - assertSubscribeReceives(synchronized)(1, 2, 3) - } - - @Test def testZip2() { - val colors: Observable[String] = Observable.from("red", "green", "blue") - val names: Observable[String] = Observable.from("lion-o", "cheetara", "panthro") - - case class Character(color: String, name: String) - - val cheetara = Character("green", "cheetara") - val panthro = Character("blue", "panthro") - val characters = Observable.zip[String, String, Character](colors, names, Character.apply _) - assertSubscribeReceives(characters)(cheetara, panthro) - } - - @Test def testZip3() { - val numbers = Observable.from(1, 2, 3) - val colors = Observable.from("red", "green", "blue") - val names = Observable.from("lion-o", "cheetara", "panthro") - - case class Character(id: Int, color: String, name: String) - - val liono = Character(1, "red", "lion-o") - val cheetara = Character(2, "green", "cheetara") - val panthro = Character(3, "blue", "panthro") - - val characters = Observable.zip[Int, String, String, Character](numbers, colors, names, Character.apply _) - assertSubscribeReceives(characters)(liono, cheetara, panthro) - } - - @Test def testZip4() { - val numbers = Observable.from(1, 2, 3) - val colors = Observable.from("red", "green", "blue") - val names = Observable.from("lion-o", "cheetara", "panthro") - val isLeader = Observable.from(true, false, false) - - case class Character(id: Int, color: String, name: String, isLeader: Boolean) - - val liono = Character(1, "red", "lion-o", true) - val cheetara = Character(2, "green", "cheetara", false) - val panthro = Character(3, "blue", "panthro", false) - - val characters = Observable.zip[Int, String, String, Boolean, Character](numbers, colors, names, isLeader, Character.apply _) - assertSubscribeReceives(characters)(liono, cheetara, panthro) - } - - //tests of instance methods - - // missing tests for : takeUntil, groupBy, next, mostRecent - - @Test def testFilter { - val numbers = Observable.from(1, 2, 3, 4, 5, 6, 7, 8, 9) - val observable = numbers.filter(isEven) - assertSubscribeReceives(observable)(2, 4, 6, 8) - } - - @Test def testLast { - val observable = Observable.from(1, 2, 3, 4) - assertEquals(4, observable.toBlockingObservable.last) - } - - @Test def testLastPredicate { - val observable = Observable.from(1, 2, 3, 4) - assertEquals(3, observable.toBlockingObservable.last(isOdd)) - } - - @Test def testLastOrDefault { - val observable = Observable.from(1, 2, 3, 4) - assertEquals(4, observable.toBlockingObservable.lastOrDefault(5)) - assertEquals(5, Observable.empty[Int]().toBlockingObservable.lastOrDefault(5)) - } - - @Test def testLastOrDefaultPredicate { - val observable = Observable.from(1, 2, 3, 4) - assertEquals(3, observable.toBlockingObservable.lastOrDefault(5, isOdd)) - assertEquals(5, Observable.empty[Int]().toBlockingObservable.lastOrDefault(5, isOdd)) - } - - @Test def testMap { - val numbers = Observable.from(1, 2, 3, 4, 5, 6, 7, 8, 9) - val mappedNumbers = ArrayBuffer.empty[Int] - val mapped: Observable[Int] = numbers map ((x: Int) => x * x) - mapped.subscribe((squareVal: Int) => { - mappedNumbers.append(squareVal) - }) - assertEquals(List(1, 4, 9, 16, 25, 36, 49, 64, 81), mappedNumbers.toList) - } - - @Test def testMapMany { - val numbers = Observable.from(1, 2, 3, 4) - val f = (i: Int) => Observable.from(List(i, -i).asJava) - val mappedNumbers = ArrayBuffer.empty[Int] - numbers.mapMany(f).subscribe((i: Int) => { - mappedNumbers.append(i) - }) - assertEquals(List(1, -1, 2, -2, 3, -3, 4, -4), mappedNumbers.toList) - } - - @Test def testMaterialize { - val observable = Observable.from(1, 2, 3, 4) - val expectedNotifications: List[Notification[Int]] = - ((1.to(4).map(i => new Notification(i))) :+ new Notification()).toList - val actualNotifications: ArrayBuffer[Notification[Int]] = ArrayBuffer.empty - observable.materialize.subscribe((n: Notification[Int]) => { - actualNotifications.append(n) - }) - assertEquals(expectedNotifications, actualNotifications.toList) - } - - @Test def testDematerialize { - val notifications: List[Notification[Int]] = - ((1.to(4).map(i => new Notification(i))) :+ new Notification()).toList - val observableNotifications: Observable[Notification[Int]] = - Observable.from(notifications.asJava) - val observable: Observable[Int] = - observableNotifications.dematerialize() - assertSubscribeReceives(observable)(1, 2, 3, 4) - } - - @Test def testOnErrorResumeNextObservableNoError { - val observable = Observable.from(1, 2, 3, 4) - val resumeObservable = Observable.from(5, 6, 7, 8) - val observableWithErrorHandler = observable.onErrorResumeNext(resumeObservable) - assertSubscribeReceives(observableWithErrorHandler)(1, 2, 3, 4) - } - - @Test def testOnErrorResumeNextObservableErrorOccurs { - val observable = Observable.create(new OnSubscribeWithException(subscription, "foo", "bar")) - val resumeObservable = Observable.from("a", "b", "c", "d") - val observableWithErrorHandler = observable.onErrorResumeNext(resumeObservable) - observableWithErrorHandler.subscribe(observer.asInstanceOf[Observer[String]]) - - List("foo", "bar", "a", "b", "c", "d").foreach(t => verify(observer, times(1)).onNext(t)) - verify(observer, never()).onError(any(classOf[Exception])) - verify(observer, times(1)).onCompleted() - } - - @Test def testOnErrorResumeNextFuncNoError { - val observable = Observable.from(1, 2, 3, 4) - val resumeFunc = (ex: Throwable) => Observable.from(5, 6, 7, 8) - val observableWithErrorHandler = observable.onErrorResumeNext(resumeFunc) - assertSubscribeReceives(observableWithErrorHandler)(1, 2, 3, 4) - } - - @Test def testOnErrorResumeNextFuncErrorOccurs { - val observable = Observable.create(new OnSubscribeWithException(subscription, "foo", "bar")) - val resumeFunc = (ex: Throwable) => Observable.from("a", "b", "c", "d") - val observableWithErrorHandler = observable.onErrorResumeNext(resumeFunc) - observableWithErrorHandler.subscribe(observer.asInstanceOf[Observer[String]]) - - List("foo", "bar", "a", "b", "c", "d").foreach(t => verify(observer, times(1)).onNext(t)) - verify(observer, never()).onError(any(classOf[Exception])) - verify(observer, times(1)).onCompleted() - } - - @Test def testOnErrorReturnFuncNoError { - val observable = Observable.from(1, 2, 3, 4) - val returnFunc = (ex: Throwable) => 87 - val observableWithErrorHandler = observable.onErrorReturn(returnFunc) - assertSubscribeReceives(observableWithErrorHandler)(1, 2, 3, 4) - } - - @Test def testOnErrorReturnFuncErrorOccurs { - val observable = Observable.create(new OnSubscribeWithException(subscription, "foo", "bar")) - val returnFunc = (ex: Throwable) => "baz" - val observableWithErrorHandler = observable.onErrorReturn(returnFunc) - observableWithErrorHandler.subscribe(observer.asInstanceOf[Observer[String]]) - - List("foo", "bar", "baz").foreach(t => verify(observer, times(1)).onNext(t)) - verify(observer, never()).onError(any(classOf[Exception])) - verify(observer, times(1)).onCompleted() - } - - @Test def testReduce { - val observable = Observable.from(1, 2, 3, 4) - assertEquals(10, observable.reduce((a: Int, b: Int) => a + b).toBlockingObservable.single) - } - - @Test def testSkip { - val observable = Observable.from(1, 2, 3, 4) - val skipped = observable.skip(2) - assertSubscribeReceives(skipped)(3, 4) - } - - @Test def testTake { - val observable = Observable.from(1, 2, 3, 4, 5) - val took = observable.take(2) - assertSubscribeReceives(took)(1, 2) - } - - @Test def testTakeWhile { - val observable = Observable.from(1, 3, 5, 6, 7, 9, 11) - val took = observable.takeWhile(isOdd) - assertSubscribeReceives(took)(1, 3, 5) - } - - @Test def testTakeWhileWithIndex { - val observable = Observable.from(1, 3, 5, 7, 9, 11, 12, 13, 15, 17) - val took = observable.takeWhileWithIndex((i: Int, idx: Int) => isOdd(i) && idx < 8) - assertSubscribeReceives(took)(1, 3, 5, 7, 9, 11) - } - - @Test def testTakeLast { - val observable = Observable.from(1, 2, 3, 4, 5, 6, 7, 8, 9) - val tookLast = observable.takeLast(3) - assertSubscribeReceives(tookLast)(7, 8, 9) - } - - @Test def testToList { - val observable = Observable.from(1, 2, 3, 4) - val toList = observable.toList - assertSubscribeReceives(toList)(List(1, 2, 3, 4).asJava) - } - - @Test def testToSortedList { - val observable = Observable.from(1, 3, 4, 2) - val toSortedList = observable.toSortedList - assertSubscribeReceives(toSortedList)(List(1, 2, 3, 4).asJava) - } - - @Test def testToArbitrarySortedList { - val observable = Observable.from("a", "aaa", "aaaa", "aa") - val sortByLength = (s1: String, s2: String) => s1.length.compareTo(s2.length) - val toSortedList = observable.toSortedList(sortByLength) - assertSubscribeReceives(toSortedList)(List("a", "aa", "aaa", "aaaa").asJava) - } - - @Test def testToIterable { - val observable = Observable.from(1, 2) - val it = observable.toBlockingObservable.toIterable.iterator - assertTrue(it.hasNext) - assertEquals(1, it.next) - assertTrue(it.hasNext) - assertEquals(2, it.next) - assertFalse(it.hasNext) - } - - @Test def testStartWith { - val observable = Observable.from(1, 2, 3, 4) - val newStart = observable.startWith(-1, 0) - assertSubscribeReceives(newStart)(-1, 0, 1, 2, 3, 4) - } - - @Test def testOneLineForComprehension { - val mappedObservable = for { - i: Int <- Observable.from(1, 2, 3, 4) - } yield i + 1 - assertSubscribeReceives(mappedObservable)(2, 3, 4, 5) - assertFalse(mappedObservable.isInstanceOf[ScalaObservable[_]]) - } - - @Test def testSimpleMultiLineForComprehension { - val flatMappedObservable = for { - i: Int <- Observable.from(1, 2, 3, 4) - j: Int <- Observable.from(1, 10, 100, 1000) - } yield i + j - assertSubscribeReceives(flatMappedObservable)(2, 12, 103, 1004) - assertFalse(flatMappedObservable.isInstanceOf[ScalaObservable[_]]) - } - - @Test def testMultiLineForComprehension { - val doubler = (i: Int) => Observable.from(i, i) - val flatMappedObservable = for { - i: Int <- Observable.from(1, 2, 3, 4) - j: Int <- doubler(i) - } yield j - //can't use assertSubscribeReceives since each number comes in 2x - flatMappedObservable.subscribe(observer.asInstanceOf[Observer[Int]]) - List(1, 2, 3, 4).foreach(i => verify(observer, times(2)).onNext(i)) - verify(observer, never()).onError(any(classOf[Exception])) - verify(observer, times(1)).onCompleted() - assertFalse(flatMappedObservable.isInstanceOf[ScalaObservable[_]]) - } - - @Test def testFilterInForComprehension { - val doubler = (i: Int) => Observable.from(i, i) - val filteredObservable: Observable[Int] = for { - i: Int <- Observable.from(1, 2, 3, 4) - j: Int <- doubler(i) if isOdd(i) - } yield j - //can't use assertSubscribeReceives since each number comes in 2x - filteredObservable.subscribe(observer.asInstanceOf[Observer[Int]]) - List(1, 3).foreach(i => verify(observer, times(2)).onNext(i)) - verify(observer, never()).onError(any(classOf[Exception])) - verify(observer, times(1)).onCompleted() - assertFalse(filteredObservable.isInstanceOf[ScalaObservable[_]]) - } - - @Test def testForEachForComprehension { - val doubler = (i: Int) => Observable.from(i, i) - val intBuffer = ArrayBuffer.empty[Int] - val forEachComprehension = for { - i: Int <- Observable.from(1, 2, 3, 4) - j: Int <- doubler(i) if isEven(i) - } { - intBuffer.append(j) - } - assertEquals(List(2, 2, 4, 4), intBuffer.toList) - } - - private def assertSubscribeReceives[T](o: Observable[T])(values: T*) = { - o.subscribe(observer.asInstanceOf[Observer[T]]) - values.toList.foreach(t => verify(observer, times(1)).onNext(t)) - verify(observer, never()).onError(any(classOf[Exception])) - verify(observer, times(1)).onCompleted() - } -} From edb1e60c693a3618735080cfe7fb63d0ee5e79cc Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Sat, 14 Sep 2013 13:57:51 +0200 Subject: [PATCH 007/333] first Scala groupBy implementation --- .../main/scala/rx/lang/scala/Observable.scala | 52 +++++-------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 4c4cccf4a0..550f042c4b 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1215,51 +1215,25 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) // because we can just use ++ instead /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. - *

- * + * Groups the items emitted by this Observable according to a specified discriminator function. * - * @param keySelector + * @param f * a function that extracts the key from an item - * @param elementSelector - * a function to map a source item to an item in a {@link GroupedObservable} - * @param - * the key type - * @param - * the type of items emitted by the resulting {@link GroupedObservable}s - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value - */ - /* TODO make a Scala GroupedObservable and groupBy - def groupBy[K,R](keySelector: T => K, elementSelector: T => R ): Observable[GroupedObservable[K,R]] = { - ??? - } - */ - // public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) - - /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. - *

- * - * - * @param keySelector - * a function that extracts the key for each item * @param - * the key type - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value + * the type of keys returned by the discriminator function. + * @return an Observable that emits {@code (key, observable)} pairs, where {@code observable} + * contains all items for which {@code f} returned {@code key}. */ - /* TODO - def groupBy[K](keySelector: T => K ): Observable[GroupedObservable[K,T]] = { - ??? + def groupBy[K](f: T => K): Observable[(K, Observable[T])] = { + val o1 = asJava.groupBy[K](f) : rx.Observable[_ <: rx.observables.GroupedObservable[K, _ <: T]] + val func = (o: rx.observables.GroupedObservable[K, _ <: T]) => (o.getKey(), Observable[T](o)) + Observable[(K, Observable[T])](o1.map[(K, Observable[T])](func)) } - */ - // public Observable> groupBy(final Func1 keySelector) + // There's no method corresponding to + // public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) + // because this can be obtained by combining groupBy and map (as in Scala) + /** * Given an Observable that emits Observables, creates a single Observable that * emits the items emitted by the most recently published of those Observables. From 8d33df1198d385c5f1b6a38ce85f2f315fd9a632 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Sat, 14 Sep 2013 13:58:12 +0200 Subject: [PATCH 008/333] start groupBy examples --- .../rx/lang/scala/examples/Olympics.scala | 39 ++++++++++++++++ .../rx/lang/scala/examples/RxScalaDemo.scala | 45 ++++++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala new file mode 100644 index 0000000000..f2180cf402 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala @@ -0,0 +1,39 @@ +package rx.lang.scala.examples + +object Olympics { + case class Medal(val year: Int, val games: String, val discipline: String, val medal: String, val athlete: String, val country: String) + + val mountainBikeMedals = List( + Medal(2012, "London 2012", "cross-country men", "Gold", "Jaroslav KULHAVY", "Czech Republic"), + Medal(2012, "London 2012", "cross-country men", "Silver", "Nino SCHURTER", "Switzerland"), + Medal(2012, "London 2012", "cross-country men", "Bronze", "Marco Aurelio FONTANA", "Italy"), + Medal(2012, "London 2012", "cross-country women", "Gold", "Julie BRESSET", "France"), + Medal(2012, "London 2012", "cross-country women", "Silver", "Sabine SPITZ", "Germany"), + Medal(2012, "London 2012", "cross-country women", "Bronze", "Georgia GOULD", "United States of America"), + Medal(2008, "Beijing 2008", "cross-country women", "Gold", "Sabine SPITZ", "Germany"), + Medal(2008, "Beijing 2008", "cross-country women", "Silver", "Maja WLOSZCZOWSKA", "Poland"), + Medal(2008, "Beijing 2008", "cross-country women", "Bronze", "Irina KALENTYEVA", "Russian Federation"), + Medal(2008, "Beijing 2008", "cross-country men", "Gold", "Julien ABSALON", "France"), + Medal(2008, "Beijing 2008", "cross-country men", "Silver", "Jean-Christophe PERAUD", "France"), + Medal(2008, "Beijing 2008", "cross-country men", "Bronze", "Nino SCHURTER", "Switzerland"), + Medal(2004, "Athens 2004", "cross-country men", "Gold", "Julien ABSALON", "France"), + Medal(2004, "Athens 2004", "cross-country men", "Silver", "Jose Antonio HERMIDA RAMOS", "Spain"), + Medal(2004, "Athens 2004", "cross-country men", "Bronze", "Bart BRENTJENS", "Netherlands"), + Medal(2004, "Athens 2004", "cross-country women", "Gold", "Gunn-Rita DAHLE", "Norway"), + Medal(2004, "Athens 2004", "cross-country women", "Silver", "Marie-Helene PREMONT", "Canada"), + Medal(2004, "Athens 2004", "cross-country women", "Bronze", "Sabine SPITZ", "Germany"), + Medal(2000, "Sydney 2000", "cross-country women", "Gold", "Paola PEZZO", "Italy"), + Medal(2000, "Sydney 2000", "cross-country women", "Silver", "Barbara BLATTER", "Switzerland"), + Medal(2000, "Sydney 2000", "cross-country women", "Bronze", "Marga FULLANA", "Spain"), + Medal(2000, "Sydney 2000", "cross-country men", "Gold", "Miguel MARTINEZ", "France"), + Medal(2000, "Sydney 2000", "cross-country men", "Silver", "Filip MEIRHAEGHE", "Belgium"), + Medal(2000, "Sydney 2000", "cross-country men", "Bronze", "Christoph SAUSER", "Switzerland"), + Medal(1996, "Atlanta 1996", "cross-country men", "Silver", "Thomas FRISCHKNECHT", "Switzerland"), + Medal(1996, "Atlanta 1996", "cross-country men", "Bronze", "Miguel MARTINEZ", "France"), + Medal(1996, "Atlanta 1996", "cross-country men", "Gold", "Bart BRENTJENS", "Netherlands"), + Medal(1996, "Atlanta 1996", "cross-country women", "Gold", "Paola PEZZO", "Italy"), + Medal(1996, "Atlanta 1996", "cross-country women", "Silver", "Alison SYDOR", "Canada"), + Medal(1996, "Atlanta 1996", "cross-country women", "Bronze", "Susan DEMATTEI", "United States of America") + ).reverse + +} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index d46bfe1d3b..461c280f4c 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -23,7 +23,7 @@ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ import rx.lang.scala.concurrency.NewThreadScheduler -@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily +//@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @Test def intervalExample() { @@ -158,6 +158,49 @@ class RxScalaDemo extends JUnitSuite { waitFor(o) } + def sampleAllUntilComplete[T](o: Observable[T], period: Duration): Observable[T] = { + for ((element, tick) <- o zip Observable.interval(period)) yield element + } + + @Ignore //TODO + @Test def groupByExample() { + import Olympics._ + // `: _*` converts list to varargs + val medals = Observable[Medal](Olympics.mountainBikeMedals : _*) + + // 1 second = 4 years :D + val medalsByYear = sampleAllUntilComplete(medals.groupBy(_.year), 1 seconds) + + /* + val t = (for ((year, medals) <- medalsByYear) yield medals).flatMap(ms => ms) + t.subscribe(println(_)) + */ + + val timedMedals = for ((year, medals) <- medalsByYear; medal <- medals) yield medal + + timedMedals.subscribe(println(_)) // doesn't print ??? + + Thread.sleep(5000) + + /* + medalsByYear.subscribe(p => println(p._1)) + + //waitFor(medalsByYear) + + val byCountry = medals.groupBy(_.country) + + def score(medals: Observable[Medal]) = medals.fold((0, 0, 0))((s, m) => (s, m.medal) match { + case ((gold, silver, bronze), "Gold") => (gold+1, silver, bronze) + case ((gold, silver, bronze), "Silver") => (gold, silver+1, bronze) + case ((gold, silver, bronze), "Bronze") => (gold, silver, bronze+1) + }) + + val scores = for ((country, medals) <- byCountry) yield (country, score(medals)) + + println(scores.toBlockingObservable.toList) + */ + } + def output(s: String): Unit = println(s) // blocks until obs has completed From e257ad39166632cdbafe8bb68c1d92475404b669 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 16 Sep 2013 17:49:29 +0800 Subject: [PATCH 009/333] Implemented the 'any' operator --- rxjava-core/src/main/java/rx/Observable.java | 33 +++ .../main/java/rx/operators/OperationAny.java | 232 ++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationAny.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index d5d8fb8297..42e154c190 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -77,6 +77,7 @@ import rx.operators.OperationZip; import rx.operators.SafeObservableSubscription; import rx.operators.SafeObserver; +import rx.operators.OperationAny; import rx.plugins.RxJavaErrorHandler; import rx.plugins.RxJavaObservableExecutionHook; import rx.plugins.RxJavaPlugins; @@ -4187,4 +4188,36 @@ private boolean isInternalImplementation(Object o) { return p != null && p.getName().startsWith("rx.operators"); } +// /** +// * Returns an {@link Observable} that emits true if the source +// * {@link Observable} is not empty, otherwise false. +// * +// * @param source +// * The source {@link Observable} to check if not empty. +// * @return A subscription function for creating the target Observable. +// * @see MSDN: Observable.Any +// */ +// public Observable any() { +// return create(OperationAny.any(this)); +// } +// +// /** +// * Returns an {@link Observable} that emits true if all items +// * of the source {@link Observable} satisfy the given condition, otherwise +// * false. +// * +// * @param predicate +// * The condition all items have to satisfy. +// * @return A subscription function for creating the target Observable. +// * +// * @see MSDN: Observable.Any +// */ +// public Observable any(Func1 predicate) { +// return create(OperationAny.any(this, predicate)); +// } + } diff --git a/rxjava-core/src/main/java/rx/operators/OperationAny.java b/rxjava-core/src/main/java/rx/operators/OperationAny.java new file mode 100644 index 0000000000..113815fc0f --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationAny.java @@ -0,0 +1,232 @@ +package rx.operators; + +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 static rx.util.functions.Functions.alwaysTrue; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.Test; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Func1; + +/** + * Returns an {@link Observable} that emits true if all items of an + * observable sequence satisfy a condition, otherwise false. + */ +public final class OperationAny { + + /** + * Returns an {@link Observable} that emits true if the source + * {@link Observable} is not empty, otherwise false. + * + * @param source + * The source {@link Observable} to check if not empty. + * @return A subscription function for creating the target Observable. + */ + public static OnSubscribeFunc any( + Observable source) { + return new Any(source, alwaysTrue()); + } + + /** + * Returns an {@link Observable} that emits true if all items + * of the source {@link Observable} satisfy the given condition, otherwise + * false. + * + * @param source + * The source {@link Observable} to check if all items in it + * satisfy the given condition + * @param predicate + * The condition all items have to satisfy. + * @return A subscription function for creating the target Observable. + */ + public static OnSubscribeFunc any( + Observable source, Func1 predicate) { + return new Any(source, predicate); + } + + private static class Any implements OnSubscribeFunc { + + private final Observable source; + private final Func1 predicate; + + private Any(Observable source, + Func1 predicate) { + this.source = source; + this.predicate = predicate; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + final SafeObservableSubscription subscription = new SafeObservableSubscription(); + return subscription.wrap(source.subscribe(new Observer() { + + private final AtomicBoolean hasEmitted = new AtomicBoolean( + false); + + private volatile boolean isNotEmpty = false; + + @Override + public void onNext(T value) { + isNotEmpty = true; + try { + if (hasEmitted.get() == false) { + if (predicate.call(value) == false + && hasEmitted.getAndSet(true) == false) { + observer.onNext(false); + observer.onCompleted(); + // this will work if the sequence is + // asynchronous, it + // will have no effect on a synchronous + // observable + subscription.unsubscribe(); + } + } + } catch (Throwable ex) { + observer.onError(ex); + // this will work if the sequence is asynchronous, it + // will have no effect on a synchronous observable + subscription.unsubscribe(); + } + + } + + @Override + public void onError(Throwable ex) { + observer.onError(ex); + } + + @Override + public void onCompleted() { + if (!hasEmitted.get()) { + observer.onNext(isNotEmpty); + observer.onCompleted(); + } + } + })); + } + + } + + public static class UnitTest { + + @Test + public void testAnyWithTwoItems() { + Observable w = Observable.from(1, 2); + Observable observable = Observable.create(any(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(false); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testAnyWithOneItem() { + Observable w = Observable.from(1); + Observable observable = Observable.create(any(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(false); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testAnyWithEmpty() { + Observable w = Observable.empty(); + Observable observable = Observable.create(any(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onNext(true); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testAnyWithPredicate1() { + Observable w = Observable.from(1, 2); + Observable observable = Observable.create(any(w, + new Func1() { + + @Override + public Boolean call(Integer t1) { + return t1 < 3; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(false); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testAnyWithPredicate2() { + Observable w = Observable.from(1, 2, 3); + Observable observable = Observable.create(any(w, + new Func1() { + + @Override + public Boolean call(Integer t1) { + return t1 < 3; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onNext(true); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testAnyWithEmptyAndPredicate() { + // If the source is empty, always output false. + Observable w = Observable.empty(); + Observable observable = Observable.create(any(w, + new Func1() { + + @Override + public Boolean call(Integer t1) { + return true; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onNext(true); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + } +} From ed030db911107a2c12bc0a791518656b3a72d13c Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Mon, 16 Sep 2013 13:19:20 +0200 Subject: [PATCH 010/333] update scala README --- language-adaptors/rxjava-scala/README.md | 67 +++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/language-adaptors/rxjava-scala/README.md b/language-adaptors/rxjava-scala/README.md index 470a65744e..c4ad66d0af 100644 --- a/language-adaptors/rxjava-scala/README.md +++ b/language-adaptors/rxjava-scala/README.md @@ -1,8 +1,71 @@ # Scala Adaptor for RxJava -There's an old Scala adaptor ( `rx.lang.scala.RxImplicits` with test `rx.lang.scala.RxImplicitsTest` ), which is deprecated. All other classes in `rx.lang.scala` belong to the new adaptor. +This adaptor allows to use RxJava in Scala with anonymous functions, e.g. -# Binaries +```scala +val o = Observable.interval(200 millis).take(5) +o.subscribe(n => println("n = " + n)) +Observable(1, 2, 3, 4).reduce(_ + _) +``` + +For-comprehensions are also supported: + +```scala +val first = Observable(10, 11, 12) +val second = Observable(10, 11, 12) +val booleans = for ((n1, n2) <- (first zip second)) yield (n1 == n2) +``` + +Further, this adaptor attempts to expose an API which is as Scala-idiomatic as possible. This means that certain methods have been renamed, their signature was changed, or static methods were changed to instance methods. Some examples: + +```scala + // instead of concat: +def ++[U >: T](that: Observable[U]): Observable[U] + +// instance method instead of static: +def zip[U](that: Observable[U]): Observable[(T, U)] + +// the implicit evidence argument ensures that dematerialize can only be called on Observables of Notifications: +def dematerialize[U](implicit evidence: T <:< Notification[U]): Observable[U] + +// additional type parameter U with lower bound to get covariance right: +def onErrorResumeNext[U >: T](resumeFunction: Throwable => Observable[U]): Observable[U] + +// curried in Scala collections, so curry fold also here: +def fold[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] + +// using Duration instead of (long timepan, TimeUnit duration): +def sample(duration: Duration): Observable[T] + +// called skip in Java, but drop in Scala +def drop(n: Int): Observable[T] + +// there's only mapWithIndex in Java, because Java doesn't have tuples: +def zipWithIndex: Observable[(T, Int)] + +// corresponds to Java's toList: +def toSeq: Observable[Seq[T]] + +// the implicit evidence argument ensures that switch can only be called on Observables of Observables: +def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] + +// Java's from becomes apply, and we use Scala Range +def apply(range: Range): Observable[Int] + +// use Bottom type: +def never: Observable[Nothing] +``` + +Also, the Scala Observable is fully covariant in its type parameter, whereas the Java Observable only achieves partial covariance due to limitations of Java's type system (or if you can fix this, your suggestions are very welcome). + +For more examples, see [RxScalaDemo.scala](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala). + +Scala code using Rx should only import members from `rx.lang.scala` and below. + +Work on this adaptor is still in progress, and for the moment, the best source of documentation are the comments in the source code of [`rx.lang.scala.Observable`](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala). + + +## Binaries Binaries and dependency information for Maven, Ivy, Gradle and others can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22rxjava-scala%22). From cb96d7d53ec52872b6794567e4e0c4764f9cc189 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Tue, 17 Sep 2013 09:28:43 +0800 Subject: [PATCH 011/333] Add 'any' operator to Observable --- rxjava-core/src/main/java/rx/Observable.java | 62 ++++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 42e154c190..59dbc61def 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4188,36 +4188,36 @@ private boolean isInternalImplementation(Object o) { return p != null && p.getName().startsWith("rx.operators"); } -// /** -// * Returns an {@link Observable} that emits true if the source -// * {@link Observable} is not empty, otherwise false. -// * -// * @param source -// * The source {@link Observable} to check if not empty. -// * @return A subscription function for creating the target Observable. -// * @see MSDN: Observable.Any -// */ -// public Observable any() { -// return create(OperationAny.any(this)); -// } -// -// /** -// * Returns an {@link Observable} that emits true if all items -// * of the source {@link Observable} satisfy the given condition, otherwise -// * false. -// * -// * @param predicate -// * The condition all items have to satisfy. -// * @return A subscription function for creating the target Observable. -// * -// * @see MSDN: Observable.Any -// */ -// public Observable any(Func1 predicate) { -// return create(OperationAny.any(this, predicate)); -// } + /** + * Returns an {@link Observable} that emits true if the source + * {@link Observable} is not empty, otherwise false. + * + * @param source + * The source {@link Observable} to check if not empty. + * @return A subscription function for creating the target Observable. + * @see MSDN: Observable.Any + */ + public Observable any() { + return create(OperationAny.any(this)); + } + + /** + * Returns an {@link Observable} that emits true if all items + * of the source {@link Observable} satisfy the given condition, otherwise + * false. + * + * @param predicate + * The condition all items have to satisfy. + * @return A subscription function for creating the target Observable. + * + * @see MSDN: Observable.Any + */ + public Observable any(Func1 predicate) { + return create(OperationAny.any(this, predicate)); + } } From d70e1d69623d317badffedad51cc767e32438a8a Mon Sep 17 00:00:00 2001 From: zsxwing Date: Tue, 17 Sep 2013 09:29:26 +0800 Subject: [PATCH 012/333] Solve the conflict of 'any' method name in unit tests --- .../src/main/java/rx/subjects/AsyncSubject.java | 4 ++-- .../src/main/java/rx/subjects/BehaviorSubject.java | 3 +-- .../src/main/java/rx/subjects/PublishSubject.java | 11 +++++------ .../src/main/java/rx/subjects/ReplaySubject.java | 5 ++--- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java index 4bd6e11106..ee14a6e166 100644 --- a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java @@ -168,7 +168,7 @@ public void testCompleted() { private void assertCompletedObserver(Observer aObserver) { verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } @@ -221,7 +221,7 @@ public void testUnsubscribeBeforeCompleted() { private void assertNoOnNextEventsReceived(Observer aObserver) { verify(aObserver, Mockito.never()).onNext(anyString()); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, Mockito.never()).onCompleted(); } diff --git a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java index 4a3729c24e..6fa45cd6c7 100644 --- a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java @@ -15,7 +15,6 @@ */ package rx.subjects; -import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.util.concurrent.ConcurrentHashMap; @@ -199,7 +198,7 @@ private void assertCompletedObserver(Observer aObserver) { verify(aObserver, times(1)).onNext("default"); verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } diff --git a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java index c196d83cdd..dab76b5ec5 100644 --- a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java @@ -16,7 +16,6 @@ package rx.subjects; import static org.junit.Assert.*; -import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.util.ArrayList; @@ -273,7 +272,7 @@ private void assertCompletedObserver(Observer aObserver) verify(aObserver, times(1)).onNext("one"); verify(aObserver, times(1)).onNext("two"); verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } @@ -340,7 +339,7 @@ private void assertCompletedStartingWithThreeObserver(Observer aObserver verify(aObserver, Mockito.never()).onNext("one"); verify(aObserver, Mockito.never()).onNext("two"); verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } @@ -374,7 +373,7 @@ private void assertObservedUntilTwo(Observer aObserver) verify(aObserver, times(1)).onNext("one"); verify(aObserver, times(1)).onNext("two"); verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, Mockito.never()).onCompleted(); } @@ -404,7 +403,7 @@ public void testUnsubscribeAfterOnCompleted() { inOrder.verify(anObserver, times(1)).onNext("one"); inOrder.verify(anObserver, times(1)).onNext("two"); inOrder.verify(anObserver, times(1)).onCompleted(); - inOrder.verify(anObserver, Mockito.never()).onError(any(Throwable.class)); + inOrder.verify(anObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); @SuppressWarnings("unchecked") Observer anotherObserver = mock(Observer.class); @@ -414,7 +413,7 @@ public void testUnsubscribeAfterOnCompleted() { inOrder.verify(anotherObserver, Mockito.never()).onNext("one"); inOrder.verify(anotherObserver, Mockito.never()).onNext("two"); inOrder.verify(anotherObserver, times(1)).onCompleted(); - inOrder.verify(anotherObserver, Mockito.never()).onError(any(Throwable.class)); + inOrder.verify(anotherObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); } @Test diff --git a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java index 51ac519357..28fbceccdf 100644 --- a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java +++ b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java @@ -15,7 +15,6 @@ */ package rx.subjects; -import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.util.ArrayList; @@ -215,7 +214,7 @@ private void assertCompletedObserver(Observer aObserver) inOrder.verify(aObserver, times(1)).onNext("one"); inOrder.verify(aObserver, times(1)).onNext("two"); inOrder.verify(aObserver, times(1)).onNext("three"); - inOrder.verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); inOrder.verify(aObserver, times(1)).onCompleted(); inOrder.verifyNoMoreInteractions(); } @@ -307,7 +306,7 @@ private void assertObservedUntilTwo(Observer aObserver) verify(aObserver, times(1)).onNext("one"); verify(aObserver, times(1)).onNext("two"); verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, Mockito.never()).onCompleted(); } From c8f11997c9b88ec97e55e0108a5e94b15580716d Mon Sep 17 00:00:00 2001 From: zsxwing Date: Tue, 17 Sep 2013 10:31:33 +0800 Subject: [PATCH 013/333] Follow the correct logic of 'any' operator. See #385 --- rxjava-core/src/main/java/rx/Observable.java | 15 ++++----- .../main/java/rx/operators/OperationAny.java | 32 +++++++++---------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 59dbc61def..23166ef074 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4192,8 +4192,6 @@ private boolean isInternalImplementation(Object o) { * Returns an {@link Observable} that emits true if the source * {@link Observable} is not empty, otherwise false. * - * @param source - * The source {@link Observable} to check if not empty. * @return A subscription function for creating the target Observable. * @see any() { } /** - * Returns an {@link Observable} that emits true if all items - * of the source {@link Observable} satisfy the given condition, otherwise - * false. + * Returns an {@link Observable} that emits true if any element + * of the source {@link Observable} satisfies the given condition, otherwise + * false. Note: always emit false if the source + * {@link Observable} is empty. * * @param predicate - * The condition all items have to satisfy. + * The condition to test every element. * @return A subscription function for creating the target Observable. - * * @see MSDN: Observable.Any + * >MSDN: Observable.Any Note: the description in this page is + * wrong. */ public Observable any(Func1 predicate) { return create(OperationAny.any(this, predicate)); diff --git a/rxjava-core/src/main/java/rx/operators/OperationAny.java b/rxjava-core/src/main/java/rx/operators/OperationAny.java index 113815fc0f..5423af2f8d 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAny.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAny.java @@ -17,8 +17,8 @@ import rx.util.functions.Func1; /** - * Returns an {@link Observable} that emits true if all items of an - * observable sequence satisfy a condition, otherwise false. + * Returns an {@link Observable} that emits true if any element of + * an observable sequence satisfies a condition, otherwise false. */ public final class OperationAny { @@ -36,15 +36,16 @@ public static OnSubscribeFunc any( } /** - * Returns an {@link Observable} that emits true if all items - * of the source {@link Observable} satisfy the given condition, otherwise - * false. + * Returns an {@link Observable} that emits true if any element + * of the source {@link Observable} satisfies the given condition, otherwise + * false. Note: always emit false if the source + * {@link Observable} is empty. * * @param source - * The source {@link Observable} to check if all items in it - * satisfy the given condition + * The source {@link Observable} to check if any element + * satisfies the given condition. * @param predicate - * The condition all items have to satisfy. + * The condition to test every element. * @return A subscription function for creating the target Observable. */ public static OnSubscribeFunc any( @@ -71,16 +72,13 @@ public Subscription onSubscribe(final Observer observer) { private final AtomicBoolean hasEmitted = new AtomicBoolean( false); - private volatile boolean isNotEmpty = false; - @Override public void onNext(T value) { - isNotEmpty = true; try { if (hasEmitted.get() == false) { - if (predicate.call(value) == false + if (predicate.call(value) == true && hasEmitted.getAndSet(true) == false) { - observer.onNext(false); + observer.onNext(true); observer.onCompleted(); // this will work if the sequence is // asynchronous, it @@ -106,7 +104,7 @@ public void onError(Throwable ex) { @Override public void onCompleted() { if (!hasEmitted.get()) { - observer.onNext(isNotEmpty); + observer.onNext(false); observer.onCompleted(); } } @@ -164,13 +162,13 @@ public void testAnyWithEmpty() { @Test public void testAnyWithPredicate1() { - Observable w = Observable.from(1, 2); + Observable w = Observable.from(1, 2, 3); Observable observable = Observable.create(any(w, new Func1() { @Override public Boolean call(Integer t1) { - return t1 < 3; + return t1 < 2; } })); @@ -192,7 +190,7 @@ public void testAnyWithPredicate2() { @Override public Boolean call(Integer t1) { - return t1 < 3; + return t1 < 1; } })); From 6e7772dc9118dafa58ee0233452e27ba88ef3f17 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 17 Sep 2013 11:49:17 +0200 Subject: [PATCH 014/333] make Olympics example data timed --- .../rx/lang/scala/examples/Olympics.scala | 70 ++++++++++++------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala index f2180cf402..66d74a9321 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala @@ -1,39 +1,55 @@ package rx.lang.scala.examples +import rx.lang.scala.Observable +import scala.concurrent.duration._ + object Olympics { case class Medal(val year: Int, val games: String, val discipline: String, val medal: String, val athlete: String, val country: String) - val mountainBikeMedals = List( - Medal(2012, "London 2012", "cross-country men", "Gold", "Jaroslav KULHAVY", "Czech Republic"), - Medal(2012, "London 2012", "cross-country men", "Silver", "Nino SCHURTER", "Switzerland"), - Medal(2012, "London 2012", "cross-country men", "Bronze", "Marco Aurelio FONTANA", "Italy"), - Medal(2012, "London 2012", "cross-country women", "Gold", "Julie BRESSET", "France"), - Medal(2012, "London 2012", "cross-country women", "Silver", "Sabine SPITZ", "Germany"), - Medal(2012, "London 2012", "cross-country women", "Bronze", "Georgia GOULD", "United States of America"), - Medal(2008, "Beijing 2008", "cross-country women", "Gold", "Sabine SPITZ", "Germany"), - Medal(2008, "Beijing 2008", "cross-country women", "Silver", "Maja WLOSZCZOWSKA", "Poland"), - Medal(2008, "Beijing 2008", "cross-country women", "Bronze", "Irina KALENTYEVA", "Russian Federation"), - Medal(2008, "Beijing 2008", "cross-country men", "Gold", "Julien ABSALON", "France"), - Medal(2008, "Beijing 2008", "cross-country men", "Silver", "Jean-Christophe PERAUD", "France"), - Medal(2008, "Beijing 2008", "cross-country men", "Bronze", "Nino SCHURTER", "Switzerland"), - Medal(2004, "Athens 2004", "cross-country men", "Gold", "Julien ABSALON", "France"), - Medal(2004, "Athens 2004", "cross-country men", "Silver", "Jose Antonio HERMIDA RAMOS", "Spain"), - Medal(2004, "Athens 2004", "cross-country men", "Bronze", "Bart BRENTJENS", "Netherlands"), - Medal(2004, "Athens 2004", "cross-country women", "Gold", "Gunn-Rita DAHLE", "Norway"), - Medal(2004, "Athens 2004", "cross-country women", "Silver", "Marie-Helene PREMONT", "Canada"), - Medal(2004, "Athens 2004", "cross-country women", "Bronze", "Sabine SPITZ", "Germany"), + def mountainBikeMedals: Observable[Medal] = Observable( + Medal(1996, "Atlanta 1996", "cross-country men", "Gold", "Bart BRENTJENS", "Netherlands"), + Medal(1996, "Atlanta 1996", "cross-country women", "Gold", "Paola PEZZO", "Italy"), + Medal(1996, "Atlanta 1996", "cross-country men", "Silver", "Thomas FRISCHKNECHT", "Switzerland"), + Medal(1996, "Atlanta 1996", "cross-country women", "Silver", "Alison SYDOR", "Canada"), + Medal(1996, "Atlanta 1996", "cross-country men", "Bronze", "Miguel MARTINEZ", "France"), + Medal(1996, "Atlanta 1996", "cross-country women", "Bronze", "Susan DEMATTEI", "United States of America") + ) ++ fourYearsEmpty ++ Observable( Medal(2000, "Sydney 2000", "cross-country women", "Gold", "Paola PEZZO", "Italy"), Medal(2000, "Sydney 2000", "cross-country women", "Silver", "Barbara BLATTER", "Switzerland"), Medal(2000, "Sydney 2000", "cross-country women", "Bronze", "Marga FULLANA", "Spain"), Medal(2000, "Sydney 2000", "cross-country men", "Gold", "Miguel MARTINEZ", "France"), Medal(2000, "Sydney 2000", "cross-country men", "Silver", "Filip MEIRHAEGHE", "Belgium"), - Medal(2000, "Sydney 2000", "cross-country men", "Bronze", "Christoph SAUSER", "Switzerland"), - Medal(1996, "Atlanta 1996", "cross-country men", "Silver", "Thomas FRISCHKNECHT", "Switzerland"), - Medal(1996, "Atlanta 1996", "cross-country men", "Bronze", "Miguel MARTINEZ", "France"), - Medal(1996, "Atlanta 1996", "cross-country men", "Gold", "Bart BRENTJENS", "Netherlands"), - Medal(1996, "Atlanta 1996", "cross-country women", "Gold", "Paola PEZZO", "Italy"), - Medal(1996, "Atlanta 1996", "cross-country women", "Silver", "Alison SYDOR", "Canada"), - Medal(1996, "Atlanta 1996", "cross-country women", "Bronze", "Susan DEMATTEI", "United States of America") - ).reverse + Medal(2000, "Sydney 2000", "cross-country men", "Bronze", "Christoph SAUSER", "Switzerland") + ) ++ fourYearsEmpty ++ Observable( + Medal(2004, "Athens 2004", "cross-country men", "Gold", "Julien ABSALON", "France"), + Medal(2004, "Athens 2004", "cross-country men", "Silver", "Jose Antonio HERMIDA RAMOS", "Spain"), + Medal(2004, "Athens 2004", "cross-country men", "Bronze", "Bart BRENTJENS", "Netherlands"), + Medal(2004, "Athens 2004", "cross-country women", "Gold", "Gunn-Rita DAHLE", "Norway"), + Medal(2004, "Athens 2004", "cross-country women", "Silver", "Marie-Helene PREMONT", "Canada"), + Medal(2004, "Athens 2004", "cross-country women", "Bronze", "Sabine SPITZ", "Germany") + ) ++ fourYearsEmpty ++ Observable( + Medal(2008, "Beijing 2008", "cross-country women", "Gold", "Sabine SPITZ", "Germany"), + Medal(2008, "Beijing 2008", "cross-country women", "Silver", "Maja WLOSZCZOWSKA", "Poland"), + Medal(2008, "Beijing 2008", "cross-country women", "Bronze", "Irina KALENTYEVA", "Russian Federation"), + Medal(2008, "Beijing 2008", "cross-country men", "Gold", "Julien ABSALON", "France"), + Medal(2008, "Beijing 2008", "cross-country men", "Silver", "Jean-Christophe PERAUD", "France"), + Medal(2008, "Beijing 2008", "cross-country men", "Bronze", "Nino SCHURTER", "Switzerland") + ) ++ fourYearsEmpty ++ Observable( + Medal(2012, "London 2012", "cross-country men", "Gold", "Jaroslav KULHAVY", "Czech Republic"), + Medal(2012, "London 2012", "cross-country men", "Silver", "Nino SCHURTER", "Switzerland"), + Medal(2012, "London 2012", "cross-country men", "Bronze", "Marco Aurelio FONTANA", "Italy"), + Medal(2012, "London 2012", "cross-country women", "Gold", "Julie BRESSET", "France"), + Medal(2012, "London 2012", "cross-country women", "Silver", "Sabine SPITZ", "Germany"), + Medal(2012, "London 2012", "cross-country women", "Bronze", "Georgia GOULD", "United States of America") + ) + + // speed it up :D + val fourYears = 4000.millis + + val neverUsedDummyMedal = Medal(3333, "?", "?", "?", "?", "?") + + def fourYearsEmpty: Observable[Medal] = { + Observable.interval(fourYears).take(1).map(i => neverUsedDummyMedal).filter(m => false) + } } \ No newline at end of file From ff2b3828ba0cd721888c8d5c7856b8a767a78895 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 17 Sep 2013 11:54:31 +0200 Subject: [PATCH 015/333] more examples/tests in RxScalaDemo --- .../rx/lang/scala/examples/RxScalaDemo.scala | 76 ++++++++++--------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index 461c280f4c..13880ce540 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -158,47 +158,53 @@ class RxScalaDemo extends JUnitSuite { waitFor(o) } - def sampleAllUntilComplete[T](o: Observable[T], period: Duration): Observable[T] = { - for ((element, tick) <- o zip Observable.interval(period)) yield element + @Test def testGroupByThenFlatMap() { + val m = Observable(1, 2, 3, 4) + val g = m.groupBy(i => i % 2) + val t = g.flatMap((p: (Int, Observable[Int])) => p._2) + assertEquals(List(1, 2, 3, 4), t.toBlockingObservable.toList) } - @Ignore //TODO - @Test def groupByExample() { - import Olympics._ - // `: _*` converts list to varargs - val medals = Observable[Medal](Olympics.mountainBikeMedals : _*) - - // 1 second = 4 years :D - val medalsByYear = sampleAllUntilComplete(medals.groupBy(_.year), 1 seconds) - - /* - val t = (for ((year, medals) <- medalsByYear) yield medals).flatMap(ms => ms) - t.subscribe(println(_)) - */ - - val timedMedals = for ((year, medals) <- medalsByYear; medal <- medals) yield medal - - timedMedals.subscribe(println(_)) // doesn't print ??? - - Thread.sleep(5000) - - /* - medalsByYear.subscribe(p => println(p._1)) - - //waitFor(medalsByYear) + @Test def testGroupByThenFlatMapByForComprehension() { + val m = Observable(1, 2, 3, 4) + val g = m.groupBy(i => i % 2) + val t = for ((i, o) <- g; n <- o) yield n + assertEquals(List(1, 2, 3, 4), t.toBlockingObservable.toList) + } + + @Test def testGroupByThenFlatMapByForComprehensionWithTiming() { + val m = Observable.interval(100 millis).take(4) + val g = m.groupBy(i => i % 2) + val t = for ((i, o) <- g; n <- o) yield n + assertEquals(List(0, 1, 2, 3), t.toBlockingObservable.toList) + } + + @Test def groupByExampleTest() { + val medalsByCountry = Olympics.mountainBikeMedals.groupBy(medal => medal.country) - val byCountry = medals.groupBy(_.country) + val firstMedalOfEachCountry = + medalsByCountry.flatMap((p: (String, Observable[Olympics.Medal])) => p._2.take(1)) + + firstMedalOfEachCountry.subscribe(medal => { + println(s"${medal.country} wins its first medal in ${medal.year}") + }) - def score(medals: Observable[Medal]) = medals.fold((0, 0, 0))((s, m) => (s, m.medal) match { - case ((gold, silver, bronze), "Gold") => (gold+1, silver, bronze) - case ((gold, silver, bronze), "Silver") => (gold, silver+1, bronze) - case ((gold, silver, bronze), "Bronze") => (gold, silver, bronze+1) - }) + //waitFor(firstMedalOfEachCountry) + Thread.sleep(20000) + } + + @Ignore // TODO this test one does not terminate! + @Test def groupByExample() { + val medalsByCountry = Olympics.mountainBikeMedals.groupBy(medal => medal.country) - val scores = for ((country, medals) <- byCountry) yield (country, score(medals)) + val firstMedalOfEachCountry = + for ((country, medals) <- medalsByCountry; firstMedal <- medals.take(1)) yield firstMedal + + firstMedalOfEachCountry.subscribe(medal => { + println(s"${medal.country} wins its first medal in ${medal.year}") + }) - println(scores.toBlockingObservable.toList) - */ + waitFor(firstMedalOfEachCountry) } def output(s: String): Unit = println(s) From 92f6bc1f67a829c7147863aff1134ec000fbfffa Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 17 Sep 2013 13:03:33 +0200 Subject: [PATCH 016/333] finish groupBy example --- .../rx/lang/scala/examples/Olympics.scala | 7 ++++++- .../rx/lang/scala/examples/RxScalaDemo.scala | 18 +----------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala index 66d74a9321..d826aa58e8 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala @@ -49,7 +49,12 @@ object Olympics { val neverUsedDummyMedal = Medal(3333, "?", "?", "?", "?", "?") def fourYearsEmpty: Observable[Medal] = { - Observable.interval(fourYears).take(1).map(i => neverUsedDummyMedal).filter(m => false) + // TODO this should return an observable which emits nothing during fourYears and then completes + // Because of https://github.com/Netflix/RxJava/issues/388, we get non-terminating tests + // So we don't use this: + // Observable.interval(fourYears).take(1).map(i => neverUsedDummyMedal).filter(m => false) + // But we just return empty, which completes immediately + Observable() } } \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index 13880ce540..bde8d63555 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -136,7 +136,6 @@ class RxScalaDemo extends JUnitSuite { } @Test def testTwoSubscriptionsToOneInterval() { - // TODO this does not yet work as expected! val o = Observable.interval(100 millis).take(8) o.subscribe( i => println(s"${i}a (on thread #${Thread.currentThread().getId()})") @@ -178,22 +177,7 @@ class RxScalaDemo extends JUnitSuite { val t = for ((i, o) <- g; n <- o) yield n assertEquals(List(0, 1, 2, 3), t.toBlockingObservable.toList) } - - @Test def groupByExampleTest() { - val medalsByCountry = Olympics.mountainBikeMedals.groupBy(medal => medal.country) - - val firstMedalOfEachCountry = - medalsByCountry.flatMap((p: (String, Observable[Olympics.Medal])) => p._2.take(1)) - - firstMedalOfEachCountry.subscribe(medal => { - println(s"${medal.country} wins its first medal in ${medal.year}") - }) - - //waitFor(firstMedalOfEachCountry) - Thread.sleep(20000) - } - - @Ignore // TODO this test one does not terminate! + @Test def groupByExample() { val medalsByCountry = Olympics.mountainBikeMedals.groupBy(medal => medal.country) From 04f69b256c5eed2f7145293d45818f71a758dcbe Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 17 Sep 2013 13:11:39 +0200 Subject: [PATCH 017/333] move ImplicitFunctionConversions out of internal package --- .../scala/{internal => }/ImplicitFunctionConversions.scala | 6 +----- .../src/main/scala/rx/lang/scala/Observable.scala | 6 +++--- .../rx/lang/scala/observables/BlockingObservable.scala | 2 +- .../rx/lang/scala/observables/ConnectableObservable.scala | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) rename language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/{internal => }/ImplicitFunctionConversions.scala (97%) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/internal/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala similarity index 97% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/internal/ImplicitFunctionConversions.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index e5f6e49fc6..de0268b70b 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/internal/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package rx.lang.scala.internal +package rx.lang.scala import java.{lang => jlang} @@ -25,10 +25,6 @@ import rx.util.functions.Func2 import rx.util.functions.Func3 import rx.util.functions.Func4 import java.{lang => jlang} -import rx.Observer -import rx.Subscription -import java.{lang => jlang} -import scala.language.implicitConversions /** * These function conversions are only used by the ScalaAdapter, users of RxScala don't need them. diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 550f042c4b..6341285a60 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -38,7 +38,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) import rx.util.functions._ import rx.lang.scala.{Notification, Subscription, Scheduler, Observer} import rx.lang.scala.util._ - import rx.lang.scala.internal.ImplicitFunctionConversions._ + import rx.lang.scala.ImplicitFunctionConversions._ /** * An {@link Observer} must call an Observable's {@code subscribe} method in order to @@ -1456,7 +1456,7 @@ object Observable { import rx.{Observable => JObservable} import rx.lang.scala.{Notification, Subscription, Scheduler, Observer} import rx.lang.scala.util._ - import rx.lang.scala.internal.ImplicitFunctionConversions._ + import rx.lang.scala.ImplicitFunctionConversions._ private[scala] def jObsOfListToScObsOfSeq[T](jObs: rx.Observable[_ <: java.util.List[T]]): Observable[Seq[T]] = { @@ -1774,7 +1774,7 @@ object Observable { // "implementation restriction: nested class is not allowed in value class. // This restriction is planned to be removed in subsequent releases." class WithFilter[+T] private[scala] (p: T => Boolean, asJava: rx.Observable[_ <: T]) { - import rx.lang.scala.internal.ImplicitFunctionConversions._ + import rx.lang.scala.ImplicitFunctionConversions._ def map[B](f: T => B): Observable[B] = { Observable[B](asJava.filter(p).map[B](f)) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala index 3b3562d652..8227caf85e 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala @@ -16,7 +16,7 @@ package rx.lang.scala.observables import scala.collection.JavaConverters._ -import rx.lang.scala.internal.ImplicitFunctionConversions._ +import rx.lang.scala.ImplicitFunctionConversions._ class BlockingObservable[+T](val asJava: rx.observables.BlockingObservable[_ <: T]) extends AnyVal diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/ConnectableObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/ConnectableObservable.scala index 7740ad043f..8530517484 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/ConnectableObservable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/ConnectableObservable.scala @@ -20,6 +20,6 @@ class ConnectableObservable[+T](val asJava: rx.observables.ConnectableObservable import rx.lang.scala._ import rx.lang.scala.util._ import rx.{Observable => JObservable} - import rx.lang.scala.internal.ImplicitFunctionConversions._ + import rx.lang.scala.ImplicitFunctionConversions._ } \ No newline at end of file From 41c91fbd136b612fa5bfc018c542871acafbec14 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 17 Sep 2013 13:24:17 +0200 Subject: [PATCH 018/333] add comment to ImplicitFunctionConversions and reformat it to use indent of 2 spaces, as all other files in rx.lang.scala --- .../scala/ImplicitFunctionConversions.scala | 244 ++++++++---------- 1 file changed, 111 insertions(+), 133 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index de0268b70b..b7f13b0783 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -15,137 +15,115 @@ */ package rx.lang.scala - -import java.{lang => jlang} -import rx.util.functions.Action0 -import rx.util.functions.Action1 -import rx.util.functions.Func0 -import rx.util.functions.Func1 -import rx.util.functions.Func2 -import rx.util.functions.Func3 -import rx.util.functions.Func4 -import java.{lang => jlang} +import java.{ lang => jlang } +import rx.util.functions._ /** - * These function conversions are only used by the ScalaAdapter, users of RxScala don't need them. + * These function conversions convert between Scala functions and Rx Funcs and Actions. + * Most users RxScala won't need them, but they might be useful if one wants to use + * the rx.Observable directly instead of using rx.lang.scala.Observable or if one wants + * to use a Java library taking/returning Funcs and Actions. */ object ImplicitFunctionConversions { - // code below is copied from - // https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/RxImplicits.scala - - import java.{ lang => jlang } - import language.implicitConversions - - import rx.observables.BlockingObservable - import rx.util.functions._ - import rx.{Observer, Subscription} - - implicit def scalaFunction1ToOnSubscribeFunc[T](f: rx.lang.scala.Observer[T] => Subscription) = - new rx.Observable.OnSubscribeFunc[T] { - def onSubscribe(obs: Observer[_ >: T]): Subscription = { - f(obs) - } - } - - /*implicit def scalaFunction1ToOnSubscribeFunc[T](f: Observer[_ >: T] => Subscription) = - new rx.Observable.OnSubscribeFunc[T] { - def onSubscribe(obs: Observer[_ >: T]): Subscription = { - f(obs) - } - }*/ - - /** - * Converts a by-name parameter to a Rx Func0 - */ - implicit def scalaByNameParamToFunc0[B](param: => B): Func0[B] = - new Func0[B]{ - def call(): B = param - } - - - /** - * Converts 0-arg function to Rx Action0 - */ - implicit def scalaFunction0ProducingUnitToAction0(f: (() => Unit)): Action0 = - new Action0 { - def call(): Unit = f() - } - - /** - * Converts 1-arg function to Rx Action1 - */ - implicit def scalaFunction1ProducingUnitToAction1[A](f: (A => Unit)): Action1[A] = - new Action1[A] { - def call(a: A): Unit = f(a) - } - - /** - * Converts 1-arg predicate to Rx Func1[A, java.lang.Boolean] - */ - implicit def scalaBooleanFunction1ToRxBooleanFunc1[A](f: (A => Boolean)): Func1[A, jlang.Boolean] = - new Func1[A, jlang.Boolean] { - def call(a: A): jlang.Boolean = f(a).booleanValue - } - - /** - * Converts 2-arg predicate to Rx Func2[A, B, java.lang.Boolean] - */ - implicit def scalaBooleanFunction2ToRxBooleanFunc1[A, B](f: ((A, B) => Boolean)): Func2[A, B, jlang.Boolean] = - new Func2[A, B, jlang.Boolean] { - def call(a: A, b: B): jlang.Boolean = f(a, b).booleanValue - } - - /** - * Converts a specific function shape (used in takeWhile) to the equivalent Java types with an Rx Func2 - */ - implicit def convertTakeWhileFuncToRxFunc2[A](f: (A, Int) => Boolean): Func2[A, jlang.Integer, jlang.Boolean] = - new Func2[A, jlang.Integer, jlang.Boolean] { - def call(a: A, b: jlang.Integer): jlang.Boolean = f(a, b).booleanValue - } - - /** - * Converts a function shaped ilke compareTo into the equivalent Rx Func2 - */ - implicit def convertComparisonFuncToRxFunc2[A](f: (A, A) => Int): Func2[A, A, jlang.Integer] = - new Func2[A, A, jlang.Integer] { - def call(a1: A, a2: A): jlang.Integer = f(a1, a2).intValue - } - - /* - * This implicit allows Scala code to use any exception type and still work - * with invariant Func1 interface - */ - implicit def exceptionFunction1ToRxExceptionFunc1[A <: Exception, B](f: (A => B)): Func1[Exception, B] = - new Func1[Exception, B] { - def call(ex: Exception): B = f(ex.asInstanceOf[A]) - } - - /** - * The following implicits convert functions of different arities into the Rx equivalents - */ - implicit def scalaFunction0ToRxFunc0[A](f: () => A): Func0[A] = - new Func0[A] { - def call(): A = f() - } - - implicit def scalaFunction1ToRxFunc1[A, B](f: (A => B)): Func1[A, B] = - new Func1[A, B] { - def call(a: A): B = f(a) - } - - implicit def scalaFunction2ToRxFunc2[A, B, C](f: (A, B) => C): Func2[A, B, C] = - new Func2[A, B, C] { - def call(a: A, b: B) = f(a, b) - } - - implicit def scalaFunction3ToRxFunc3[A, B, C, D](f: (A, B, C) => D): Func3[A, B, C, D] = - new Func3[A, B, C, D] { - def call(a: A, b: B, c: C) = f(a, b, c) - } - - implicit def scalaFunction4ToRxFunc4[A, B, C, D, E](f: (A, B, C, D) => E): Func4[A, B, C, D, E] = - new Func4[A, B, C, D, E] { - def call(a: A, b: B, c: C, d: D) = f(a, b, c, d) - } - -} \ No newline at end of file + import language.implicitConversions + + implicit def scalaFunction1ToOnSubscribeFunc[T](f: rx.lang.scala.Observer[T] => Subscription) = + new rx.Observable.OnSubscribeFunc[T] { + def onSubscribe(obs: Observer[_ >: T]): Subscription = { + f(obs) + } + } + + /** + * Converts a by-name parameter to a Rx Func0 + */ + implicit def scalaByNameParamToFunc0[B](param: => B): Func0[B] = + new Func0[B] { + def call(): B = param + } + + /** + * Converts 0-arg function to Rx Action0 + */ + implicit def scalaFunction0ProducingUnitToAction0(f: (() => Unit)): Action0 = + new Action0 { + def call(): Unit = f() + } + + /** + * Converts 1-arg function to Rx Action1 + */ + implicit def scalaFunction1ProducingUnitToAction1[A](f: (A => Unit)): Action1[A] = + new Action1[A] { + def call(a: A): Unit = f(a) + } + + /** + * Converts 1-arg predicate to Rx Func1[A, java.lang.Boolean] + */ + implicit def scalaBooleanFunction1ToRxBooleanFunc1[A](f: (A => Boolean)): Func1[A, jlang.Boolean] = + new Func1[A, jlang.Boolean] { + def call(a: A): jlang.Boolean = f(a).booleanValue + } + + /** + * Converts 2-arg predicate to Rx Func2[A, B, java.lang.Boolean] + */ + implicit def scalaBooleanFunction2ToRxBooleanFunc1[A, B](f: ((A, B) => Boolean)): Func2[A, B, jlang.Boolean] = + new Func2[A, B, jlang.Boolean] { + def call(a: A, b: B): jlang.Boolean = f(a, b).booleanValue + } + + /** + * Converts a specific function shape (used in takeWhile) to the equivalent Java types with an Rx Func2 + */ + implicit def convertTakeWhileFuncToRxFunc2[A](f: (A, Int) => Boolean): Func2[A, jlang.Integer, jlang.Boolean] = + new Func2[A, jlang.Integer, jlang.Boolean] { + def call(a: A, b: jlang.Integer): jlang.Boolean = f(a, b).booleanValue + } + + /** + * Converts a function shaped ilke compareTo into the equivalent Rx Func2 + */ + implicit def convertComparisonFuncToRxFunc2[A](f: (A, A) => Int): Func2[A, A, jlang.Integer] = + new Func2[A, A, jlang.Integer] { + def call(a1: A, a2: A): jlang.Integer = f(a1, a2).intValue + } + + /** + * This implicit allows Scala code to use any exception type and still work + * with invariant Func1 interface + */ + implicit def exceptionFunction1ToRxExceptionFunc1[A <: Exception, B](f: (A => B)): Func1[Exception, B] = + new Func1[Exception, B] { + def call(ex: Exception): B = f(ex.asInstanceOf[A]) + } + + /** + * The following implicits convert functions of different arities into the Rx equivalents + */ + implicit def scalaFunction0ToRxFunc0[A](f: () => A): Func0[A] = + new Func0[A] { + def call(): A = f() + } + + implicit def scalaFunction1ToRxFunc1[A, B](f: (A => B)): Func1[A, B] = + new Func1[A, B] { + def call(a: A): B = f(a) + } + + implicit def scalaFunction2ToRxFunc2[A, B, C](f: (A, B) => C): Func2[A, B, C] = + new Func2[A, B, C] { + def call(a: A, b: B) = f(a, b) + } + + implicit def scalaFunction3ToRxFunc3[A, B, C, D](f: (A, B, C) => D): Func3[A, B, C, D] = + new Func3[A, B, C, D] { + def call(a: A, b: B, c: C) = f(a, b, c) + } + + implicit def scalaFunction4ToRxFunc4[A, B, C, D, E](f: (A, B, C, D) => E): Func4[A, B, C, D, E] = + new Func4[A, B, C, D, E] { + def call(a: A, b: B, c: C, d: D) = f(a, b, c, d) + } +} From ac26e42d1e85deac0b7bfa50c3ca3e5298493dd4 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 17 Sep 2013 14:44:25 +0200 Subject: [PATCH 019/333] use Java Subject as contravariant in T and covariant in R --- rxjava-core/src/main/java/rx/Observable.java | 2 +- .../src/main/java/rx/operators/OperationMulticast.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index d5d8fb8297..a14e78329c 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -397,7 +397,7 @@ public Subscription subscribe(final Action1 onNext, final Action1 ConnectableObservable multicast(Subject subject) { + public ConnectableObservable multicast(Subject subject) { return OperationMulticast.multicast(this, subject); } diff --git a/rxjava-core/src/main/java/rx/operators/OperationMulticast.java b/rxjava-core/src/main/java/rx/operators/OperationMulticast.java index e83cfdaa03..e24c24a91a 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMulticast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMulticast.java @@ -27,7 +27,7 @@ import rx.subjects.Subject; public class OperationMulticast { - public static ConnectableObservable multicast(Observable source, final Subject subject) { + public static ConnectableObservable multicast(Observable source, final Subject subject) { return new MulticastConnectableObservable(source, subject); } @@ -35,11 +35,11 @@ private static class MulticastConnectableObservable extends ConnectableObs private final Object lock = new Object(); private final Observable source; - private final Subject subject; + private final Subject subject; private Subscription subscription; - public MulticastConnectableObservable(Observable source, final Subject subject) { + public MulticastConnectableObservable(Observable source, final Subject subject) { super(new OnSubscribeFunc() { @Override public Subscription onSubscribe(Observer observer) { From 5efe114f8035a5617ed24acab28b63e64779c014 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 17 Sep 2013 15:16:47 +0200 Subject: [PATCH 020/333] add multicast, publish, replay --- .../main/scala/rx/lang/scala/Observable.scala | 30 ++++++++------- .../rx/lang/scala/examples/RxScalaDemo.scala | 38 +++++++++++++++++++ .../observables/ConnectableObservable.scala | 25 ------------ .../main/scala/rx/lang/scala/package.scala | 6 --- .../rx/lang/scala/subjects/package.scala | 14 +++++++ 5 files changed, 69 insertions(+), 44 deletions(-) delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/ConnectableObservable.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 6341285a60..3462c3316a 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -20,7 +20,6 @@ package rx.lang.scala import org.scalatest.junit.JUnitSuite import scala.collection.Seq import rx.lang.scala.observables.BlockingObservable -import rx.lang.scala.observables.ConnectableObservable /** @@ -38,6 +37,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) import rx.util.functions._ import rx.lang.scala.{Notification, Subscription, Scheduler, Observer} import rx.lang.scala.util._ + import rx.lang.scala.subjects.Subject import rx.lang.scala.ImplicitFunctionConversions._ /** @@ -132,11 +132,13 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) * into * @param * result type - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * push results into the specified {@link Subject} + * @return a pair of a start function and an {@link Observable} such that when the start function + * is called, the Observable starts to push results into the specified {@link Subject} */ - // public ConnectableObservable multicast(Subject subject) TODO - + def multicast[R](subject: Subject[T, R]): (() => Subscription, Observable[R]) = { + val javaCO = asJava.multicast[R](subject) + (() => javaCO.connect(), Observable[R](javaCO)) + } /** * Returns an Observable that first emits the items emitted by this, and then the items emitted @@ -904,11 +906,12 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) *

* * - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * emit items to its {@link Observer}s + * @return a pair of a start function and an {@link Observable} such that when the start function + * is called, the Observable starts to emit items to its {@link Observer}s */ - def replay(): ConnectableObservable[T] = { - new ConnectableObservable[T](asJava.replay()) + def replay(): (() => Subscription, Observable[T]) = { + val javaCO = asJava.replay() + (() => javaCO.connect(), Observable[T](javaCO)) } /** @@ -937,11 +940,12 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) *

* * - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * emit items to its {@link Observer}s + * @return a pair of a start function and an {@link Observable} such that when the start function + * is called, the Observable starts to emit items to its {@link Observer}s */ - def publish: ConnectableObservable[T] = { - new ConnectableObservable[T](asJava.publish()) + def publish: (() => Subscription, Observable[T]) = { + val javaCO = asJava.publish() + (() => javaCO.connect(), Observable[T](javaCO)) } // There is no aggregate function with signature diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index bde8d63555..2900f52a86 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -191,6 +191,44 @@ class RxScalaDemo extends JUnitSuite { waitFor(firstMedalOfEachCountry) } + @Test def exampleWithoutPublish() { + val unshared = Observable(1 to 4) + unshared.subscribe(n => println(s"subscriber 1 gets $n")) + unshared.subscribe(n => println(s"subscriber 2 gets $n")) + } + + @Test def exampleWithPublish() { + val unshared = Observable(1 to 4) + val (startFunc, shared) = unshared.publish + shared.subscribe(n => println(s"subscriber 1 gets $n")) + shared.subscribe(n => println(s"subscriber 2 gets $n")) + startFunc() + } + + def doLater(waitTime: Duration, action: () => Unit): Unit = { + Observable.interval(waitTime).take(1).subscribe(_ => action()) + } + + @Test def exampleWithoutReplay() { + val numbers = Observable.interval(1000 millis).take(6) + val (startFunc, sharedNumbers) = numbers.publish + sharedNumbers.subscribe(n => println(s"subscriber 1 gets $n")) + startFunc() + // subscriber 2 misses 0, 1, 2! + doLater(3500 millis, () => { sharedNumbers.subscribe(n => println(s"subscriber 2 gets $n")) }) + waitFor(sharedNumbers) + } + + @Test def exampleWithReplay() { + val numbers = Observable.interval(1000 millis).take(6) + val (startFunc, sharedNumbers) = numbers.replay + sharedNumbers.subscribe(n => println(s"subscriber 1 gets $n")) + startFunc() + // subscriber 2 subscribes later but still gets all numbers + doLater(3500 millis, () => { sharedNumbers.subscribe(n => println(s"subscriber 2 gets $n")) }) + waitFor(sharedNumbers) + } + def output(s: String): Unit = println(s) // blocks until obs has completed diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/ConnectableObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/ConnectableObservable.scala deleted file mode 100644 index 8530517484..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/ConnectableObservable.scala +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright 2013 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.lang.scala.observables - - -class ConnectableObservable[+T](val asJava: rx.observables.ConnectableObservable[_ <: T]) extends AnyVal { - import rx.lang.scala._ - import rx.lang.scala.util._ - import rx.{Observable => JObservable} - import rx.lang.scala.ImplicitFunctionConversions._ - -} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala index 6910599783..0f6ea79d34 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala @@ -53,12 +53,6 @@ rx.plugins.RxJavaErrorHandler rx.plugins.RxJavaObservableExecutionHook rx.plugins.RxJavaPlugins -rx.subjects.AsyncSubject -rx.subjects.BehaviorSubject -rx.subjects.PublishSubject -rx.subjects.ReplaySubject -rx.subjects.Subject - rx.subscriptions.BooleanSubscription rx.subscriptions.CompositeSubscription rx.subscriptions.Subscriptions diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala new file mode 100644 index 0000000000..8f99d02bf6 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala @@ -0,0 +1,14 @@ +package rx.lang.scala + +package object subjects { + + // in Java: public abstract class Subject extends Observable implements Observer + type Subject[-T, +R] = rx.subjects.Subject[_ >: T, _ <: R] + + // TODO (including static methods of these classes) + // rx.subjects.AsyncSubject + // rx.subjects.BehaviorSubject + // rx.subjects.PublishSubject + // rx.subjects.ReplaySubject + +} \ No newline at end of file From f54857a4ddb866b846cb5af32702d100070cf43c Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 17 Sep 2013 16:57:34 +0200 Subject: [PATCH 021/333] add methods to BlockingObservable --- .../main/scala/rx/lang/scala/Observable.scala | 2 +- .../rx/lang/scala/examples/RxScalaDemo.scala | 10 +- .../observables/BlockingObservable.scala | 107 ++++++++++++++---- 3 files changed, 92 insertions(+), 27 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 3462c3316a..d26602bada 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1830,7 +1830,7 @@ class UnitTestSuite extends JUnitSuite { @Test def testTest() = { val a: Observable[Int] = Observable() - assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.last) + assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) } } diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index 2900f52a86..6030329b26 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -23,7 +23,7 @@ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ import rx.lang.scala.concurrency.NewThreadScheduler -//@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily +@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @Test def intervalExample() { @@ -229,11 +229,17 @@ class RxScalaDemo extends JUnitSuite { waitFor(sharedNumbers) } + @Test def testSingleOption() { + assertEquals(None, Observable(1, 2).toBlockingObservable.singleOption) + assertEquals(Some(1), Observable(1) .toBlockingObservable.singleOption) + assertEquals(None, Observable() .toBlockingObservable.singleOption) + } + def output(s: String): Unit = println(s) // blocks until obs has completed def waitFor[T](obs: Observable[T]): Unit = { - obs.toBlockingObservable.last + obs.toBlockingObservable.toIterable.last } } diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala index 8227caf85e..5470f6f1cb 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala @@ -22,40 +22,99 @@ class BlockingObservable[+T](val asJava: rx.observables.BlockingObservable[_ <: extends AnyVal { + /** + * Invoke a method on each item emitted by the {@link Observable}; block until the Observable + * completes. + *

+ * NOTE: This will block even if the Observable is asynchronous. + *

+ * This is similar to {@link Observable#subscribe(Observer)}, but it blocks. Because it blocks it does + * not need the {@link Observer#onCompleted()} or {@link Observer#onError(Throwable)} methods. + *

+ * + * + * @param onNext + * the {@link Action1} to invoke for every item emitted by the {@link Observable} + * @throws RuntimeException + * if an error occurs + */ def foreach(f: T => Unit): Unit = { - asJava.forEach(f) + asJava.forEach(f); } - def last: T = { - asJava.last() : T // useless ascription because of compiler bug + // last -> use toIterable.last + // lastOrDefault -> use toIterable.lastOption + // first -> use toIterable.head + // firstOrDefault -> use toIterable.headOption + // single(predicate) -> use filter and single + // singleOrDefault -> use singleOption + + /** + * Returns an {@link Iterable} that always returns the item most recently emitted by an {@link Observable}. + *

+ * + * + * @param initialValue + * the initial value that will be yielded by the {@link Iterable} sequence if the {@link Observable} has not yet emitted an item + * @return an {@link Iterable} that on each iteration returns the item that the {@link Observable} has most recently emitted + */ + def mostRecent[U >: T](initialValue: U): Iterable[U] = { + val asJavaU = asJava.asInstanceOf[rx.observables.BlockingObservable[U]] + asJavaU.mostRecent(initialValue).asScala: Iterable[U] // useless ascription because of compiler bug } - // last(Func1) - // lastOrDefault(T) - // lastOrDefault(T, Func1) - // mostRecent(T) - // next() + /** + * Returns an {@link Iterable} that blocks until the {@link Observable} emits another item, + * then returns that item. + *

+ * + * + * @return an {@link Iterable} that blocks upon each iteration until the {@link Observable} emits a new item, whereupon the Iterable returns that item + */ + def next: Iterable[T] = { + asJava.next().asScala: Iterable[T] // useless ascription because of compiler bug + } + /** + * If this {@link Observable} completes after emitting a single item, return that item, + * otherwise throw an exception. + *

+ * + * + * @return the single item emitted by the {@link Observable} + */ def single: T = { - asJava.single() : T // useless ascription because of compiler bug + asJava.single(): T // useless ascription because of compiler bug + } + + /** + * If this {@link Observable} completes after emitting a single item, return an Option containing + * this item, otherwise return {@code None}. + */ + def singleOption: Option[T] = { + var size: Int = 0 + var last: Option[T] = None + for (t <- toIterable) { + size += 1 + last = Some(t) + } + if (size == 1) last else None } - - // single(Func1) - - // def singleOption: Option[T] = { TODO } - // corresponds to Java's - // singleOrDefault(T) - - // singleOrDefault(BlockingObservable, boolean, T) - // singleOrDefault(T, Func1) - // toFuture() - + + // TODO toFuture() + + /** + * Returns an {@link Iterator} that iterates over all items emitted by this {@link Observable}. + */ def toIterable: Iterable[T] = { - asJava.toIterable().asScala : Iterable[T] // useless ascription because of compiler bug + asJava.toIterable().asScala: Iterable[T] // useless ascription because of compiler bug } - + + /** + * Returns a {@link List} that contains all items emitted by this {@link Observable}. + */ def toList: List[T] = { - asJava.toIterable().asScala.toList : List[T] // useless ascription because of compiler bug + asJava.toIterable().asScala.toList: List[T] // useless ascription because of compiler bug } -} \ No newline at end of file +} From a9178d8046ff505dcbeff63bb86cb677a217a78b Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 17 Sep 2013 18:39:26 +0200 Subject: [PATCH 022/333] start completeness test --- .../rx/lang/scala/CompletenessTest.scala | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala new file mode 100644 index 0000000000..2cbc79af15 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -0,0 +1,68 @@ +package rx.lang.scala + +import scala.reflect.runtime.universe._ +import org.scalatest.junit.JUnitSuite +import org.junit.Test +import rx.util.functions._ + +class CompletenessTest extends JUnitSuite { + + case class Op(name: String, argTypes: Type*) { + override def toString = name + argTypes.mkString("(", ", ", ")") + } + + val correspondence = Map( + Op("toList") -> Op("toSeq"), + Op("window", typeOf[Int]) -> Op("window", typeOf[Int]) + ) + + def check(cond: Boolean, ifGood: String, ifBad: String): Unit = { + if (cond) println(ifGood) else println(ifBad) + } + + def checkOperatorPresence(op: Op, tp: Type): Unit = { + val paramTypeLists = for (alt <- tp.member(newTermName(op.name)).asTerm.alternatives) + yield alt.asMethod.paramss.headOption match { + case Some(paramList) => paramList.map(symb => symb.typeSignature) + case None => List() + } + + println(paramTypeLists) + + check(paramTypeLists.contains(op.argTypes.toList), + s"$op is present in $tp", s"$op is NOT present in $tp") + } + + @Test def test3() { + val javaObs = typeOf[rx.Observable[_]] + val scalaObs = typeOf[rx.lang.scala.Observable[_]] + + for ((javaOp, scalaOp) <- correspondence) { + checkOperatorPresence(javaOp, javaObs) + checkOperatorPresence(scalaOp, scalaObs) + } + } + + + @Test def test1() { + val c = Class.forName("rx.Observable") + for (method <- c.getMethods()) { + println(method.getName()) + } + + } + + @Test def test2() { + val tp = typeOf[rx.Observable[_]] + for (member <- tp.members) println(member) + println("00000") + //println(tp.member(stringToTermName("all"))) + //println(tp.member("all")) + + val methodAll = tp.member(newTermName("all")).asMethod + println(methodAll.fullName) + + val methodBufferAlts = tp.member(newTermName("buffer")).asTerm.alternatives + } + +} From 9902832d5ad2dc79a8ed51276c691f77c606814c Mon Sep 17 00:00:00 2001 From: brad Date: Tue, 17 Sep 2013 22:25:03 +0100 Subject: [PATCH 023/333] remove scalatest packages from OSGI imports --- language-adaptors/rxjava-scala/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/build.gradle b/language-adaptors/rxjava-scala/build.gradle index f0a6579228..753c2749e1 100644 --- a/language-adaptors/rxjava-scala/build.gradle +++ b/language-adaptors/rxjava-scala/build.gradle @@ -49,7 +49,7 @@ jar { name = 'rxjava-scala' instruction 'Bundle-Vendor', 'Netflix' instruction 'Bundle-DocURL', 'https://github.com/Netflix/RxJava' - instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*' + instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,!org.scalatest.*,*' instruction 'Fragment-Host', 'com.netflix.rxjava.core' } } From 5df8e3c6ae689ac1e25db3fe6e64456bdde6b0da Mon Sep 17 00:00:00 2001 From: zsxwing Date: Wed, 18 Sep 2013 13:07:05 +0800 Subject: [PATCH 024/333] Implemented the 'elementAt' and 'elementAtOrDefault' operators. see #41 --- rxjava-core/src/main/java/rx/Observable.java | 41 +++ .../java/rx/operators/OperationElementAt.java | 238 ++++++++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationElementAt.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index d5d8fb8297..f20a4ec5e6 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -38,6 +38,7 @@ import rx.operators.OperationDematerialize; import rx.operators.OperationDistinctUntilChanged; import rx.operators.OperationDistinct; +import rx.operators.OperationElementAt; import rx.operators.OperationFilter; import rx.operators.OperationFinally; import rx.operators.OperationFirstOrDefault; @@ -4186,5 +4187,45 @@ private boolean isInternalImplementation(Object o) { Package p = o.getClass().getPackage(); // it can be null return p != null && p.getName().startsWith("rx.operators"); } + + /** + * Returns the element at a specified index in a sequence. + * + * @param index + * The zero-based index of the element to retrieve. + * + * @return An observable sequence that produces the element at the specified + * position in the source sequence. + * + * @throws IndexOutOfBoundsException + * Index is greater than or equal to the number of elements in + * the source sequence. + * @throws IndexOutOfBoundsException + * Index is less than 0. + */ + public Observable elementAt(int index) { + return create(OperationElementAt.elementAt(this, index)); + } + /** + * Returns the element at a specified index in a sequence or the default + * value if the index is out of range. + * + * @param index + * The zero-based index of the element to retrieve. + * @param defaultValue + * The default value. + * + * @return An observable sequence that produces the element at the specified + * position in the source sequence, or the default value if the + * index is outside the bounds of the source sequence. + * + * @throws IndexOutOfBoundsException + * Index is less than 0. + */ + public Observable elementAtOrDefault(int index, T defaultValue) { + return create(OperationElementAt.elementAtOrDefault(this, index, + defaultValue)); + } + } diff --git a/rxjava-core/src/main/java/rx/operators/OperationElementAt.java b/rxjava-core/src/main/java/rx/operators/OperationElementAt.java new file mode 100644 index 0000000000..7585650e19 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationElementAt.java @@ -0,0 +1,238 @@ +package rx.operators; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +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.Iterator; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; + +/** + * Returns the element at a specified index in a sequence. + */ +public class OperationElementAt { + + /** + * Returns the element at a specified index in a sequence. + * + * @param source + * Observable sequence to return the element from. + * @param index + * The zero-based index of the element to retrieve. + * + * @return An observable sequence that produces the element at the specified + * position in the source sequence. + * + * @throws IndexOutOfBoundsException + * Index is greater than or equal to the number of elements in + * the source sequence. + * @throws IndexOutOfBoundsException + * Index is less than 0. + */ + public static OnSubscribeFunc elementAt( + Observable source, int index) { + return new ElementAt(source, index, null, false); + } + + /** + * Returns the element at a specified index in a sequence or the default + * value if the index is out of range. + * + * @param source + * Observable sequence to return the element from. + * @param index + * The zero-based index of the element to retrieve. + * @param defaultValue + * The default value. + * + * @return An observable sequence that produces the element at the specified + * position in the source sequence, or the default value if the + * index is outside the bounds of the source sequence. + * + * @throws IndexOutOfBoundsException + * Index is less than 0. + */ + public static OnSubscribeFunc elementAtOrDefault( + Observable source, int index, T defaultValue) { + return new ElementAt(source, index, defaultValue, true); + } + + private static class ElementAt implements OnSubscribeFunc { + + private final Observable source; + private final int index; + private final boolean hasDefault; + private final T defaultValue; + + private ElementAt(Observable source, int index, + T defaultValue, boolean hasDefault) { + this.source = source; + this.index = index; + this.defaultValue = defaultValue; + this.hasDefault = hasDefault; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + final SafeObservableSubscription subscription = new SafeObservableSubscription(); + return subscription.wrap(source.subscribe(new Observer() { + + private AtomicInteger counter = new AtomicInteger(); + + @Override + public void onNext(T value) { + try { + int currentIndex = counter.getAndIncrement(); + if (currentIndex == index) { + observer.onNext(value); + observer.onCompleted(); + } else if (currentIndex > index) { + // this will work if the sequence is asynchronous, + // it will have no effect on a synchronous + // observable + subscription.unsubscribe(); + } + } catch (Throwable ex) { + observer.onError(ex); + // this will work if the sequence is asynchronous, it + // will have no effect on a synchronous observable + subscription.unsubscribe(); + } + + } + + @Override + public void onError(Throwable ex) { + observer.onError(ex); + } + + @Override + public void onCompleted() { + if (index < 0) { + observer.onError(new IndexOutOfBoundsException(index + + " is out of bounds")); + } else if (counter.get() <= index) { + if (hasDefault) { + observer.onNext(defaultValue); + observer.onCompleted(); + } else { + observer.onError(new IndexOutOfBoundsException( + index + " is out of bounds")); + } + } + } + })); + } + } + + public static class UnitTest { + + @Test + public void testElementAt() { + Observable w = Observable.from(1, 2); + Observable observable = Observable.create(elementAt(w, 1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(1); + verify(aObserver, times(1)).onNext(2); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testElementAtWithMinusIndex() { + Observable w = Observable.from(1, 2); + Observable observable = Observable + .create(elementAt(w, -1)); + + try { + Iterator iter = OperationToIterator + .toIterator(observable); + assertTrue(iter.hasNext()); + iter.next(); + fail("expect an IndexOutOfBoundsException when index is out of bounds"); + } catch (IndexOutOfBoundsException e) { + } + } + + @Test + public void testElementAtWithIndexOutOfBounds() + throws InterruptedException, ExecutionException { + Observable w = Observable.from(1, 2); + Observable observable = Observable.create(elementAt(w, 2)); + try { + Iterator iter = OperationToIterator + .toIterator(observable); + assertTrue(iter.hasNext()); + iter.next(); + fail("expect an IndexOutOfBoundsException when index is out of bounds"); + } catch (IndexOutOfBoundsException e) { + } + } + + @Test + public void testElementAtOrDefault() throws InterruptedException, + ExecutionException { + Observable w = Observable.from(1, 2); + Observable observable = Observable + .create(elementAtOrDefault(w, 1, 0)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(1); + verify(aObserver, times(1)).onNext(2); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testElementAtOrDefaultWithIndexOutOfBounds() + throws InterruptedException, ExecutionException { + Observable w = Observable.from(1, 2); + Observable observable = Observable + .create(elementAtOrDefault(w, 2, 0)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(1); + verify(aObserver, never()).onNext(2); + verify(aObserver, times(1)).onNext(0); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testElementAtOrDefaultWithMinusIndex() { + Observable w = Observable.from(1, 2); + Observable observable = Observable + .create(elementAtOrDefault(w, -1, 0)); + + try { + Iterator iter = OperationToIterator + .toIterator(observable); + assertTrue(iter.hasNext()); + iter.next(); + fail("expect an IndexOutOfBoundsException when index is out of bounds"); + } catch (IndexOutOfBoundsException e) { + } + } + } +} From ff29ea6a4c23ea6c05027334313dab6246988195 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Wed, 18 Sep 2013 06:33:39 +0000 Subject: [PATCH 025/333] [Gradle Release Plugin] - pre tag commit: '0.13.2'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 6d6ed65604..b18dfa7feb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.13.2-SNAPSHOT +version=0.13.2 From a8434b0ef979f65ea70be280c4f8dbfe225b49f8 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Wed, 18 Sep 2013 06:33:43 +0000 Subject: [PATCH 026/333] [Gradle Release Plugin] - new version commit: '0.13.3-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b18dfa7feb..2fbc7b5558 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.13.2 +version=0.13.3-SNAPSHOT From 380932af2fc816f5a10e7fff66d0a205e49b6ebb Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 17 Sep 2013 23:39:22 -0700 Subject: [PATCH 027/333] Version 0.13.2 --- CHANGES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index dabf343132..d1958d73e7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,13 @@ # RxJava Releases # +### Version 0.13.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.13.2%22)) ### + +* [Pull 389](https://github.com/Netflix/RxJava/pull/389) Scala Adaptor Improvements +* [Pull 382](https://github.com/Netflix/RxJava/pull/382) Removing deprecated RxImplicits from rxjava-scala +* [Pull 381](https://github.com/Netflix/RxJava/pull/381) Operator: mapWithIndex +* [Pull 380](https://github.com/Netflix/RxJava/pull/380) Implemented `distinct` and `distinctUntilChanged` variants using a comparator +* [Pull 379](https://github.com/Netflix/RxJava/pull/379) Make `interval` work with multiple subscribers + ### Version 0.13.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.13.1%22)) ### This release includes a new Scala adaptor as part of the effort from issue https://github.com/Netflix/RxJava/issues/336 pursuing idiomatic Scala support. From 61ff94481e1c2e3f603bf1cfb87cb6a705fe7859 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 18 Sep 2013 14:50:23 +0200 Subject: [PATCH 028/333] completeness test for instance methods in Scala adaptor --- .../rx/lang/scala/CompletenessTest.scala | 186 ++++++++++++++---- 1 file changed, 144 insertions(+), 42 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index 2cbc79af15..29d94c98fa 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -4,65 +4,167 @@ import scala.reflect.runtime.universe._ import org.scalatest.junit.JUnitSuite import org.junit.Test import rx.util.functions._ +import scala.collection.SortedSet +import scala.collection.SortedMap +import org.junit.Ignore class CompletenessTest extends JUnitSuite { - case class Op(name: String, argTypes: Type*) { - override def toString = name + argTypes.mkString("(", ", ", ")") + val unnecessary = "[considered unnecessary in Scala land]" + + val correspondence = defaultInstanceMethodCorrespondence ++ Map( + // manually added entries + "aggregate(Func2[T, T, T])" -> "reduce((U, U) => U)", + "aggregate(R, Func2[R, _ >: T, R])" -> "fold(R)((R, T) => R)", + "all(Func1[_ >: T, Boolean])" -> "forall(T => Boolean)", + "buffer(Long, Long, TimeUnit)" -> "buffer(Duration, Duration)", + "buffer(Long, Long, TimeUnit, Scheduler)" -> "buffer(Duration, Duration, Scheduler)", + "dematerialize()" -> "dematerialize(<:<[T, Notification[U]])", + "groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> "groupBy(T => K)", + "mapMany(Func1[_ >: T, _ <: Observable[_ <: R]])" -> "flatMap(T => Observable[R])", + "onErrorResumeNext(Func1[Throwable, _ <: Observable[_ <: T]])" -> "onErrorResumeNext(Throwable => Observable[U])", + "onErrorResumeNext(Observable[_ <: T])" -> "onErrorResumeNext(Observable[U])", + "onErrorReturn(Func1[Throwable, _ <: T])" -> "onErrorReturn(Throwable => U)", + "onExceptionResumeNext(Observable[_ <: T])" -> "onExceptionResumeNext(Observable[U])", + "reduce(Func2[T, T, T])" -> "reduce((U, U) => U)", + "reduce(R, Func2[R, _ >: T, R])" -> "fold(R)((R, T) => R)", + "scan(Func2[T, T, T])" -> unnecessary, + "scan(R, Func2[R, _ >: T, R])" -> "scan(R)((R, T) => R)", + "skip(Int)" -> "drop(Int)", + "skipWhile(Func1[_ >: T, Boolean])" -> "dropWhile(T => Boolean)", + "skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary, + "startWith(Iterable[T])" -> "[unnecessary because we can just use ++ instead]", + "toList()" -> "toSeq", + "toSortedList()" -> unnecessary, + "toSortedList(Func2[_ >: T, _ >: T, Integer])" -> unnecessary, + "where(Func1[_ >: T, Boolean])" -> "filter(T => Boolean)", + "window(Long, Long, TimeUnit)" -> "window(Duration, Duration)", + "window(Long, Long, TimeUnit, Scheduler)" -> "window(Duration, Duration, Scheduler)" + ) ++ List.iterate("T", 9)(s => s + ", T").map( + // all 9 overloads of startWith: + "startWith(" + _ + ")" -> "[unnecessary because we can just use ++ instead]" + ).toMap + + def removePackage(s: String) = s.replaceAll("(\\w+\\.)+(\\w+)", "$2") + + def methodMembersToMethodStrings(members: Iterable[Symbol]): Iterable[String] = { + for (member <- members; alt <- member.asTerm.alternatives) yield { + val m = alt.asMethod + // multiple parameter lists in case of curried functions + val paramListStrs = for (paramList <- m.paramss) yield { + paramList.map( + symb => removePackage(symb.typeSignature.toString.replaceAll(",(\\S)", ", $1")) + ).mkString("(", ", ", ")") + } + val name = alt.asMethod.name.decoded + name + paramListStrs.mkString("") + } + } + + def getPublicInstanceMethods(tp: Type): Iterable[String] = { + // declarations: => only those declared in Observable + // members => also those of superclasses + methodMembersToMethodStrings(tp.declarations.filter(m => m.isMethod && m.isPublic)) } - val correspondence = Map( - Op("toList") -> Op("toSeq"), - Op("window", typeOf[Int]) -> Op("window", typeOf[Int]) - ) + def getStaticMethods(tp: Type): Iterable[String] = { + ??? + } - def check(cond: Boolean, ifGood: String, ifBad: String): Unit = { - if (cond) println(ifGood) else println(ifBad) + @Test def printJavaInstanceMethods: Unit = { + println("\nInstance methods of rx.Observable") + println( "---------------------------------\n") + getPublicInstanceMethods(typeOf[rx.Observable[_]]).toList.sorted.foreach(println(_)) } - def checkOperatorPresence(op: Op, tp: Type): Unit = { - val paramTypeLists = for (alt <- tp.member(newTermName(op.name)).asTerm.alternatives) - yield alt.asMethod.paramss.headOption match { - case Some(paramList) => paramList.map(symb => symb.typeSignature) - case None => List() - } - - println(paramTypeLists) - - check(paramTypeLists.contains(op.argTypes.toList), - s"$op is present in $tp", s"$op is NOT present in $tp") + @Test def printScalaInstanceMethods: Unit = { + println("\nInstance methods of rx.lang.scala.Observable") + println( "--------------------------------------------\n") + getPublicInstanceMethods(typeOf[rx.lang.scala.Observable[_]]).toList.sorted.foreach(s => println(s)) } - @Test def test3() { - val javaObs = typeOf[rx.Observable[_]] - val scalaObs = typeOf[rx.lang.scala.Observable[_]] - - for ((javaOp, scalaOp) <- correspondence) { - checkOperatorPresence(javaOp, javaObs) - checkOperatorPresence(scalaOp, scalaObs) + def javaMethodSignatureToScala(s: String): String = { + s.replaceAllLiterally("Long, TimeUnit", "Duration") + .replaceAll("Action0", "() => Unit") + // nested [] can't be parsed with regex, but we still do it, please forgive us ;-) + .replaceAll("Action1\\[([^]]*)\\]", "$1 => Unit") + .replaceAll("Action2\\[([^]]*), ([^]]*)\\]", "($1, $2) => Unit") + .replaceAll("Func0\\[([^]]*)\\]", "() => $1") + .replaceAll("Func1\\[([^]]*), ([^]]*)\\]", "$1 => $2") + .replaceAll("Func2\\[([^]]*), ([^]]*), ([^]]*)\\]", "($1, $2) => $3") + .replaceAllLiterally("_ <: ", "") + .replaceAllLiterally("_ >: ", "") + .replaceAll("(\\w+)\\(\\)", "$1") + } + + def defaultInstanceMethodCorrespondence: Map[String, String] = { + val instanceMethods = getPublicInstanceMethods(typeOf[rx.Observable[_]]).toList.sorted + val tuples = for (javaM <- instanceMethods) yield (javaM, javaMethodSignatureToScala(javaM)) + tuples.toMap + } + + @Test def printDefaultInstanceMethodCorrespondence: Unit = { + println("\nDefault Instance Method Correspondence") + println( "--------------------------------------\n") + val c = SortedMap(defaultInstanceMethodCorrespondence.toSeq : _*) + val len = c.keys.map(_.length).max + 2 + for ((javaM, scalaM) <- c) { + println(s""" %-${len}s -> %s,""".format("\"" + javaM + "\"", "\"" + scalaM + "\"")) } } - - @Test def test1() { - val c = Class.forName("rx.Observable") - for (method <- c.getMethods()) { - println(method.getName()) + @Ignore // Does not yet work + @Test def printJavaStaticMethods: Unit = { + println("\nStatic methods of rx.Observable") + println( "-------------------------------\n") + getStaticMethods(typeOf[rx.Observable[_]]).toList.sorted.foreach(println(_)) + } + + def checkMethodPresence(expectedMethods: Iterable[String], tp: Type): Unit = { + val actualMethods = getPublicInstanceMethods(tp).toSet + val expMethodsSorted = expectedMethods.toList.sorted + var good = 0 + var bad = 0 + for (m <- expMethodsSorted) if (actualMethods.contains(m) || m.charAt(0) == '[') { + good += 1 + } else { + bad += 1 + println(s"Warning: $m is NOT present in $tp") } - + val status = if (bad == 0) "SUCCESS" else "BAD" + println(s"$status: $bad out of ${bad+good} methods were not found in $tp") } - @Test def test2() { - val tp = typeOf[rx.Observable[_]] - for (member <- tp.members) println(member) - println("00000") - //println(tp.member(stringToTermName("all"))) - //println(tp.member("all")) - - val methodAll = tp.member(newTermName("all")).asMethod - println(methodAll.fullName) + @Test def checkScalaMethodPresenceVerbose: Unit = { + println("\nTesting that all mentioned Scala methods exist") + println( "----------------------------------------------\n") - val methodBufferAlts = tp.member(newTermName("buffer")).asTerm.alternatives + val actualMethods = getPublicInstanceMethods(typeOf[rx.lang.scala.Observable[_]]).toSet + var good = 0 + var bad = 0 + for ((javaM, scalaM) <- SortedMap(correspondence.toSeq :_*)) { + if (actualMethods.contains(scalaM) || scalaM.charAt(0) == '[') { + good += 1 + } else { + bad += 1 + println(s"Warning:") + println(s"$scalaM is NOT present in Scala Observable") + println(s"$javaM is the method in Java Observable generating this warning") + } + } + val status = if (bad == 0) "SUCCESS" else "BAD" + println(s"$status: $bad out of ${bad+good} methods were not found in Scala Observable") + } + + @Ignore // because we prefer the verbose version + @Test def checkScalaMethodPresence: Unit = { + checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable[_]]) + } + + @Test def checkJavaMethodPresence: Unit = { + println("\nTesting that all mentioned Java methods exist") + println( "---------------------------------------------\n") + checkMethodPresence(correspondence.keys, typeOf[rx.Observable[_]]) } } From 4076ad1154cd3b04f25d9a0783df54877699db54 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 18 Sep 2013 16:09:34 +0200 Subject: [PATCH 029/333] extract static methods in completeness test --- .../rx/lang/scala/CompletenessTest.scala | 54 +++++++++++++------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index 29d94c98fa..2b1fda605d 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -7,6 +7,7 @@ import rx.util.functions._ import scala.collection.SortedSet import scala.collection.SortedMap import org.junit.Ignore +import java.lang.reflect.Modifier class CompletenessTest extends JUnitSuite { @@ -65,22 +66,50 @@ class CompletenessTest extends JUnitSuite { // declarations: => only those declared in Observable // members => also those of superclasses methodMembersToMethodStrings(tp.declarations.filter(m => m.isMethod && m.isPublic)) + // TODO how can we filter out instance methods which were put into companion because + // of extends AnyVal in a way which does not depend on implementation-chosen name '$extension'? + .filter(! _.contains("$extension")) } - def getStaticMethods(tp: Type): Iterable[String] = { - ??? - } + def getStaticJavaMethods(className: String): Iterable[String] = { + val c = Class.forName(className) + for (method <- c.getMethods() if Modifier.isStatic(method.getModifiers)) yield { + method.getName + method.getParameterTypes().map(_.getSimpleName()).mkString("(", ", ", ")") + } + } + + def getObservableCompanionMethods: Iterable[String] = { + val tp = typeOf[rx.lang.scala.Observable.type] + getPublicInstanceMethods(tp.typeSymbol.companionSymbol.typeSignature) + // TODO how can we filter out instance methods which were put into companion because + // of extends AnyVal in a way which does not depend on implementation-chosen name '$extension'? + .filter(! _.contains("$extension")) + } + + def printMethodSet(title: String, tp: Type) { + println("\n" + title) + println(title.map(_ => '-') + "\n") + getPublicInstanceMethods(tp).toList.sorted.foreach(println(_)) + } @Test def printJavaInstanceMethods: Unit = { - println("\nInstance methods of rx.Observable") - println( "---------------------------------\n") - getPublicInstanceMethods(typeOf[rx.Observable[_]]).toList.sorted.foreach(println(_)) + printMethodSet("Instance methods of rx.Observable", + typeOf[rx.Observable[_]]) } @Test def printScalaInstanceMethods: Unit = { - println("\nInstance methods of rx.lang.scala.Observable") - println( "--------------------------------------------\n") - getPublicInstanceMethods(typeOf[rx.lang.scala.Observable[_]]).toList.sorted.foreach(s => println(s)) + printMethodSet("Instance methods of rx.lang.scala.Observable", + typeOf[rx.lang.scala.Observable[_]]) + } + + @Test def printJavaStaticMethods: Unit = { + printMethodSet("Static methods of rx.Observable", + typeOf[rx.Observable[_]].typeSymbol.companionSymbol.typeSignature) + } + + @Test def printScalaCompanionMethods: Unit = { + printMethodSet("Companion methods of rx.lang.scala.Observable", + typeOf[rx.lang.scala.Observable.type]) } def javaMethodSignatureToScala(s: String): String = { @@ -113,13 +142,6 @@ class CompletenessTest extends JUnitSuite { } } - @Ignore // Does not yet work - @Test def printJavaStaticMethods: Unit = { - println("\nStatic methods of rx.Observable") - println( "-------------------------------\n") - getStaticMethods(typeOf[rx.Observable[_]]).toList.sorted.foreach(println(_)) - } - def checkMethodPresence(expectedMethods: Iterable[String], tp: Type): Unit = { val actualMethods = getPublicInstanceMethods(tp).toSet val expMethodsSorted = expectedMethods.toList.sorted From b051f09b2baa2a07b8c94a308b01bbf035c589fc Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 18 Sep 2013 17:14:58 +0200 Subject: [PATCH 030/333] completeness test now also checks static methods --- .../rx/lang/scala/CompletenessTest.scala | 101 ++++++++++++------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index 2b1fda605d..661da1afc4 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -13,8 +13,8 @@ class CompletenessTest extends JUnitSuite { val unnecessary = "[considered unnecessary in Scala land]" - val correspondence = defaultInstanceMethodCorrespondence ++ Map( - // manually added entries + val correspondence = defaultMethodCorrespondence ++ Map( + // manually added entries for Java instance methods "aggregate(Func2[T, T, T])" -> "reduce((U, U) => U)", "aggregate(R, Func2[R, _ >: T, R])" -> "fold(R)((R, T) => R)", "all(Func1[_ >: T, Boolean])" -> "forall(T => Boolean)", @@ -40,11 +40,38 @@ class CompletenessTest extends JUnitSuite { "toSortedList(Func2[_ >: T, _ >: T, Integer])" -> unnecessary, "where(Func1[_ >: T, Boolean])" -> "filter(T => Boolean)", "window(Long, Long, TimeUnit)" -> "window(Duration, Duration)", - "window(Long, Long, TimeUnit, Scheduler)" -> "window(Duration, Duration, Scheduler)" + "window(Long, Long, TimeUnit, Scheduler)" -> "window(Duration, Duration, Scheduler)", + + // manually added entries for Java static methods + "create(OnSubscribeFunc[T])" -> "apply(Observer[T] => Subscription)", + "defer(Func0[_ <: Observable[_ <: T]])" -> "defer(=> Observable[T])", + "empty()" -> "apply(T*)", + "error(Throwable)" -> "apply(Throwable)", + "from(Array[T])" -> "apply(T*)", + "from(Iterable[_ <: T])" -> "apply(T*)", + "merge(Observable[_ <: T], Observable[_ <: T])" -> "merge(Observable[T])", + "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[T])", + "range(Int, Int)" -> "apply(Range)", + "sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use (first zip second) map (p => p._1 == p._2)]", + "sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> "[use (first zip second) map (p => equality(p._1, p._2))]", + "switchDo(Observable[_ <: Observable[_ <: T]])" -> "switch", + "synchronize(Observable[_ <: T])" -> "synchronize", + "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method zip and map]" ) ++ List.iterate("T", 9)(s => s + ", T").map( // all 9 overloads of startWith: "startWith(" + _ + ")" -> "[unnecessary because we can just use ++ instead]" - ).toMap + ).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( + // concat 2-9 + "concat(" + _ + ")" -> "[unnecessary because we can use ++ instead]" + ).drop(1).toMap ++ List.iterate("T", 10)(s => s + ", T").map( + // all 10 overloads of from: + "from(" + _ + ")" -> "apply(T*)" + ).toMap ++ (3 to 9).map(i => { + // zip3-9: + val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") + val funcParams = (1 to i).map(j => s"_ >: T$j, ").mkString("") + ("zip(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", unnecessary) + }).toMap def removePackage(s: String) = s.replaceAll("(\\w+\\.)+(\\w+)", "$2") @@ -71,20 +98,11 @@ class CompletenessTest extends JUnitSuite { .filter(! _.contains("$extension")) } - def getStaticJavaMethods(className: String): Iterable[String] = { - val c = Class.forName(className) - for (method <- c.getMethods() if Modifier.isStatic(method.getModifiers)) yield { - method.getName + method.getParameterTypes().map(_.getSimpleName()).mkString("(", ", ", ")") - } - } + // also applicable for Java types + def getPublicInstanceAndCompanionMethods(tp: Type): Iterable[String] = + getPublicInstanceMethods(tp) ++ + getPublicInstanceMethods(tp.typeSymbol.companionSymbol.typeSignature) - def getObservableCompanionMethods: Iterable[String] = { - val tp = typeOf[rx.lang.scala.Observable.type] - getPublicInstanceMethods(tp.typeSymbol.companionSymbol.typeSignature) - // TODO how can we filter out instance methods which were put into companion because - // of extends AnyVal in a way which does not depend on implementation-chosen name '$extension'? - .filter(! _.contains("$extension")) - } def printMethodSet(title: String, tp: Type) { println("\n" + title) @@ -92,21 +110,25 @@ class CompletenessTest extends JUnitSuite { getPublicInstanceMethods(tp).toList.sorted.foreach(println(_)) } + @Ignore // because spams output @Test def printJavaInstanceMethods: Unit = { printMethodSet("Instance methods of rx.Observable", typeOf[rx.Observable[_]]) } + @Ignore // because spams output @Test def printScalaInstanceMethods: Unit = { printMethodSet("Instance methods of rx.lang.scala.Observable", typeOf[rx.lang.scala.Observable[_]]) } + @Ignore // because spams output @Test def printJavaStaticMethods: Unit = { printMethodSet("Static methods of rx.Observable", typeOf[rx.Observable[_]].typeSymbol.companionSymbol.typeSignature) } + @Ignore // because spams output @Test def printScalaCompanionMethods: Unit = { printMethodSet("Companion methods of rx.lang.scala.Observable", typeOf[rx.lang.scala.Observable.type]) @@ -125,25 +147,36 @@ class CompletenessTest extends JUnitSuite { .replaceAllLiterally("_ >: ", "") .replaceAll("(\\w+)\\(\\)", "$1") } - - def defaultInstanceMethodCorrespondence: Map[String, String] = { - val instanceMethods = getPublicInstanceMethods(typeOf[rx.Observable[_]]).toList.sorted - val tuples = for (javaM <- instanceMethods) yield (javaM, javaMethodSignatureToScala(javaM)) + + def defaultMethodCorrespondence: Map[String, String] = { + val allMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.Observable[_]]) + val tuples = for (javaM <- allMethods) yield (javaM, javaMethodSignatureToScala(javaM)) tuples.toMap } - @Test def printDefaultInstanceMethodCorrespondence: Unit = { - println("\nDefault Instance Method Correspondence") - println( "--------------------------------------\n") - val c = SortedMap(defaultInstanceMethodCorrespondence.toSeq : _*) + @Ignore // because spams output + @Test def printDefaultMethodCorrespondence: Unit = { + println("\nDefault Method Correspondence") + println( "-----------------------------\n") + val c = SortedMap(defaultMethodCorrespondence.toSeq : _*) val len = c.keys.map(_.length).max + 2 for ((javaM, scalaM) <- c) { println(s""" %-${len}s -> %s,""".format("\"" + javaM + "\"", "\"" + scalaM + "\"")) } } + @Ignore // because spams output + @Test def printCorrectedMethodCorrespondence: Unit = { + println("\nCorrected Method Correspondence") + println( "-------------------------------\n") + val c = SortedMap(correspondence.toSeq : _*) + for ((javaM, scalaM) <- c) { + println("%s -> %s,".format("\"" + javaM + "\"", "\"" + scalaM + "\"")) + } + } + def checkMethodPresence(expectedMethods: Iterable[String], tp: Type): Unit = { - val actualMethods = getPublicInstanceMethods(tp).toSet + val actualMethods = getPublicInstanceAndCompanionMethods(tp).toSet val expMethodsSorted = expectedMethods.toList.sorted var good = 0 var bad = 0 @@ -161,7 +194,7 @@ class CompletenessTest extends JUnitSuite { println("\nTesting that all mentioned Scala methods exist") println( "----------------------------------------------\n") - val actualMethods = getPublicInstanceMethods(typeOf[rx.lang.scala.Observable[_]]).toSet + val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable[_]]).toSet var good = 0 var bad = 0 for ((javaM, scalaM) <- SortedMap(correspondence.toSeq :_*)) { @@ -175,18 +208,18 @@ class CompletenessTest extends JUnitSuite { } } val status = if (bad == 0) "SUCCESS" else "BAD" - println(s"$status: $bad out of ${bad+good} methods were not found in Scala Observable") + println(s"\n$status: $bad out of ${bad+good} methods were not found in Scala Observable") } - - @Ignore // because we prefer the verbose version - @Test def checkScalaMethodPresence: Unit = { - checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable[_]]) - } - + @Test def checkJavaMethodPresence: Unit = { println("\nTesting that all mentioned Java methods exist") println( "---------------------------------------------\n") checkMethodPresence(correspondence.keys, typeOf[rx.Observable[_]]) } + @Ignore // because we prefer the verbose version + @Test def checkScalaMethodPresence: Unit = { + checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable[_]]) + } + } From 1179bfd54ff980e07f935996405beba6ece72d1c Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 18 Sep 2013 17:57:34 +0200 Subject: [PATCH 031/333] all move explanations why methods are not present in Observable to CompletenessTest --- .../main/scala/rx/lang/scala/Observable.scala | 106 +----------------- .../rx/lang/scala/CompletenessTest.scala | 1 + 2 files changed, 2 insertions(+), 105 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index d26602bada..d1bde2f980 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -200,16 +200,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) Observable[(T, U)](JObservable.zip[T, U, (T, U)](this.asJava, that.asJava, (t: T, u: U) => (t, u))) } - // There is no method corresponding to - // public static Observable sequenceEqual(Observable first, Observable second) - // because the Scala-idiomatic way of doing this is - // (first zip second) map (p => p._1 == p._2) - - // There is no method corresponding to - // public static Observable sequenceEqual(Observable first, Observable second, Func2 equality) - // because the Scala-idiomatic way of doing this is - // (first zip second) map (p => equality(p._1, p._2)) - /** * Creates an Observable which produces buffers of collected values. * @@ -673,10 +663,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) Observable[R](asJava.flatMap[R]((t: T) => f(t).asJava)) } - // There is no method like - // public Observable where(Func1 predicate) - // because that's called filter in Scala. - /** * Returns an Observable that applies the given function to each item emitted by an * Observable and emits the result. @@ -692,10 +678,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) Observable[R](asJava.map[R](func)) } - // There's no method like - // public Observable mapMany(Func1> func) - // because that's called flatMap in Scala. - /** * Turns all of the notifications from a source Observable into {@link Observer#onNext onNext} emissions, and marks them with their original notification types within {@link Notification} objects. *

@@ -948,10 +930,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) (() => javaCO.connect(), Observable[T](javaCO)) } - // There is no aggregate function with signature - // public Observable aggregate(Func2 accumulator) - // because that's called reduce in Scala. - // TODO add Scala-like aggregate function /** @@ -980,13 +958,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) def fold[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { Observable[R](asJava.reduce(initialValue, accumulator)) } - // corresponds to Java's - // public Observable reduce(R initialValue, Func2 accumulator) - // public Observable aggregate(R initialValue, Func2 accumulator) - - // There is no method like - // public Observable scan(Func2 accumulator) - // because scan has a seed in Scala /** * Returns an Observable that emits the results of sampling the items emitted by the source @@ -1024,7 +995,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) Observable[T](asJava.sample(duration.length, duration.unit, scheduler)) } - /** + /** * Returns an Observable that applies a function of your choosing to the first item emitted by a * source Observable, then feeds the result of that function along with the second item emitted * by an Observable into the same function, and so on until all items have been emitted by the @@ -1048,8 +1019,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) def scan[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { Observable[R](asJava.scan(initialValue, accumulator)) } - // corresponds to Scala's - // public Observable scan(R initialValue, Func2 accumulator) /** * Returns an Observable that emits a Boolean that indicates whether all of the items emitted by @@ -1068,8 +1037,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) // it's more fun in Scala: this.map(predicate).fold(true)(_ && _) } - // corresponds to Java's - // public Observable all(Func1 predicate) /** * Returns an Observable that skips the first num items emitted by the source @@ -1088,8 +1055,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) def drop(n: Int): Observable[T] = { Observable[T](asJava.skip(n)) } - // corresponds to Java's - // public Observable skip(int num) /** * Returns an Observable that emits only the first num items emitted by the source @@ -1165,8 +1130,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) def takeRight(n: Int): Observable[T] = { Observable[T](asJava.takeLast(n)) } - // corresponds to Java's - // public Observable takeLast(final int count) /** * Returns an Observable that emits the items from the source Observable only until the @@ -1207,16 +1170,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) Observable.jObsOfListToScObsOfSeq(asJava.toList()) : Observable[Seq[T]] // SI-7818 } - // corresponds to Java's method - // public Observable> toList() { - - // There are no toSortedList methods because Scala can sort itself - // public Observable> toSortedList() - // public Observable> toSortedList(Func2 sortFunction) - - // There is no method - // def startWith[U >: T](values: U*): Observable[U] - // because we can just use ++ instead /** * Groups the items emitted by this Observable according to a specified discriminator function. @@ -1233,10 +1186,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) val func = (o: rx.observables.GroupedObservable[K, _ <: T]) => (o.getKey(), Observable[T](o)) Observable[(K, Observable[T])](o1.map[(K, Observable[T])](func)) } - - // There's no method corresponding to - // public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) - // because this can be obtained by combining groupBy and map (as in Scala) /** * Given an Observable that emits Observables, creates a single Observable that @@ -1505,12 +1454,6 @@ object Observable { def apply[T](func: Observer[T] => Subscription): Observable[T] = { Observable[T](JObservable.create(func)) } - // corresponds to Java's - // public static Observable create(OnSubscribeFunc func) - - // Java's - // public static Observable empty() - // is not needed in Scala because it's a special case of varargs apply /** * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it @@ -1526,12 +1469,6 @@ object Observable { def apply(exception: Throwable): Observable[Nothing] = { Observable[Nothing](JObservable.error(exception)) } - // corresponds to Java's - // public static Observable error(Throwable exception) - - // There is no method corresponding to - // public static Observable from(Iterable iterable) - // because Scala automatically uses the varargs apply for this /** * Converts a sequence of values into an Observable. @@ -1551,18 +1488,11 @@ object Observable { def apply[T](args: T*): Observable[T] = { Observable[T](JObservable.from(args.toIterable.asJava)) } - // corresponds to Java's - // public static Observable from(T... items) def apply(range: Range): Observable[Int] = { Observable[Int](JObservable.from(range.toIterable.asJava)) } - // There is no method corresponding to - // public static Observable range(int start, int count) - // because the Scala collection library provides enough methods to create Iterables. - // Examples: Observable(1 to 5), Observable(1 until 10) - /** * Returns an Observable that calls an Observable factory to create its Observable for each * new Observer that subscribes. That is, for each subscriber, the actuall Observable is determined @@ -1586,8 +1516,6 @@ object Observable { def defer[T](observable: => Observable[T]): Observable[T] = { Observable[T](JObservable.defer(observable.asJava)) } - // corresponds to Java's - // public static Observable defer(Func0> observableFactory) /** * Returns an Observable that emits a single item and then completes. @@ -1611,16 +1539,10 @@ object Observable { def just[T](value: T): Observable[T] = { Observable[T](JObservable.just(value)) } - // corresponds to Java's - // public static Observable just(T value) // TODO we have merge and concat (++) as binary instance methods, but do we also need them as // static methods with arity > 2? - // There is no method corresponding to - // public static Observable concat(Observable... source) - // because we have the instance method ++ instead - /** * This behaves like {@link #merge(java.util.List)} except that if any of the merged Observables * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will @@ -1703,49 +1625,23 @@ object Observable { Observable[Nothing](JObservable.never()) } - // There is no method corresponding to - // public static Observable switchDo(Observable> sequenceOfSequences) - // because it's deprecated. - - // There's no - // public static Observable switchOnNext(Observable> sequenceOfSequences) - // here because that's an instance method. - - // There is no method here corresponding to - // public static Observable synchronize(Observable observable) - // because that's an instance method. - /* def apply[T](f: Future[T]): Observable[T] = { ??? // TODO convert Scala Future to Java Future } */ - // corresponds to - // public static Observable from(Future future) /* def apply[T](f: Future[T], scheduler: Scheduler): Observable[T] = { ??? // TODO convert Scala Future to Java Future } */ - // public static Observable from(Future future, Scheduler scheduler) /* def apply[T](f: Future[T], duration: Duration): Observable[T] = { ??? // TODO convert Scala Future to Java Future } */ - // corresponds to - // public static Observable from(Future future, long timeout, TimeUnit unit) - - // There is no method here corresponding to - // public static Observable zip(Observable o1, Observable o2, Func2 zipFunction) - // because it's an instance method - - // There is no method corresponding to - // public static Observable zip(Observable o1, Observable o2, Observable o3, Func3 zipFunction) - // because zip3 is not known in the Scala world - // Also applies to all zipN with N > 3 ;-) /** * Combines the given observables, emitting an event containing an aggregation of the latest values of each of the source observables diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index 661da1afc4..a21a99c5c1 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -35,6 +35,7 @@ class CompletenessTest extends JUnitSuite { "skipWhile(Func1[_ >: T, Boolean])" -> "dropWhile(T => Boolean)", "skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary, "startWith(Iterable[T])" -> "[unnecessary because we can just use ++ instead]", + "takeLast(Int)" -> "takeRight(Int)", "toList()" -> "toSeq", "toSortedList()" -> unnecessary, "toSortedList(Func2[_ >: T, _ >: T, Integer])" -> unnecessary, From b236f89f98e1f6b01d2a1f79a1387f2d09a493d1 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 18 Sep 2013 19:02:36 +0200 Subject: [PATCH 032/333] add zipWithIndex, sum, product, and an explanation why no average --- .../main/scala/rx/lang/scala/Observable.scala | 34 ++++++++++++++++++- .../rx/lang/scala/examples/RxScalaDemo.scala | 28 ++++++++++++++- .../rx/lang/scala/CompletenessTest.scala | 12 +++++++ 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index d1bde2f980..0fe47291b5 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -198,8 +198,20 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) */ def zip[U](that: Observable[U]): Observable[(T, U)] = { Observable[(T, U)](JObservable.zip[T, U, (T, U)](this.asJava, that.asJava, (t: T, u: U) => (t, u))) - } + } + /** + * Zips this Observable with its indices. + * + * @return An Observable emitting pairs consisting of all elements of this Observable paired with + * their index. Indices start at 0. + */ + def zipWithIndex: Observable[(T, Int)] = { + val fScala: (T, Integer) => (T, Int) = (elem: T, index: Integer) => (elem, index) + val fJava : Func2[_ >: T, Integer, _ <: (T, Int)] = fScala + Observable[(T, Int)](asJava.mapWithIndex[(T, Int)](fJava)) + } + /** * Creates an Observable which produces buffers of collected values. * @@ -1384,7 +1396,27 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) def throttleLast(intervalDuration: Duration, scheduler: Scheduler): Observable[T] = { Observable[T](asJava.throttleLast(intervalDuration.length, intervalDuration.unit, scheduler)) } + + /** + * Returns an Observable that sums up the elements of this Observable. + * + * @return an Observable emitting the sum of all the elements of the source Observable + * as its single item. + */ + def sum[U >: T](implicit num: Numeric[U]): Observable[U] = { + fold(num.zero)(num.plus) + } + /** + * Returns an Observable that multiplies up the elements of this Observable. + * + * @return an Observable emitting the product of all the elements of the source Observable + * as its single item. + */ + def product[U >: T](implicit num: Numeric[U]): Observable[U] = { + fold(num.one)(num.times) + } + /** * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking * operators). diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index 6030329b26..9ebfa7b999 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -233,7 +233,33 @@ class RxScalaDemo extends JUnitSuite { assertEquals(None, Observable(1, 2).toBlockingObservable.singleOption) assertEquals(Some(1), Observable(1) .toBlockingObservable.singleOption) assertEquals(None, Observable() .toBlockingObservable.singleOption) - } + } + + // We can't put a general average method into Observable.scala, because Scala's Numeric + // does not have scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum) + def doubleAverage(o: Observable[Double]): Observable[Double] = { + for ((finalSum, finalCount) <- o.fold((0.0, 0))({case ((sum, count), elem) => (sum+elem, count+1)})) + yield finalSum / finalCount + } + + @Test def averageExample() { + println(doubleAverage(Observable()).toBlockingObservable.single) + println(doubleAverage(Observable(0)).toBlockingObservable.single) + println(doubleAverage(Observable(4.44)).toBlockingObservable.single) + println(doubleAverage(Observable(1, 2, 3.5)).toBlockingObservable.single) + } + + @Test def testSum() { + assertEquals(10, Observable(1, 2, 3, 4).sum.toBlockingObservable.single) + assertEquals(6, Observable(4, 2).sum.toBlockingObservable.single) + assertEquals(0, Observable[Int]().sum.toBlockingObservable.single) + } + + @Test def testProduct() { + assertEquals(24, Observable(1, 2, 3, 4).product.toBlockingObservable.single) + assertEquals(8, Observable(4, 2).product.toBlockingObservable.single) + assertEquals(1, Observable[Int]().product.toBlockingObservable.single) + } def output(s: String): Unit = println(s) diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index a21a99c5c1..dfd3e196ee 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -12,6 +12,10 @@ import java.lang.reflect.Modifier class CompletenessTest extends JUnitSuite { val unnecessary = "[considered unnecessary in Scala land]" + + val averageProblem = "[We can't have a general average method because Scala's Numeric does not have " + + "scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum). " + + "You can use fold instead to accumulate sum and numberOfElements and divide at the end.]" val correspondence = defaultMethodCorrespondence ++ Map( // manually added entries for Java instance methods @@ -44,6 +48,10 @@ class CompletenessTest extends JUnitSuite { "window(Long, Long, TimeUnit, Scheduler)" -> "window(Duration, Duration, Scheduler)", // manually added entries for Java static methods + "average(Observable[Integer])" -> averageProblem, + "averageDoubles(Observable[Double])" -> averageProblem, + "averageFloats(Observable[Float])" -> averageProblem, + "averageLongs(Observable[Long])" -> averageProblem, "create(OnSubscribeFunc[T])" -> "apply(Observer[T] => Subscription)", "defer(Func0[_ <: Observable[_ <: T]])" -> "defer(=> Observable[T])", "empty()" -> "apply(T*)", @@ -55,6 +63,10 @@ class CompletenessTest extends JUnitSuite { "range(Int, Int)" -> "apply(Range)", "sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use (first zip second) map (p => p._1 == p._2)]", "sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> "[use (first zip second) map (p => equality(p._1, p._2))]", + "sum(Observable[Integer])" -> "sum(Numeric[U])", + "sumDoubles(Observable[Double])" -> "sum(Numeric[U])", + "sumFloats(Observable[Float])" -> "sum(Numeric[U])", + "sumLongs(Observable[Long])" -> "sum(Numeric[U])", "switchDo(Observable[_ <: Observable[_ <: T]])" -> "switch", "synchronize(Observable[_ <: T])" -> "synchronize", "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method zip and map]" From de52acb8ef05b805990f3e0eb03eb8ec0f07b286 Mon Sep 17 00:00:00 2001 From: "David M. Gross" Date: Wed, 18 Sep 2013 15:51:38 -0700 Subject: [PATCH 033/333] Adding marble diagrams --- rxjava-core/src/main/java/rx/Observable.java | 202 +++++++++++++++---- 1 file changed, 168 insertions(+), 34 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index a14e78329c..21bb697d1f 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -155,8 +155,8 @@ protected Observable(OnSubscribeFunc onSubscribe) { /** * An {@link Observer} must call an Observable's {@code subscribe} method in order to * receive items and notifications from the Observable. - * - *

A typical implementation of {@code subscribe} does the following: + *

+ * A typical implementation of {@code subscribe} does the following: *

* It stores a reference to the Observer in a collection object, such as a {@code List} object. *

@@ -232,8 +232,8 @@ public Subscription subscribe(Observer observer) { /** * An {@link Observer} must call an Observable's {@code subscribe} method in order to * receive items and notifications from the Observable. - * - *

A typical implementation of {@code subscribe} does the following: + *

+ * A typical implementation of {@code subscribe} does the following: *

* It stores a reference to the Observer in a collection object, such as a {@code List} object. *

@@ -271,6 +271,12 @@ private Subscription protectivelyWrapAndSubscribe(Observer o) { return subscription.wrap(subscribe(new SafeObserver(subscription, o))); } + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in order to + * receive items and notifications from the Observable. + * + * @param onNext + */ public Subscription subscribe(final Action1 onNext) { if (onNext == null) { throw new IllegalArgumentException("onNext can not be null"); @@ -302,6 +308,13 @@ public void onNext(T args) { }); } + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in order to + * receive items and notifications from the Observable. + * + * @param onNext + * @param scheduler + */ public Subscription subscribe(final Action1 onNext, Scheduler scheduler) { return subscribeOn(scheduler).subscribe(onNext); } @@ -340,10 +353,26 @@ public void onNext(T args) { }); } + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in order to + * receive items and notifications from the Observable. + * + * @param onNext + * @param onError + * @param scheduler + */ public Subscription subscribe(final Action1 onNext, final Action1 onError, Scheduler scheduler) { return subscribeOn(scheduler).subscribe(onNext, onError); } + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in order to + * receive items and notifications from the Observable. + * + * @param onNext + * @param onError + * @param onComplete + */ public Subscription subscribe(final Action1 onNext, final Action1 onError, final Action0 onComplete) { if (onNext == null) { throw new IllegalArgumentException("onNext can not be null"); @@ -381,6 +410,15 @@ public void onNext(T args) { }); } + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in order to + * receive items and notifications from the Observable. + * + * @param onNext + * @param onError + * @param onComplete + * @param scheduler + */ public Subscription subscribe(final Action1 onNext, final Action1 onError, final Action0 onComplete, Scheduler scheduler) { return subscribeOn(scheduler).subscribe(onNext, onError, onComplete); } @@ -880,7 +918,6 @@ public static Observable range(int start, int count) { * Returns an Observable that calls an Observable factory to create its Observable for each * new Observer that subscribes. That is, for each subscriber, the actuall Observable is determined * by the factory function. - * *

* *

@@ -1787,6 +1824,9 @@ public static Observable synchronize(Observable observable) /** * Emits an item each time interval (containing a sequential number). + *

+ * + * * @param interval * Interval size in time units (see below). * @param unit @@ -1800,6 +1840,9 @@ public static Observable interval(long interval, TimeUnit unit) { /** * Emits an item each time interval (containing a sequential number). + *

+ * + * * @param interval * Interval size in time units (see below). * @param unit @@ -2389,10 +2432,11 @@ public static Observable zip(Observab } /** - * Combines the given observables, emitting an event containing an aggregation of the latest values of each of the source observables - * each time an event is received from one of the source observables, where the aggregation is defined by the given function. + * Combines the given observables, emitting an event containing an aggregation of the latest values of each of + * the source observables each time an event is received from one of the source observables, where the + * aggregation is defined by the given function. *

- * + * * * @param o1 * The first source observable. @@ -2463,8 +2507,10 @@ public static Observable combineLates /** * Creates an Observable which produces buffers of collected values. - * - *

This Observable produces connected non-overlapping buffers. The current buffer is + *

+ * + *

+ * This Observable produces connected non-overlapping buffers. The current buffer is * emitted and replaced with a new buffer when the Observable produced by the specified {@link Func0} produces a {@link rx.util.Closing} object. The * {@link Func0} will then * be used to create a new Observable to listen for the end of the next buffer. * @@ -2482,8 +2528,10 @@ public Observable> buffer(Func0> /** * Creates an Observable which produces buffers of collected values. - * - *

This Observable produces buffers. Buffers are created when the specified "bufferOpenings" + *

+ * + *

+ * This Observable produces buffers. Buffers are created when the specified "bufferOpenings" * Observable produces a {@link rx.util.Opening} object. Additionally the {@link Func0} argument * is used to create an Observable which produces {@link rx.util.Closing} objects. When this * Observable produces such an object, the associated buffer is emitted. @@ -2504,8 +2552,10 @@ public Observable> buffer(Observable bufferOpenings, /** * Creates an Observable which produces buffers of collected values. - * - *

This Observable produces connected non-overlapping buffers, each containing "count" + *

+ * + *

+ * This Observable produces connected non-overlapping buffers, each containing "count" * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. * @@ -2521,7 +2571,9 @@ public Observable> buffer(int count) { /** * Creates an Observable which produces buffers of collected values. - * + *

+ * + *

*

This Observable produces buffers every "skip" values, each containing "count" * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. @@ -2541,8 +2593,10 @@ public Observable> buffer(int count, int skip) { /** * Creates an Observable which produces buffers of collected values. - * - *

This Observable produces connected non-overlapping buffers, each of a fixed duration + *

+ * + *

+ * This Observable produces connected non-overlapping buffers, each of a fixed duration * specified by the "timespan" argument. When the source Observable completes or encounters * an error, the current buffer is emitted and the event is propagated. * @@ -2560,8 +2614,10 @@ public Observable> buffer(long timespan, TimeUnit unit) { /** * Creates an Observable which produces buffers of collected values. - * - *

This Observable produces connected non-overlapping buffers, each of a fixed duration + *

+ * + *

+ * This Observable produces connected non-overlapping buffers, each of a fixed duration * specified by the "timespan" argument. When the source Observable completes or encounters * an error, the current buffer is emitted and the event is propagated. * @@ -2584,6 +2640,8 @@ public Observable> buffer(long timespan, TimeUnit unit, Scheduler schedu * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size * specified by the "count" argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. + *

+ * * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and @@ -2605,6 +2663,8 @@ public Observable> buffer(long timespan, TimeUnit unit, int count) { * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size * specified by the "count" argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. + *

+ * * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and @@ -2628,6 +2688,8 @@ public Observable> buffer(long timespan, TimeUnit unit, int count, Sched * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan * specified by the "timespan" argument. When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. + *

+ * * * @param timespan * The period of time each buffer is collecting values before it should be emitted. @@ -2648,6 +2710,8 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan * specified by the "timespan" argument. When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. + *

+ * * * @param timespan * The period of time each buffer is collecting values before it should be emitted. @@ -2670,6 +2734,8 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit, * non-overlapping windows. The current window is emitted and replaced with a new window when the * Observable produced by the specified {@link Func0} produces a {@link rx.util.Closing} object. The {@link Func0} will then be used to create a new Observable to listen for the end of the next * window. + *

+ * * * @param closingSelector * The {@link Func0} which is used to produce an {@link Observable} for every window created. @@ -2688,6 +2754,8 @@ public Observable> window(Func0 + * * * @param windowOpenings * The {@link Observable} which when it produces a {@link rx.util.Opening} object, will cause @@ -2707,6 +2775,8 @@ public Observable> window(Observable windowOpen * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each containing "count" elements. When the source Observable completes or * encounters an error, the current window is emitted, and the event is propagated. + *

+ * * * @param count * The maximum size of each window before it should be emitted. @@ -2722,6 +2792,8 @@ public Observable> window(int count) { * Creates an Observable which produces windows of collected values. This Observable produces windows every * "skip" values, each containing "count" elements. When the source Observable completes or encounters an error, * the current window is emitted and the event is propagated. + *

+ * * * @param count * The maximum size of each window before it should be emitted. @@ -2740,6 +2812,8 @@ public Observable> window(int count, int skip) { * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the "timespan" argument. When the source * Observable completes or encounters an error, the current window is emitted and the event is propagated. + *

+ * * * @param timespan * The period of time each window is collecting values before it should be emitted, and @@ -2757,6 +2831,8 @@ public Observable> window(long timespan, TimeUnit unit) { * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the "timespan" argument. When the source * Observable completes or encounters an error, the current window is emitted and the event is propagated. + *

+ * * * @param timespan * The period of time each window is collecting values before it should be emitted, and @@ -2777,6 +2853,8 @@ public Observable> window(long timespan, TimeUnit unit, Scheduler * non-overlapping windows, each of a fixed duration specified by the "timespan" argument or a maximum size * specified by the "count" argument (which ever is reached first). When the source Observable completes * or encounters an error, the current window is emitted and the event is propagated. + *

+ * * * @param timespan * The period of time each window is collecting values before it should be emitted, and @@ -2798,6 +2876,8 @@ public Observable> window(long timespan, TimeUnit unit, int count) * non-overlapping windows, each of a fixed duration specified by the "timespan" argument or a maximum size * specified by the "count" argument (which ever is reached first). When the source Observable completes * or encounters an error, the current window is emitted and the event is propagated. + *

+ * * * @param timespan * The period of time each window is collecting values before it should be emitted, and @@ -2821,6 +2901,8 @@ public Observable> window(long timespan, TimeUnit unit, int count, * periodically, which is determined by the "timeshift" argument. Each window is emitted after a fixed timespan * specified by the "timespan" argument. When the source Observable completes or encounters an error, the * current window is emitted and the event is propagated. + *

+ * * * @param timespan * The period of time each window is collecting values before it should be emitted. @@ -2841,6 +2923,8 @@ public Observable> window(long timespan, long timeshift, TimeUnit * periodically, which is determined by the "timeshift" argument. Each window is emitted after a fixed timespan * specified by the "timespan" argument. When the source Observable completes or encounters an error, the * current window is emitted and the event is propagated. + *

+ * * * @param timespan * The period of time each window is collecting values before it should be emitted. @@ -2861,8 +2945,8 @@ public Observable> window(long timespan, long timeshift, TimeUnit /** * Returns an Observable that emits the results of a function of your choosing applied to * combinations of N items emitted, in sequence, by N other Observables as provided by an Iterable. - * - *

{@code zip} applies this function in strict sequence, so the first item emitted by the + *

+ * {@code zip} applies this function in strict sequence, so the first item emitted by the * new Observable will be the result of the function applied to the first item emitted by * all of the Observalbes; the second item emitted by the new Observable will be the result of * the function applied to the second item emitted by each of those Observables; and so forth. @@ -2913,6 +2997,7 @@ public static Observable zip(Iterable> ws, FuncN< } /** + * Filter items emitted by an Observable. *

* * @@ -2927,6 +3012,8 @@ public Observable filter(Func1 predicate) { /** * Returns an Observable that forwards all sequentially distinct items emitted from the source Observable. + *

+ * * * @return an Observable of sequentially distinct items * @see MSDN: Observable.distinctUntilChanged @@ -2936,8 +3023,10 @@ public Observable distinctUntilChanged() { } /** - * Returns an Observable that forwards all items emitted from the source Observable that are sequentially distinct according to - * a key selector function. + * Returns an Observable that forwards all items emitted from the source Observable that are sequentially + * distinct according to a key selector function. + *

+ * * * @param keySelector * a function that projects an emitted item to a key value which is used for deciding whether an item is sequentially @@ -2950,8 +3039,10 @@ public Observable distinctUntilChanged(Func1 keyS } /** - * Returns an Observable that forwards all items emitted from the source Observable that are sequentially distinct according to - * a comparator. + * Returns an Observable that forwards all items emitted from the source Observable that are sequentially + * distinct according to a comparator. + *

+ * * * @param equalityComparator * a comparator for deciding whether two emitted items are equal or not @@ -2963,8 +3054,10 @@ public Observable distinctUntilChanged(Comparator equalityComparator) } /** - * Returns an Observable that forwards all items emitted from the source Observable that are sequentially distinct according to - * a key selector function and a comparator. + * Returns an Observable that forwards all items emitted from the source Observable that are sequentially + * distinct according to a key selector function and a comparator. + *

+ * * * @param keySelector * a function that projects an emitted item to a key value which is used for deciding whether an item is sequentially @@ -2980,6 +3073,8 @@ public Observable distinctUntilChanged(Func1 keyS /** * Returns an Observable that forwards all distinct items emitted from the source Observable. + *

+ * * * @return an Observable of distinct items * @see MSDN: Observable.distinct @@ -2989,8 +3084,10 @@ public Observable distinct() { } /** - * Returns an Observable that forwards all items emitted from the source Observable that are distinct according to - * a comparator. + * Returns an Observable that forwards all items emitted from the source Observable that are distinct according + * to a comparator. + *

+ * * * @param equalityComparator * a comparator for deciding whether two emitted items are equal or not @@ -3002,8 +3099,10 @@ public Observable distinct(Comparator equalityComparator) { } /** - * Returns an Observable that forwards all items emitted from the source Observable that are distinct according to - * a key selector function. + * Returns an Observable that forwards all items emitted from the source Observable that are distinct according + * to a key selector function. + *

+ * * * @param keySelector * a function that projects an emitted item to a key value which is used for deciding whether an item is @@ -3016,8 +3115,10 @@ public Observable distinct(Func1 keySelector) { } /** - * Returns an Observable that forwards all items emitted from the source Observable that are distinct according to - * a key selector function and a comparator. + * Returns an Observable that forwards all items emitted from the source Observable that are distinct according + * to a key selector function and a comparator. + *

+ * * * @param keySelector * a function that projects an emitted item to a key value which is used for deciding whether an item is @@ -3067,11 +3168,13 @@ public Observable flatMap(Func1 * * * @param predicate - * a function that evaluates an item emitted by the source Observable, returning {@code true} if it passes the filter + * a function that evaluates an item emitted by the source Observable, returning {@code true} if it + * passes the filter * @return an Observable that emits only those items in the original Observable that the filter * evaluates as {@code true} * @see #filter(Func1) @@ -3332,6 +3435,9 @@ public Observable reduce(Func2 accumulator) { /** * Returns an Observable that counts the total number of elements in the source Observable. + *

+ * + * * @return an Observable emitting the number of counted elements of the source Observable * as its single item. * @see MSDN: Observable.Count @@ -3347,6 +3453,9 @@ public Integer call(Integer t1, T t2) { /** * Returns an Observable that sums up the elements in the source Observable. + *

+ * + * * @param source * Source observable to compute the sum of. * @return an Observable emitting the sum of all the elements of the source Observable @@ -3384,6 +3493,9 @@ public static Observable sumDoubles(Observable source) { /** * Returns an Observable that computes the average of all elements in the source Observable. * For an empty source, it causes an ArithmeticException. + *

+ * + * * @param source * Source observable to compute the average of. * @return an Observable emitting the averageof all the elements of the source Observable @@ -3434,6 +3546,8 @@ public ConnectableObservable replay() { /** * Retry subscription to origin Observable upto given retry count. *

+ * + *

* If {@link Observer#onError} is invoked the source Observable will be re-subscribed to as many times as defined by retryCount. *

* Any {@link Observer#onNext} calls received on each attempt will be emitted and concatenated together. @@ -3452,6 +3566,8 @@ public Observable retry(int retryCount) { /** * Retry subscription to origin Observable whenever onError is called (infinite retry count). *

+ * + *

* If {@link Observer#onError} is invoked the source Observable will be re-subscribed to. *

* Any {@link Observer#onNext} calls received on each attempt will be emitted and concatenated together. @@ -3665,6 +3781,8 @@ public Observable skip(int num) { /** * Returns an Observable that emits only the very first item emitted by the source Observable. + *

+ * * * @return an Observable that emits only the very first item from the source, or none if the * source Observable completes without emitting a single item. @@ -3677,6 +3795,8 @@ public Observable first() { /** * Returns an Observable that emits only the very first item emitted by the source Observable * that satisfies a given condition. + *

+ * * * @param predicate * The condition any source emitted item has to satisfy. @@ -3691,6 +3811,8 @@ public Observable first(Func1 predicate) { /** * Returns an Observable that emits only the very first item emitted by the source Observable, or * a default value. + *

+ * * * @param defaultValue * The default value to emit if the source Observable doesn't emit anything. @@ -3705,6 +3827,8 @@ public Observable firstOrDefault(T defaultValue) { /** * Returns an Observable that emits only the very first item emitted by the source Observable * that satisfies a given condition, or a default value otherwise. + *

+ * * * @param predicate * The condition any source emitted item has to satisfy. @@ -3774,6 +3898,8 @@ public Observable takeWhileWithIndex(final Func2 + * * * @return an Observable that emits only the very first item from the source, or none if the * source Observable completes without emitting a single item. @@ -3787,6 +3913,8 @@ public Observable takeFirst() { /** * Returns an Observable that emits only the very first item emitted by the source Observable * that satisfies a given condition. + *

+ * * * @param predicate * The condition any source emitted item has to satisfy. @@ -3836,6 +3964,9 @@ public Observable takeUntil(Observable other) { /** * Returns an Observable that bypasses all items from the source Observable as long as the specified * condition holds true. Emits all further source items as soon as the condition becomes false. + *

+ * + * * @param predicate * A function to test each item emitted from the source Observable for a condition. * It receives the emitted item as first parameter and the index of the emitted item as @@ -3851,6 +3982,9 @@ public Observable skipWhileWithIndex(Func2 predi /** * Returns an Observable that bypasses all items from the source Observable as long as the specified * condition holds true. Emits all further source items as soon as the condition becomes false. + *

+ * + * * @param predicate * A function to test each item emitted from the source Observable for a condition. * @return an Observable that emits all items from the source Observable as soon as the condition From 1fb7532a2d3e024510b0b53c5a5025db88812a85 Mon Sep 17 00:00:00 2001 From: "David M. Gross" Date: Wed, 18 Sep 2013 15:57:06 -0700 Subject: [PATCH 034/333] Adding marble diagrams --- rxjava-core/src/main/java/rx/subjects/AsyncSubject.java | 3 ++- rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java | 3 ++- rxjava-core/src/main/java/rx/subjects/PublishSubject.java | 3 ++- rxjava-core/src/main/java/rx/subjects/ReplaySubject.java | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java index 4bd6e11106..f50ff21322 100644 --- a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java @@ -33,7 +33,8 @@ /** * Subject that publishes only the last event to each {@link Observer} that has subscribed when the * sequence completes. - * + *

+ * *

* Example usage: *

diff --git a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java index 4a3729c24e..fd9be518a9 100644 --- a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java @@ -32,7 +32,8 @@ /** * Subject that publishes the most recent and all subsequent events to each subscribed {@link Observer}. - * + *

+ * *

* Example usage: *

diff --git a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java index c196d83cdd..0be1463b70 100644 --- a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java @@ -45,7 +45,8 @@ /** * Subject that, once and {@link Observer} has subscribed, publishes all subsequent events to the subscriber. - * + *

+ * *

* Example usage: *

diff --git a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java index 51ac519357..eb09d84a1b 100644 --- a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java +++ b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java @@ -37,7 +37,8 @@ /** * Subject that retains all events and will replay them to an {@link Observer} that subscribes. - * + *

+ * *

* Example usage: *

From 1ea15baf5b76d407c1aa97331c3857c95d229bd6 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 17 Sep 2013 22:39:51 -0700 Subject: [PATCH 035/333] Fix ObserveOn, NewThreadScheduler and ScheduledObserver bugs @headinthebox and I were working on some code and found differences in behavior between Rx.Net and RxJava with observeOn. This commit should fix that. --- .../rx/concurrency/NewThreadScheduler.java | 78 +++++++++--- .../java/rx/operators/OperationObserveOn.java | 7 +- .../java/rx/operators/OperationRetry.java | 25 ++-- .../java/rx/operators/ScheduledObserver.java | 112 ++++++++++++----- .../subscriptions/CompositeSubscription.java | 27 ++-- .../MultipleAssignmentSubscription.java | 45 +++++++ .../java/rx/subscriptions/Subscriptions.java | 52 ++++++-- .../src/test/java/rx/ObserveOnTests.java | 118 ++++++++++++++++++ 8 files changed, 380 insertions(+), 84 deletions(-) create mode 100644 rxjava-core/src/main/java/rx/subscriptions/MultipleAssignmentSubscription.java create mode 100644 rxjava-core/src/test/java/rx/ObserveOnTests.java diff --git a/rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java b/rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java index d8e178bc0d..c33918353b 100644 --- a/rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java +++ b/rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java @@ -15,12 +15,15 @@ */ package rx.concurrency; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; import rx.Scheduler; import rx.Subscription; -import rx.operators.SafeObservableSubscription; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Func2; @@ -29,27 +32,74 @@ * Schedules work on a new thread. */ public class NewThreadScheduler extends Scheduler { - private static final NewThreadScheduler INSTANCE = new NewThreadScheduler(); + + private final static NewThreadScheduler INSTANCE = new NewThreadScheduler(); + private final static AtomicLong count = new AtomicLong(); public static NewThreadScheduler getInstance() { return INSTANCE; } - @Override - public Subscription schedule(final T state, final Func2 action) { - final SafeObservableSubscription subscription = new SafeObservableSubscription(); - final Scheduler _scheduler = this; + private NewThreadScheduler() { - Thread t = new Thread(new Runnable() { - @Override - public void run() { - subscription.wrap(action.call(_scheduler, state)); - } - }, "RxNewThreadScheduler"); + } - t.start(); + private static class EventLoopScheduler extends Scheduler { + private final ExecutorService executor; - return subscription; + private EventLoopScheduler() { + executor = Executors.newFixedThreadPool(1, new ThreadFactory() { + + @Override + public Thread newThread(Runnable r) { + return new Thread(r, "RxNewThreadScheduler-" + count.incrementAndGet()); + } + }); + } + + @Override + public Subscription schedule(final T state, final Func2 action) { + final Scheduler _scheduler = this; + return Subscriptions.from(executor.submit(new Runnable() { + + @Override + public void run() { + action.call(_scheduler, state); + } + })); + } + + @Override + public Subscription schedule(final T state, final Func2 action, final long delayTime, final TimeUnit unit) { + // we will use the system scheduler since it doesn't make sense to launch a new Thread and then sleep + // we will instead schedule the event then launch the thread after the delay has passed + final Scheduler _scheduler = this; + final CompositeSubscription subscription = new CompositeSubscription(); + ScheduledFuture f = GenericScheduledExecutorService.getInstance().schedule(new Runnable() { + + @Override + public void run() { + if (!subscription.isUnsubscribed()) { + // when the delay has passed we now do the work on the actual scheduler + Subscription s = _scheduler.schedule(state, action); + // add the subscription to the CompositeSubscription so it is unsubscribed + subscription.add(s); + } + } + }, delayTime, unit); + + // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens + subscription.add(Subscriptions.create(f)); + + return subscription; + } + + } + + @Override + public Subscription schedule(final T state, final Func2 action) { + EventLoopScheduler s = new EventLoopScheduler(); + return s.schedule(state, action); } @Override diff --git a/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java b/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java index f947333456..aef1cb9548 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java @@ -20,6 +20,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import org.mockito.InOrder; @@ -33,6 +34,8 @@ import rx.Subscription; import rx.concurrency.ImmediateScheduler; import rx.concurrency.Schedulers; +import rx.subscriptions.CompositeSubscription; +import rx.util.functions.Func2; /** * Asynchronously notify Observers on the specified Scheduler. @@ -60,7 +63,9 @@ public Subscription onSubscribe(final Observer observer) { // do nothing if we request ImmediateScheduler so we don't invoke overhead return source.subscribe(observer); } else { - return source.subscribe(new ScheduledObserver(observer, scheduler)); + CompositeSubscription s = new CompositeSubscription(); + s.add(source.subscribe(new ScheduledObserver(s, observer, scheduler))); + return s; } } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationRetry.java b/rxjava-core/src/main/java/rx/operators/OperationRetry.java index 977373cb01..0f664ee3ec 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationRetry.java +++ b/rxjava-core/src/main/java/rx/operators/OperationRetry.java @@ -26,11 +26,13 @@ import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; +import rx.Scheduler; import rx.Subscription; import rx.concurrency.Schedulers; import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.MultipleAssignmentSubscription; import rx.subscriptions.Subscriptions; -import rx.util.functions.Action0; +import rx.util.functions.Func2; public class OperationRetry { @@ -58,17 +60,19 @@ public Retry(Observable source, int retryCount) { @Override public Subscription onSubscribe(Observer observer) { - subscription.add(Schedulers.currentThread().schedule(attemptSubscription(observer))); + MultipleAssignmentSubscription rescursiveSubscription = new MultipleAssignmentSubscription(); + subscription.add(Schedulers.currentThread().schedule(rescursiveSubscription, attemptSubscription(observer))); + subscription.add(rescursiveSubscription); return subscription; } - private Action0 attemptSubscription(final Observer observer) { - return new Action0() { + private Func2 attemptSubscription(final Observer observer) { + return new Func2() { @Override - public void call() { + public Subscription call(final Scheduler scheduler, final MultipleAssignmentSubscription rescursiveSubscription) { attempts.incrementAndGet(); - source.subscribe(new Observer() { + return source.subscribe(new Observer() { @Override public void onCompleted() { @@ -79,10 +83,8 @@ public void onCompleted() { public void onError(Throwable e) { if ((retryCount == INFINITE_RETRY || attempts.get() <= retryCount) && !subscription.isUnsubscribed()) { // retry again - // remove the last subscription since we have completed (so as we retry we don't build up a huge list) - subscription.removeLast(); - // add the new subscription and schedule a retry - subscription.add(Schedulers.currentThread().schedule(attemptSubscription(observer))); + // add the new subscription and schedule a retry recursively + rescursiveSubscription.setSubscription(scheduler.schedule(rescursiveSubscription, attemptSubscription(observer))); } else { // give up and pass the failure observer.onError(e); @@ -96,6 +98,7 @@ public void onNext(T v) { }); } + }; } @@ -157,7 +160,7 @@ public void testRetrySuccess() { inOrder.verify(observer, times(1)).onCompleted(); inOrder.verifyNoMoreInteractions(); } - + @Test public void testInfiniteRetry() { int NUM_FAILURES = 20; diff --git a/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java b/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java index a5eff1b852..ed1e83e299 100644 --- a/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java +++ b/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java @@ -16,21 +16,28 @@ package rx.operators; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import rx.Notification; import rx.Observer; import rx.Scheduler; -import rx.util.functions.Action0; +import rx.Subscription; +import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.MultipleAssignmentSubscription; +import rx.util.functions.Func2; /* package */class ScheduledObserver implements Observer { private final Observer underlying; private final Scheduler scheduler; + private final CompositeSubscription parentSubscription; + private final EventLoop eventLoop = new EventLoop(); private final ConcurrentLinkedQueue> queue = new ConcurrentLinkedQueue>(); - private final AtomicInteger counter = new AtomicInteger(0); + private final AtomicBoolean started = new AtomicBoolean(); - public ScheduledObserver(Observer underlying, Scheduler scheduler) { + public ScheduledObserver(CompositeSubscription s, Observer underlying, Scheduler scheduler) { + this.parentSubscription = s; this.underlying = underlying; this.scheduler = scheduler; } @@ -50,46 +57,83 @@ public void onNext(final T args) { enqueue(new Notification(args)); } + final AtomicInteger counter = new AtomicInteger(); + private void enqueue(Notification notification) { - // this must happen before 'counter' is used to provide synchronization between threads + // this must happen before synchronization between threads queue.offer(notification); - // we now use counter to atomically determine if we need to start processing or not - // it will be 0 if it's the first notification or the scheduler has finished processing work - // and we need to start doing it again - if (counter.getAndIncrement() == 0) { - processQueue(); + /** + * If the counter is currently at 0 (before incrementing with this addition) + * we will schedule the work. + */ + if (counter.getAndIncrement() <= 0) { + if (!started.get() && started.compareAndSet(false, true)) { + // first time we use the parent scheduler to start the event loop + MultipleAssignmentSubscription recursiveSubscription = new MultipleAssignmentSubscription(); + parentSubscription.add(scheduler.schedule(recursiveSubscription, eventLoop)); + parentSubscription.add(recursiveSubscription); + } else { + // subsequent times we reschedule existing one + eventLoop.reschedule(); + } } } - private void processQueue() { - scheduler.schedule(new Action0() { - @Override - public void call() { - Notification not = queue.poll(); - - switch (not.getKind()) { - case OnNext: - underlying.onNext(not.getValue()); - break; - case OnError: - underlying.onError(not.getThrowable()); - break; - case OnCompleted: - underlying.onCompleted(); - break; - default: - throw new IllegalStateException("Unknown kind of notification " + not); + private class EventLoop implements Func2 { - } + volatile Scheduler _recursiveScheduler; + volatile MultipleAssignmentSubscription _recursiveSubscription; - // decrement count and if we still have work to do - // recursively schedule ourselves to process again - if (counter.decrementAndGet() > 0) { - scheduler.schedule(this); - } + public void reschedule() { + _recursiveSubscription.setSubscription(_recursiveScheduler.schedule(_recursiveSubscription, this)); + } + @Override + public Subscription call(Scheduler s, MultipleAssignmentSubscription recursiveSubscription) { + /* + * -------------------------------------------------------------------------------------- + * Set these the first time through so we can externally trigger recursive execution again + */ + if (_recursiveScheduler == null) { + _recursiveScheduler = s; + } + if (_recursiveSubscription == null) { + _recursiveSubscription = recursiveSubscription; } - }); + /* + * Back to regular flow + * -------------------------------------------------------------------------------------- + */ + + do { + Notification notification = queue.poll(); + // if we got a notification, send it + if (notification != null) { + + // if unsubscribed stop working + if (parentSubscription.isUnsubscribed()) { + return parentSubscription; + } + // process notification + + switch (notification.getKind()) { + case OnNext: + underlying.onNext(notification.getValue()); + break; + case OnError: + underlying.onError(notification.getThrowable()); + break; + case OnCompleted: + underlying.onCompleted(); + break; + default: + throw new IllegalStateException("Unknown kind of notification " + notification); + } + } + } while (counter.decrementAndGet() > 0); + + return parentSubscription; + } } } diff --git a/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java b/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java index 330657cd5d..6ca5c8a699 100644 --- a/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java +++ b/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java @@ -20,7 +20,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -42,18 +42,24 @@ public class CompositeSubscription implements Subscription { * TODO evaluate whether use of synchronized is a performance issue here and if it's worth using an atomic state machine or other non-locking approach */ private AtomicBoolean unsubscribed = new AtomicBoolean(false); - private final LinkedBlockingDeque subscriptions = new LinkedBlockingDeque(); + private final ConcurrentHashMap subscriptions = new ConcurrentHashMap(); public CompositeSubscription(List subscriptions) { - this.subscriptions.addAll(subscriptions); + for (Subscription s : subscriptions) { + this.subscriptions.put(s, Boolean.TRUE); + } } public CompositeSubscription(Subscription... subscriptions) { for (Subscription s : subscriptions) { - this.subscriptions.add(s); + this.subscriptions.put(s, Boolean.TRUE); } } + public void remove(Subscription s) { + this.subscriptions.remove(s); + } + public boolean isUnsubscribed() { return unsubscribed.get(); } @@ -62,24 +68,15 @@ public synchronized void add(Subscription s) { if (unsubscribed.get()) { s.unsubscribe(); } else { - subscriptions.add(s); + subscriptions.put(s, Boolean.TRUE); } } - /** - * Remove the last Subscription that was added. - * - * @return Subscription or null if none exists - */ - public synchronized Subscription removeLast() { - return subscriptions.pollLast(); - } - @Override public synchronized void unsubscribe() { if (unsubscribed.compareAndSet(false, true)) { Collection es = null; - for (Subscription s : subscriptions) { + for (Subscription s : subscriptions.keySet()) { try { s.unsubscribe(); } catch (Throwable e) { diff --git a/rxjava-core/src/main/java/rx/subscriptions/MultipleAssignmentSubscription.java b/rxjava-core/src/main/java/rx/subscriptions/MultipleAssignmentSubscription.java new file mode 100644 index 0000000000..2af6501425 --- /dev/null +++ b/rxjava-core/src/main/java/rx/subscriptions/MultipleAssignmentSubscription.java @@ -0,0 +1,45 @@ +package rx.subscriptions; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import rx.Observable; +import rx.Subscription; + +/** + * Subscription that can be checked for status such as in a loop inside an {@link Observable} to exit the loop if unsubscribed. + * + * @see Rx.Net equivalent MultipleAssignmentDisposable + */ +public class MultipleAssignmentSubscription implements Subscription { + + private final AtomicBoolean unsubscribed = new AtomicBoolean(false); + private AtomicReference subscription = new AtomicReference(); + + public boolean isUnsubscribed() { + return unsubscribed.get(); + } + + @Override + public synchronized void unsubscribe() { + unsubscribed.set(true); + Subscription s = getSubscription(); + if (s != null) { + s.unsubscribe(); + } + + } + + public synchronized void setSubscription(Subscription s) { + if (unsubscribed.get()) { + s.unsubscribe(); + } else { + subscription.set(s); + } + } + + public Subscription getSubscription() { + return subscription.get(); + } + +} diff --git a/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java b/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java index 5d52a69cb2..032a0eaece 100644 --- a/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java +++ b/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -26,7 +26,7 @@ public class Subscriptions { /** * A {@link Subscription} that does nothing. - * + * * @return {@link Subscription} */ public static Subscription empty() { @@ -35,8 +35,9 @@ public static Subscription empty() { /** * A {@link Subscription} which invokes the given {@link Action0} when unsubscribed. - * - * @param unsubscribe Action to invoke on unsubscribe. + * + * @param unsubscribe + * Action to invoke on unsubscribe. * @return {@link Subscription} */ public static Subscription create(final Action0 unsubscribe) { @@ -52,12 +53,32 @@ public void unsubscribe() { /** * A {@link Subscription} that wraps a {@link Future} and cancels it when unsubscribed. - * - * + * + * * @param f * {@link Future} * @return {@link Subscription} */ + public static Subscription from(final Future f) { + return new Subscription() { + + @Override + public void unsubscribe() { + f.cancel(true); + } + + }; + } + + /** + * A {@link Subscription} that wraps a {@link Future} and cancels it when unsubscribed. + * + * + * @param f + * {@link Future} + * @return {@link Subscription} + * @deprecated Use {@link #from(Future)} instead + */ public static Subscription create(final Future f) { return new Subscription() { @@ -71,10 +92,23 @@ public void unsubscribe() { /** * A {@link Subscription} that groups multiple Subscriptions together and unsubscribes from all of them together. - * + * + * @param subscriptions + * Subscriptions to group together + * @return {@link Subscription} + */ + + public static CompositeSubscription from(Subscription... subscriptions) { + return new CompositeSubscription(subscriptions); + } + + /** + * A {@link Subscription} that groups multiple Subscriptions together and unsubscribes from all of them together. + * * @param subscriptions * Subscriptions to group together * @return {@link Subscription} + * @deprecated Use {@link #from(Subscription...)} instead */ public static CompositeSubscription create(Subscription... subscriptions) { diff --git a/rxjava-core/src/test/java/rx/ObserveOnTests.java b/rxjava-core/src/test/java/rx/ObserveOnTests.java new file mode 100644 index 0000000000..e6e4e46b2d --- /dev/null +++ b/rxjava-core/src/test/java/rx/ObserveOnTests.java @@ -0,0 +1,118 @@ +package rx; + +import static org.junit.Assert.*; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import rx.concurrency.Schedulers; +import rx.util.functions.Action1; +import rx.util.functions.Func1; + +public class ObserveOnTests { + + /** + * Confirm that running on a NewThreadScheduler uses the same thread for the entire stream + */ + @Test + public void testObserveOnWithNewThreadScheduler() { + final AtomicInteger count = new AtomicInteger(); + final int _multiple = 99; + + Observable.range(1, 100000).map(new Func1() { + + @Override + public Integer call(Integer t1) { + return t1 * _multiple; + } + + }).observeOn(Schedulers.newThread()) + .toBlockingObservable().forEach(new Action1() { + + @Override + public void call(Integer t1) { + assertEquals(count.incrementAndGet() * _multiple, t1.intValue()); + assertTrue(Thread.currentThread().getName().startsWith("RxNewThreadScheduler")); + } + + }); + } + + /** + * Confirm that running on a ThreadPoolScheduler allows multiple threads but is still ordered. + */ + @Test + public void testObserveOnWithThreadPoolScheduler() { + final AtomicInteger count = new AtomicInteger(); + final int _multiple = 99; + + Observable.range(1, 100000).map(new Func1() { + + @Override + public Integer call(Integer t1) { + return t1 * _multiple; + } + + }).observeOn(Schedulers.threadPoolForComputation()) + .toBlockingObservable().forEach(new Action1() { + + @Override + public void call(Integer t1) { + assertEquals(count.incrementAndGet() * _multiple, t1.intValue()); + assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool")); + } + + }); + } + + /** + * Attempts to confirm that when pauses exist between events, the ScheduledObserver + * does not lose or reorder any events since the scheduler will not block, but will + * be re-scheduled when it receives new events after each pause. + * + * + * This is non-deterministic in proving success, but if it ever fails (non-deterministically) + * it is a sign of potential issues as thread-races and scheduling should not affect output. + */ + @Test + public void testObserveOnOrderingConcurrency() { + final AtomicInteger count = new AtomicInteger(); + final int _multiple = 99; + + Observable.range(1, 10000).map(new Func1() { + + @Override + public Integer call(Integer t1) { + if (randomIntFrom0to100() > 98) { + try { + Thread.sleep(2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return t1 * _multiple; + } + + }).observeOn(Schedulers.threadPoolForComputation()) + .toBlockingObservable().forEach(new Action1() { + + @Override + public void call(Integer t1) { + assertEquals(count.incrementAndGet() * _multiple, t1.intValue()); + assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool")); + } + + }); + } + + private static int randomIntFrom0to100() { + // XORShift instead of Math.random http://javamex.com/tutorials/random_numbers/xorshift.shtml + long x = System.nanoTime(); + x ^= (x << 21); + x ^= (x >>> 35); + x ^= (x << 4); + return Math.abs((int) x % 100); + } + +} From 53c30d3d113c42c049947e1010f03fac91dfa626 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 18 Sep 2013 21:34:03 -0700 Subject: [PATCH 036/333] Parallel Operator This operator came out of discussions and work with @headinthebox to allow explicit and composable declaration of blocks of work that can be scheduled for parallel execution. An Observable event stream will be sharded using groupBy using a value from Scheduler. degreeOfParallelism() (defaulting to number of CPU cores) and perform the defined work in parallel. Instead of having various parallel operators like parallelMap, parallelFilter parallelScan etc this can work generically for any operators or sequence of operators. --- .../groovy/rx/lang/groovy/TestParallel.groovy | 21 ++++ rxjava-core/src/main/java/rx/Observable.java | 36 +++++-- rxjava-core/src/main/java/rx/Scheduler.java | 13 ++- .../java/rx/operators/OperationParallel.java | 99 +++++++++++++++++++ 4 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/TestParallel.groovy create mode 100644 rxjava-core/src/main/java/rx/operators/OperationParallel.java diff --git a/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/TestParallel.groovy b/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/TestParallel.groovy new file mode 100644 index 0000000000..c2e2eb52bd --- /dev/null +++ b/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/TestParallel.groovy @@ -0,0 +1,21 @@ +package rx.lang.groovy + +import org.junit.Test + +import rx.Observable +import rx.Scheduler +import rx.concurrency.Schedulers +import rx.util.functions.Func1 + +class TestParallel { + + @Test + public void testParallelOperator() { + Observable.range(0, 100) + .parallel({ + it.map({ return it; }) + }) + .toBlockingObservable() + .forEach({ println("T: " + it + " Thread: " + Thread.currentThread()); }); + } +} diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index a14e78329c..6f515b2be3 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -34,10 +34,11 @@ import rx.operators.OperationCache; import rx.operators.OperationCombineLatest; import rx.operators.OperationConcat; +import rx.operators.OperationDebounce; import rx.operators.OperationDefer; import rx.operators.OperationDematerialize; -import rx.operators.OperationDistinctUntilChanged; import rx.operators.OperationDistinct; +import rx.operators.OperationDistinctUntilChanged; import rx.operators.OperationFilter; import rx.operators.OperationFinally; import rx.operators.OperationFirstOrDefault; @@ -53,6 +54,7 @@ import rx.operators.OperationOnErrorResumeNextViaObservable; import rx.operators.OperationOnErrorReturn; import rx.operators.OperationOnExceptionResumeNextViaObservable; +import rx.operators.OperationParallel; import rx.operators.OperationRetry; import rx.operators.OperationSample; import rx.operators.OperationScan; @@ -67,7 +69,6 @@ import rx.operators.OperationTakeUntil; import rx.operators.OperationTakeWhile; import rx.operators.OperationThrottleFirst; -import rx.operators.OperationDebounce; import rx.operators.OperationTimestamp; import rx.operators.OperationToObservableFuture; import rx.operators.OperationToObservableIterable; @@ -1773,15 +1774,13 @@ public static Observable switchOnNext(Observable * the type of item emitted by the source Observable * @return an Observable that is a chronologically well-behaved version of the source * Observable, and that synchronously notifies its {@link Observer}s */ - public static Observable synchronize(Observable observable) { - return create(OperationSynchronize.synchronize(observable)); + public Observable synchronize() { + return create(OperationSynchronize.synchronize(this)); } @@ -3484,6 +3483,31 @@ public Observable cache() { return create(OperationCache.cache(this)); } + /** + * Perform work in parallel by sharding an {@code Observable} on a {@link Schedulers#threadPoolForComputation()} {@link Scheduler} and return an {@code Observable} with the output. + * + * @param f + * a {@link Func1} that applies Observable operators to {@code Observable} in parallel and returns an {@code Observable} + * @return an Observable with the output of the {@link Func1} executed on a {@link Scheduler} + */ + public Observable parallel(Func1, Observable> f) { + return OperationParallel.parallel(this, f); + } + + /** + * Perform work in parallel by sharding an {@code Observable} on a {@link Scheduler} and return an {@code Observable} with the output. + * + * @param f + * a {@link Func1} that applies Observable operators to {@code Observable} in parallel and returns an {@code Observable} + * @param s + * a {@link Scheduler} to perform the work on. + * @return an Observable with the output of the {@link Func1} executed on a {@link Scheduler} + */ + + public Observable parallel(final Func1, Observable> f, final Scheduler s) { + return OperationParallel.parallel(this, f, s); + } + /** * Returns a {@link ConnectableObservable}, which waits until its {@link ConnectableObservable#connect connect} method is called before it begins emitting * items to those {@link Observer}s that have subscribed to it. diff --git a/rxjava-core/src/main/java/rx/Scheduler.java b/rxjava-core/src/main/java/rx/Scheduler.java index 06a708d4d4..6fdfc286c0 100644 --- a/rxjava-core/src/main/java/rx/Scheduler.java +++ b/rxjava-core/src/main/java/rx/Scheduler.java @@ -212,12 +212,23 @@ public Subscription call(Scheduler scheduler, Void state) { } /** - * Returns the scheduler's notion of current absolute time in milliseconds. + * @return the scheduler's notion of current absolute time in milliseconds. */ public long now() { return System.currentTimeMillis(); } + /** + * Parallelism available to a Scheduler. + *

+ * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. + * + * @return the scheduler's available degree of parallelism. + */ + public int degreeOfParallelism() { + return Runtime.getRuntime().availableProcessors(); + } + public static class UnitTest { @SuppressWarnings("unchecked") // mocking is unchecked, unfortunately @Test diff --git a/rxjava-core/src/main/java/rx/operators/OperationParallel.java b/rxjava-core/src/main/java/rx/operators/OperationParallel.java new file mode 100644 index 0000000000..125fd2d291 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationParallel.java @@ -0,0 +1,99 @@ +/** + * Copyright 2013 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.operators; + +import static org.junit.Assert.*; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import rx.Observable; +import rx.Scheduler; +import rx.concurrency.Schedulers; +import rx.observables.GroupedObservable; +import rx.util.functions.Action1; +import rx.util.functions.Func0; +import rx.util.functions.Func1; + +/** + * Identifies unit of work that can be executed in parallel on a given Scheduler. + */ +public final class OperationParallel { + + public static Observable parallel(Observable source, Func1, Observable> f) { + return parallel(source, f, Schedulers.threadPoolForComputation()); + } + + public static Observable parallel(final Observable source, final Func1, Observable> f, final Scheduler s) { + return Observable.defer(new Func0>() { + + @Override + public Observable call() { + final AtomicInteger i = new AtomicInteger(0); + return source.groupBy(new Func1() { + + @Override + public Integer call(T t) { + return i.incrementAndGet() % s.degreeOfParallelism(); + } + + }).flatMap(new Func1, Observable>() { + + @Override + public Observable call(GroupedObservable group) { + return f.call(group.observeOn(s)); + } + }).synchronize(); + } + }); + } + + public static class UnitTest { + + @Test + public void testParallel() { + int NUM = 1000; + final AtomicInteger count = new AtomicInteger(); + Observable.range(1, NUM).parallel( + new Func1, Observable>() { + + @Override + public Observable call(Observable o) { + return o.map(new Func1() { + + @Override + public Integer[] call(Integer t) { + return new Integer[] { t, t * 99 }; + } + + }); + } + }).toBlockingObservable().forEach(new Action1() { + + @Override + public void call(Integer[] v) { + count.incrementAndGet(); + System.out.println("V: " + v[0] + " R: " + v[1] + " Thread: " + Thread.currentThread()); + } + + }); + + // just making sure we finish and get the number we expect + assertEquals(NUM, count.get()); + } + } +} From c2fd36e7eb6063aa94c94039c497c142303691a0 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 18 Sep 2013 21:47:54 -0700 Subject: [PATCH 037/333] Making Observable.synchronize an instance method rather than static --- .../src/main/scala/rx/lang/scala/Observable.scala | 2 +- rxjava-core/src/main/java/rx/Observable.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index d26602bada..ee9ed92d5e 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -176,7 +176,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) * Observable, and that synchronously notifies its {@link Observer}s */ def synchronize: Observable[T] = { - Observable[T](JObservable.synchronize(asJava)) + Observable[T](asJava.synchronize) } /** diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 6f515b2be3..f790323ee8 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -1783,6 +1783,13 @@ public Observable synchronize() { return create(OperationSynchronize.synchronize(this)); } + /** + * @deprecated Replaced with instance method. + */ + @Deprecated + public static Observable synchronize(Observable source) { + return create(OperationSynchronize.synchronize(source)); + } /** * Emits an item each time interval (containing a sequential number). From e21805091b1f479ef81101dec4fb1b7e114753af Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 18 Sep 2013 21:53:46 -0700 Subject: [PATCH 038/333] Reorg fields --- .../src/main/java/rx/operators/ScheduledObserver.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java b/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java index ed1e83e299..20416f2235 100644 --- a/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java +++ b/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java @@ -32,10 +32,12 @@ private final Scheduler scheduler; private final CompositeSubscription parentSubscription; private final EventLoop eventLoop = new EventLoop(); - - private final ConcurrentLinkedQueue> queue = new ConcurrentLinkedQueue>(); + final AtomicInteger counter = new AtomicInteger(); private final AtomicBoolean started = new AtomicBoolean(); + private final ConcurrentLinkedQueue> queue = new ConcurrentLinkedQueue>(); + + public ScheduledObserver(CompositeSubscription s, Observer underlying, Scheduler scheduler) { this.parentSubscription = s; this.underlying = underlying; @@ -57,8 +59,6 @@ public void onNext(final T args) { enqueue(new Notification(args)); } - final AtomicInteger counter = new AtomicInteger(); - private void enqueue(Notification notification) { // this must happen before synchronization between threads queue.offer(notification); From ef792c12e4abc5321e3cc3a1e987521a38c5784a Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 18 Sep 2013 22:06:56 -0700 Subject: [PATCH 039/333] Change Interval and Sample default Scheduler Change to use built-in thread-pools rather than creating a new Executor on each invocation. The built-in ones are shared across all operators, have threads ready, are marked as daemon threads so don't prevent system shutdown, and are named for clarity when looking at thread dumps and debuggers. --- rxjava-core/src/main/java/rx/operators/OperationInterval.java | 2 +- rxjava-core/src/main/java/rx/operators/OperationSample.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationInterval.java b/rxjava-core/src/main/java/rx/operators/OperationInterval.java index 0f53c884ba..0711bff4a2 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationInterval.java +++ b/rxjava-core/src/main/java/rx/operators/OperationInterval.java @@ -46,7 +46,7 @@ public final class OperationInterval { * Creates an event each time interval. */ public static OnSubscribeFunc interval(long interval, TimeUnit unit) { - return interval(interval, unit, Schedulers.executor(Executors.newSingleThreadScheduledExecutor())); + return interval(interval, unit, Schedulers.threadPoolForComputation()); } /** diff --git a/rxjava-core/src/main/java/rx/operators/OperationSample.java b/rxjava-core/src/main/java/rx/operators/OperationSample.java index cb95737fa8..3be189f32b 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSample.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSample.java @@ -49,7 +49,7 @@ public final class OperationSample { * Samples the observable sequence at each interval. */ public static OnSubscribeFunc sample(final Observable source, long period, TimeUnit unit) { - return new Sample(source, period, unit, Schedulers.executor(Executors.newSingleThreadScheduledExecutor())); + return new Sample(source, period, unit, Schedulers.threadPoolForComputation()); } /** From 5baa40403484681880b3ee5b2d2aa98be40d2a37 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 18 Sep 2013 23:20:11 -0700 Subject: [PATCH 040/333] 0.13.4-SNAPSHOT Discarding 0.13.3 as the build upload to Sonatype failed so 0.13.3 will be skipped. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2fbc7b5558..93e1320601 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.13.3-SNAPSHOT +version=0.13.4-SNAPSHOT From e6847097bf857a78105a69cf3447da0e1c57a9d8 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Thu, 19 Sep 2013 06:25:26 +0000 Subject: [PATCH 041/333] [Gradle Release Plugin] - pre tag commit: '0.13.4'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 93e1320601..791481a357 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.13.4-SNAPSHOT +version=0.13.4 From 3e5e3229667a7321f76a2133013b07d1d55cc0ae Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Thu, 19 Sep 2013 06:25:29 +0000 Subject: [PATCH 042/333] [Gradle Release Plugin] - new version commit: '0.13.5-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 791481a357..086739b427 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.13.4 +version=0.13.5-SNAPSHOT From 9ec8b0e6ea7cec79c4a9c54f84c70de911542129 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 18 Sep 2013 23:29:20 -0700 Subject: [PATCH 043/333] Version 0.13.4 --- CHANGES.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index d1958d73e7..f356aecb7f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,15 @@ # RxJava Releases # +### Version 0.13.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.13.4%22)) ### + +* [Pull 393](https://github.com/Netflix/RxJava/pull/393) Parallel Operator & ObserveOn/ScheduledObserver Fixes +* [Pull 394](https://github.com/Netflix/RxJava/pull/394) Change Interval and Sample default Scheduler +* [Pull 391](https://github.com/Netflix/RxJava/pull/391) Fix OSGI support for rxjava-scala + +### Version 0.13.3 + +* Upload to Sonatype failed so version skipped + ### Version 0.13.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.13.2%22)) ### * [Pull 389](https://github.com/Netflix/RxJava/pull/389) Scala Adaptor Improvements From 2b7f0625019d8ea450d002ce230a4803d79a3809 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Thu, 19 Sep 2013 16:14:12 +0200 Subject: [PATCH 044/333] add merge operation and examples and try to make Olympics groupBy work with timing, but did not work due to problems with RxJava groupBy, see pull #289 --- .../main/scala/rx/lang/scala/Observable.scala | 20 +++++++ .../rx/lang/scala/examples/Olympics.scala | 1 + .../rx/lang/scala/examples/RxScalaDemo.scala | 53 ++++++++++++++++++- .../rx/lang/scala/CompletenessTest.scala | 12 +++-- 4 files changed, 80 insertions(+), 6 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 7bfbbcdd3c..57b43a5e1e 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1239,6 +1239,26 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) Observable[U](rx.Observable.merge(thisJava, thatJava)) } + /** + * Flattens the sequence of Observables emitted by {@code this} into one Observable, without any + * transformation. + *

+ * + *

+ * You can combine the items emitted by multiple Observables so that they act like a single + * Observable, by using the {@code merge} method. + * + * @return an Observable that emits items that are the result of flattening the items emitted + * by the Observables emitted by {@code this} + */ + def merge[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { + val o2: Observable[Observable[U]] = this + val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) + val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava + val o5 = rx.Observable.merge[U](o4) + Observable[U](o5) + } + /** * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. *

diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala index d826aa58e8..a3fa2345bc 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala @@ -51,6 +51,7 @@ object Olympics { def fourYearsEmpty: Observable[Medal] = { // TODO this should return an observable which emits nothing during fourYears and then completes // Because of https://github.com/Netflix/RxJava/issues/388, we get non-terminating tests + // And this https://github.com/Netflix/RxJava/pull/289#issuecomment-24738668 also causes problems // So we don't use this: // Observable.interval(fourYears).take(1).map(i => neverUsedDummyMedal).filter(m => false) // But we just return empty, which completes immediately diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index 9ebfa7b999..0fe9c3424e 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -95,7 +95,7 @@ class RxScalaDemo extends JUnitSuite { println((before ++ source).toBlockingObservable.toList) } - @Test def mergeExample() { + @Test def mergeTwoExample() { val slowNumbers = Observable.interval(400 millis).take(5).map("slow " + _) val fastNumbers = Observable.interval(200 millis).take(10).map("fast " + _) val o = (slowNumbers merge fastNumbers) @@ -103,6 +103,25 @@ class RxScalaDemo extends JUnitSuite { waitFor(o) } + def myInterval(period: Long): Observable[String] = { + Observable.interval(period.millis).map(n => s"Obs-$period emits $n") + } + + @Test def mergeManyExample() { + val o = Observable.interval(500 millis).map(n => myInterval((n+1)*100)) + val stopper = Observable.interval(5 seconds) + o.merge.takeUntil(stopper).toBlockingObservable.foreach(println(_)) + } + + @Test def mergeSomeExample() { + // To merge some observables which are all known already: + Observable( + Observable.interval(200 millis), + Observable.interval(400 millis), + Observable.interval(800 millis) + ).merge.take(12).toBlockingObservable.foreach(println(_)) + } + @Test def rangeAndBufferExample() { val o = Observable(1 to 18) o.buffer(5).subscribe((l: Seq[Int]) => println(l.mkString("[", ", ", "]"))) @@ -178,6 +197,29 @@ class RxScalaDemo extends JUnitSuite { assertEquals(List(0, 1, 2, 3), t.toBlockingObservable.toList) } + @Test def timingTest() { + val firstOnly = false + val numbersByModulo3 = Observable.interval(1000 millis).take(9).groupBy(_ % 3) + + (for ((modulo, numbers) <- numbersByModulo3) yield { + println("Observable for modulo" + modulo + " started") + + if (firstOnly) numbers.take(1) else numbers + }).merge.toBlockingObservable.foreach(println(_)) + } + + @Test def timingTest1() { + val numbersByModulo3 = Observable.interval(1000 millis).take(9).groupBy(_ % 3) + + val t0 = System.currentTimeMillis + + (for ((modulo, numbers) <- numbersByModulo3) yield { + println("Observable for modulo" + modulo + " started at t = " + (System.currentTimeMillis - t0)) + numbers.take(1) // <- TODO very unexpected + //numbers + }).merge.toBlockingObservable.foreach(println(_)) + } + @Test def groupByExample() { val medalsByCountry = Olympics.mountainBikeMedals.groupBy(medal => medal.country) @@ -191,6 +233,13 @@ class RxScalaDemo extends JUnitSuite { waitFor(firstMedalOfEachCountry) } + @Test def olympicsExample() { + val (go, medals) = Olympics.mountainBikeMedals.publish + medals.subscribe(println(_)) + go() + waitFor(medals) + } + @Test def exampleWithoutPublish() { val unshared = Observable(1 to 4) unshared.subscribe(n => println(s"subscriber 1 gets $n")) @@ -260,7 +309,7 @@ class RxScalaDemo extends JUnitSuite { assertEquals(8, Observable(4, 2).product.toBlockingObservable.single) assertEquals(1, Observable[Int]().product.toBlockingObservable.single) } - + def output(s: String): Unit = println(s) // blocks until obs has completed diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index dfd3e196ee..bc6c5a6310 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -58,7 +58,8 @@ class CompletenessTest extends JUnitSuite { "error(Throwable)" -> "apply(Throwable)", "from(Array[T])" -> "apply(T*)", "from(Iterable[_ <: T])" -> "apply(T*)", - "merge(Observable[_ <: T], Observable[_ <: T])" -> "merge(Observable[T])", + "merge(Observable[_ <: T], Observable[_ <: T])" -> "merge(Observable[U])", + "merge(Observable[_ <: Observable[_ <: T]])" -> "merge(<:<[Observable[T], Observable[Observable[U]]])", "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[T])", "range(Int, Int)" -> "apply(Range)", "sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use (first zip second) map (p => p._1 == p._2)]", @@ -67,8 +68,8 @@ class CompletenessTest extends JUnitSuite { "sumDoubles(Observable[Double])" -> "sum(Numeric[U])", "sumFloats(Observable[Float])" -> "sum(Numeric[U])", "sumLongs(Observable[Long])" -> "sum(Numeric[U])", + "synchronize(Observable[T])" -> "synchronize", "switchDo(Observable[_ <: Observable[_ <: T]])" -> "switch", - "synchronize(Observable[_ <: T])" -> "synchronize", "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method zip and map]" ) ++ List.iterate("T", 9)(s => s + ", T").map( // all 9 overloads of startWith: @@ -84,8 +85,11 @@ class CompletenessTest extends JUnitSuite { val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") val funcParams = (1 to i).map(j => s"_ >: T$j, ").mkString("") ("zip(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", unnecessary) - }).toMap - + }).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( + // merge 3-9: + "merge(" + _ + ")" -> "[unnecessary because we can use Observable(o1, o2, ...).merge instead]" + ).drop(2).toMap + def removePackage(s: String) = s.replaceAll("(\\w+\\.)+(\\w+)", "$2") def methodMembersToMethodStrings(members: Iterable[Symbol]): Iterable[String] = { From 9fc14a1334ffa5b9bd27580a1850967698eed65f Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Thu, 19 Sep 2013 18:23:23 +0200 Subject: [PATCH 045/333] add methods and examples --- .../scala/ImplicitFunctionConversions.scala | 5 + .../main/scala/rx/lang/scala/Observable.scala | 129 +++++++++++++++++- .../rx/lang/scala/examples/RxScalaDemo.scala | 32 ++++- .../rx/lang/scala/CompletenessTest.scala | 14 +- 4 files changed, 173 insertions(+), 7 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index b7f13b0783..80adb8d22a 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -73,6 +73,11 @@ object ImplicitFunctionConversions { new Func2[A, B, jlang.Boolean] { def call(a: A, b: B): jlang.Boolean = f(a, b).booleanValue } + + implicit def scalaFuncNToRxFuncN[R](f: Seq[java.lang.Object] => R): FuncN[R] = + new FuncN[R] { + def call(args: java.lang.Object*): R = f(args) + } /** * Converts a specific function shape (used in takeWhile) to the equivalent Java types with an Rx Func2 diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 57b43a5e1e..5c2da25f38 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -199,6 +199,8 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) def zip[U](that: Observable[U]): Observable[(T, U)] = { Observable[(T, U)](JObservable.zip[T, U, (T, U)](this.asJava, that.asJava, (t: T, u: U) => (t, u))) } + + // public static Observable zip(Observable> ws, final FuncN zipFunction) { /** * Zips this Observable with its indices. @@ -1068,6 +1070,21 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) Observable[T](asJava.skip(n)) } + /** + * Returns an Observable that bypasses all items from the source Observable as long as the specified + * condition holds true. Emits all further source items as soon as the condition becomes false. + *

+ * + * + * @param predicate + * A function to test each item emitted from the source Observable for a condition. + * @return an Observable that emits all items from the source Observable as soon as the condition + * becomes false. + */ + def dropWhile(predicate: T => Boolean): Observable[T] = { + Observable[T](asJava.skipWhile(predicate)) + } + /** * Returns an Observable that emits only the first num items emitted by the source * Observable. @@ -1436,6 +1453,56 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) def product[U >: T](implicit num: Numeric[U]): Observable[U] = { fold(num.one)(num.times) } + + /** + * TODO doc&test + */ + def firstOrElse[U >: T](default: => U): Observable[U] = { + this.materialize.take(1).map((n: Notification[T]) => { + if (n.getKind() == rx.Notification.Kind.OnNext) + n.getValue + else + default + }) + } + + // TODO which of these two find variants do we want? + + /** + * Finds the first element of the list satisfying a predicate, if any. + * @param p + * the predicate used to test elements. + * @return an Observable emitting an Option containing the first element in the source + * Observable that satisfies p, or None if none exists or onError was called. + */ + def find(p: T => Boolean): Observable[Option[T]] = { + this.filter(p).materialize.take(1).map((n: Notification[T]) => { + if (n.getKind() == rx.Notification.Kind.OnNext) + Some(n.getValue()) + else + None + }) + } + + /** + * Finds the first element of the list satisfying a predicate, if any. + * @param p + * the predicate used to test elements. + * @return an Observable emitting an Option containing the first element in the source + * Observable that satisfies p, or None if none exists. + */ + private def findWhichTransmitsError(p: T => Boolean): Observable[Option[T]] = { + val o: Observable[Notification[Option[T]]] = + this.filter(p).materialize.take(1).map((n: Notification[T]) => { + if (n.getKind() == rx.Notification.Kind.OnCompleted) + Notification(None) + else if (n.getKind() == rx.Notification.Kind.OnNext) + Notification(Some(n.getValue())) + else + Notification(n.getThrowable()) + }) + o.dematerialize + } /** * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking @@ -1461,6 +1528,7 @@ object Observable { import rx.{Observable => JObservable} import rx.lang.scala.{Notification, Subscription, Scheduler, Observer} import rx.lang.scala.util._ + import rx.util.functions._ import rx.lang.scala.ImplicitFunctionConversions._ private[scala] @@ -1676,19 +1744,19 @@ object Observable { def never: Observable[Nothing] = { Observable[Nothing](JObservable.never()) } - + /* def apply[T](f: Future[T]): Observable[T] = { ??? // TODO convert Scala Future to Java Future } */ - + /* def apply[T](f: Future[T], scheduler: Scheduler): Observable[T] = { ??? // TODO convert Scala Future to Java Future } */ - + /* def apply[T](f: Future[T], duration: Duration): Observable[T] = { ??? // TODO convert Scala Future to Java Future @@ -1700,7 +1768,7 @@ object Observable { * each time an event is received from one of the source observables, where the aggregation is defined by the given function. *

* - * + * * @param o1 * The first source observable. * @param o2 @@ -1712,14 +1780,57 @@ object Observable { // public static Observable combineLatest(Observable o1, Observable o2, Func2 combineFunction) // TODO do we want this as an instance method? // TODO then decide about combineLatest with > 2 Observables - + // TODO what about these two? // public static Observable zip(Observable> ws, final FuncN zipFunction) // public static Observable zip(Collection> ws, FuncN zipFunction) + /** + * Given a Seq of N observables, returns an observable that emits Seqs of N elements each. + * The first emitted Seq will contain the first element of each source observable, + * the second Seq the second element of each source observable, and so on. + * + * @param observables + * A Seq of source Observables + * @return an Observable that emits the zipped Seqs + */ + def zip[T](observables: Seq[Observable[T]]): Observable[Seq[T]] = { + val f: FuncN[Seq[T]] = (args: Seq[java.lang.Object]) => { + val asSeq: Seq[Object] = args.toSeq + asSeq.asInstanceOf[Seq[T]] + } + val list = observables.map(_.asJava).asJava + val o = rx.Observable.zip(list, f) + Observable[Seq[T]](o) + } + + /** + * Given an Observable emitting N source observables, returns an observable that emits Seqs of N elements each. + * The first emitted Seq will contain the first element of each source observable, + * the second Seq the second element of each source observable, and so on. + * + * @param observables + * An Observable emitting N source Observables + * @return an Observable that emits the zipped Seqs + */ + def zip[T](observables: Observable[Observable[T]]): Observable[Seq[T]] = { + val f: FuncN[Seq[T]] = (args: Seq[java.lang.Object]) => { + val asSeq: Seq[Object] = args.toSeq + asSeq.asInstanceOf[Seq[T]] + } + val list = observables.map(_.asJava).asJava + val o = rx.Observable.zip(list, f) + Observable[Seq[T]](o) + } + def interval(duration: Duration): Observable[Long] = { (new Observable[java.lang.Long](JObservable.interval(duration.length, duration.unit))).map(_.longValue()) } + + def interval(duration: Duration, scheduler: Scheduler): Observable[Long] = { + (new Observable[java.lang.Long](JObservable.interval(duration.length, duration.unit, scheduler))).map(_.longValue()) + } + } // Cannot yet have inner class because of this error message: @@ -1776,6 +1887,14 @@ class UnitTestSuite extends JUnitSuite { assertEquals(demat.toBlockingObservable.toIterable.toList, List(1, 2, 3)) } + @Test def testFind() { + assertEquals(Some(3), Observable(1, 3, 5).find(_ >= 2).toBlockingObservable.single) + assertEquals(Some(1), Observable(1, 3, 5).find(_ => true).toBlockingObservable.single) + assertEquals(None, Observable(1, 3, 5).find(_ > 10).toBlockingObservable.single) + assertEquals(None, Observable(new Exception()).find((i: Int) => i > 10).toBlockingObservable.single) + assertEquals(None, Observable().find((i: Int) => i > 10).toBlockingObservable.single) + } + @Test def testTest() = { val a: Observable[Int] = Observable() assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index 0fe9c3424e..804c42e764 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -23,7 +23,7 @@ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ import rx.lang.scala.concurrency.NewThreadScheduler -@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily +//@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @Test def intervalExample() { @@ -310,6 +310,36 @@ class RxScalaDemo extends JUnitSuite { assertEquals(1, Observable[Int]().product.toBlockingObservable.single) } + @Test def mapWithIndexExample() { + // We don't need mapWithIndex because we already have zipWithIndex, which we can easily + // combine with map: + Observable("a", "b", "c").zipWithIndex.map(pair => pair._1 + " has index " + pair._2) + .toBlockingObservable.foreach(println(_)) + + // Or even nicer with for-comprehension syntax: + (for ((letter, index) <- Observable("a", "b", "c").zipWithIndex) yield letter + " has index " + index) + .toBlockingObservable.foreach(println(_)) + } + + // source Observables are in a List: + @Test def zipManySeqExample() { + val observables = List(Observable(1, 2), Observable(10, 20), Observable(100, 200)) + (for (seq <- Observable.zip(observables)) yield seq.mkString("(", ", ", ")")) + .toBlockingObservable.foreach(println(_)) + } + + // source Observables are in an Observable: + @Test def zipManyObservableExample() { + val observables = Observable(Observable(1, 2), Observable(10, 20), Observable(100, 200)) + (for (seq <- Observable.zip(observables)) yield seq.mkString("(", ", ", ")")) + .toBlockingObservable.foreach(println(_)) + } + + @Test def takeFirstWithCondition() { + val condition: Int => Boolean = _ >= 3 + assertEquals(3, Observable(1, 2, 3, 4).dropWhile(!condition(_)).take(1).toBlockingObservable.single) + } + def output(s: String): Unit = println(s) // blocks until obs has completed diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index bc6c5a6310..bbfdb36eb1 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -16,6 +16,9 @@ class CompletenessTest extends JUnitSuite { val averageProblem = "[We can't have a general average method because Scala's Numeric does not have " + "scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum). " + "You can use fold instead to accumulate sum and numberOfElements and divide at the end.]" + + val commentForFirst = "[use take(1)]" + val commentForFirstWithPredicate = "[use .dropWhile(!condition).take(1) or use find(condition)]" val correspondence = defaultMethodCorrespondence ++ Map( // manually added entries for Java instance methods @@ -25,8 +28,13 @@ class CompletenessTest extends JUnitSuite { "buffer(Long, Long, TimeUnit)" -> "buffer(Duration, Duration)", "buffer(Long, Long, TimeUnit, Scheduler)" -> "buffer(Duration, Duration, Scheduler)", "dematerialize()" -> "dematerialize(<:<[T, Notification[U]])", + "first()" -> commentForFirst, + "first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, + //"firstOrDefault(T)" -> + "firstOrDefault(Func1[_ >: T, Boolean], T)" -> "[use find and map instead]", // TODO maybe firstOrElse method? "groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> "groupBy(T => K)", "mapMany(Func1[_ >: T, _ <: Observable[_ <: R]])" -> "flatMap(T => Observable[R])", + "mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> "[combine zipWithIndex with map or with a for comprehension]", "onErrorResumeNext(Func1[Throwable, _ <: Observable[_ <: T]])" -> "onErrorResumeNext(Throwable => Observable[U])", "onErrorResumeNext(Observable[_ <: T])" -> "onErrorResumeNext(Observable[U])", "onErrorReturn(Func1[Throwable, _ <: T])" -> "onErrorReturn(Throwable => U)", @@ -39,6 +47,8 @@ class CompletenessTest extends JUnitSuite { "skipWhile(Func1[_ >: T, Boolean])" -> "dropWhile(T => Boolean)", "skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary, "startWith(Iterable[T])" -> "[unnecessary because we can just use ++ instead]", + "takeFirst()" -> commentForFirst, + "takeFirst(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, "takeLast(Int)" -> "takeRight(Int)", "toList()" -> "toSeq", "toSortedList()" -> unnecessary, @@ -70,7 +80,9 @@ class CompletenessTest extends JUnitSuite { "sumLongs(Observable[Long])" -> "sum(Numeric[U])", "synchronize(Observable[T])" -> "synchronize", "switchDo(Observable[_ <: Observable[_ <: T]])" -> "switch", - "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method zip and map]" + "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method zip and map]", + "zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use zip in companion object and map]", + "zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use zip in companion object and map]" ) ++ List.iterate("T", 9)(s => s + ", T").map( // all 9 overloads of startWith: "startWith(" + _ + ")" -> "[unnecessary because we can just use ++ instead]" From fe1c127949c2f75bb7ef729fe6d165affd14895b Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 20 Sep 2013 09:49:54 +0200 Subject: [PATCH 046/333] first and firstOrElse --- .../main/scala/rx/lang/scala/Observable.scala | 110 ++++++++++-------- .../rx/lang/scala/examples/RxScalaDemo.scala | 9 +- .../rx/lang/scala/CompletenessTest.scala | 10 +- 3 files changed, 71 insertions(+), 58 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 5c2da25f38..48c1b1bdcf 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1453,57 +1453,39 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) def product[U >: T](implicit num: Numeric[U]): Observable[U] = { fold(num.one)(num.times) } - + /** - * TODO doc&test + * Returns an Observable that emits only the very first item emitted by the source Observable, or + * a default value if the source Observable is empty. + *

+ * + * + * @param defaultValue + * The default value to emit if the source Observable doesn't emit anything. + * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything. + * @return an Observable that emits only the very first item from the source, or a default value + * if the source Observable completes without emitting any item. */ def firstOrElse[U >: T](default: => U): Observable[U] = { - this.materialize.take(1).map((n: Notification[T]) => { - if (n.getKind() == rx.Notification.Kind.OnNext) - n.getValue - else - default + this.take(1).fold[Option[U]](None)((v: Option[U], e: U) => Some(e)).map({ + case Some(element) => element + case None => default }) } - - // TODO which of these two find variants do we want? - + /** - * Finds the first element of the list satisfying a predicate, if any. - * @param p - * the predicate used to test elements. - * @return an Observable emitting an Option containing the first element in the source - * Observable that satisfies p, or None if none exists or onError was called. - */ - def find(p: T => Boolean): Observable[Option[T]] = { - this.filter(p).materialize.take(1).map((n: Notification[T]) => { - if (n.getKind() == rx.Notification.Kind.OnNext) - Some(n.getValue()) - else - None - }) + * Returns an Observable that emits only the very first item emitted by the source Observable. + * This is just a shorthand for {@code take(1)}. + *

+ * + * + * @return an Observable that emits only the very first item from the source, or none if the + * source Observable completes without emitting a single item. + */ + def first: Observable[T] = { + take(1) } - /** - * Finds the first element of the list satisfying a predicate, if any. - * @param p - * the predicate used to test elements. - * @return an Observable emitting an Option containing the first element in the source - * Observable that satisfies p, or None if none exists. - */ - private def findWhichTransmitsError(p: T => Boolean): Observable[Option[T]] = { - val o: Observable[Notification[Option[T]]] = - this.filter(p).materialize.take(1).map((n: Notification[T]) => { - if (n.getKind() == rx.Notification.Kind.OnCompleted) - Notification(None) - else if (n.getKind() == rx.Notification.Kind.OnNext) - Notification(Some(n.getValue())) - else - Notification(n.getThrowable()) - }) - o.dematerialize - } - /** * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking * operators). @@ -1586,8 +1568,8 @@ object Observable { * the type of the items (ostensibly) emitted by the Observable * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it */ - def apply(exception: Throwable): Observable[Nothing] = { - Observable[Nothing](JObservable.error(exception)) + def apply[T](exception: Throwable): Observable[T] = { + Observable[T](JObservable.error(exception)) } /** @@ -1886,13 +1868,39 @@ class UnitTestSuite extends JUnitSuite { assertEquals(demat.toBlockingObservable.toIterable.toList, List(1, 2, 3)) } + + // Test that Java's firstOrDefault propagates errors. + // If this changes (i.e. it suppresses errors and returns default) then Scala's firstOrElse + // should be changed accordingly. + @Test def testJavaFirstOrDefault() { + assertEquals(1, rx.Observable.from(1, 2).firstOrDefault(10).toBlockingObservable().single) + assertEquals(10, rx.Observable.empty().firstOrDefault(10).toBlockingObservable().single) + val msg = "msg6251" + var receivedMsg = "none" + try { + rx.Observable.error(new Exception(msg)).firstOrDefault(10).toBlockingObservable().single + } catch { + case e: Exception => receivedMsg = e.getCause().getMessage() + } + assertEquals(receivedMsg, msg) + } - @Test def testFind() { - assertEquals(Some(3), Observable(1, 3, 5).find(_ >= 2).toBlockingObservable.single) - assertEquals(Some(1), Observable(1, 3, 5).find(_ => true).toBlockingObservable.single) - assertEquals(None, Observable(1, 3, 5).find(_ > 10).toBlockingObservable.single) - assertEquals(None, Observable(new Exception()).find((i: Int) => i > 10).toBlockingObservable.single) - assertEquals(None, Observable().find((i: Int) => i > 10).toBlockingObservable.single) + @Test def testFirstOrElse() { + def mustNotBeCalled: String = error("this method should not be called") + def mustBeCalled: String = "this is the default value" + assertEquals("hello", Observable("hello").firstOrElse(mustNotBeCalled).toBlockingObservable.single) + assertEquals("this is the default value", Observable().firstOrElse(mustBeCalled).toBlockingObservable.single) + } + + @Test def testFirstOrElseWithError() { + val msg = "msg6251" + var receivedMsg = "none" + try { + Observable[Int](new Exception(msg)).firstOrElse(10).toBlockingObservable.single + } catch { + case e: Exception => receivedMsg = e.getCause().getMessage() + } + assertEquals(receivedMsg, msg) } @Test def testTest() = { diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index 804c42e764..73e3c9c7ea 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -337,9 +337,16 @@ class RxScalaDemo extends JUnitSuite { @Test def takeFirstWithCondition() { val condition: Int => Boolean = _ >= 3 - assertEquals(3, Observable(1, 2, 3, 4).dropWhile(!condition(_)).take(1).toBlockingObservable.single) + assertEquals(3, Observable(1, 2, 3, 4).filter(condition).first.toBlockingObservable.single) } + @Test def firstOrDefaultWithCondition() { + val condition: Int => Boolean = _ >= 3 + assertEquals(3, Observable(1, 2, 3, 4).filter(condition).firstOrElse(10).toBlockingObservable.single) + assertEquals(10, Observable(-1, 0, 1).filter(condition).firstOrElse(10).toBlockingObservable.single) + } + + def output(s: String): Unit = println(s) // blocks until obs has completed diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index bbfdb36eb1..78aa70ccae 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -17,8 +17,7 @@ class CompletenessTest extends JUnitSuite { "scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum). " + "You can use fold instead to accumulate sum and numberOfElements and divide at the end.]" - val commentForFirst = "[use take(1)]" - val commentForFirstWithPredicate = "[use .dropWhile(!condition).take(1) or use find(condition)]" + val commentForFirstWithPredicate = "[use .filter(condition).first]" val correspondence = defaultMethodCorrespondence ++ Map( // manually added entries for Java instance methods @@ -28,10 +27,9 @@ class CompletenessTest extends JUnitSuite { "buffer(Long, Long, TimeUnit)" -> "buffer(Duration, Duration)", "buffer(Long, Long, TimeUnit, Scheduler)" -> "buffer(Duration, Duration, Scheduler)", "dematerialize()" -> "dematerialize(<:<[T, Notification[U]])", - "first()" -> commentForFirst, "first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, - //"firstOrDefault(T)" -> - "firstOrDefault(Func1[_ >: T, Boolean], T)" -> "[use find and map instead]", // TODO maybe firstOrElse method? + "firstOrDefault(T)" -> "firstOrElse(=> U)", + "firstOrDefault(Func1[_ >: T, Boolean], T)" -> "[use .filter(condition).firstOrElse(default)]", "groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> "groupBy(T => K)", "mapMany(Func1[_ >: T, _ <: Observable[_ <: R]])" -> "flatMap(T => Observable[R])", "mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> "[combine zipWithIndex with map or with a for comprehension]", @@ -47,7 +45,7 @@ class CompletenessTest extends JUnitSuite { "skipWhile(Func1[_ >: T, Boolean])" -> "dropWhile(T => Boolean)", "skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary, "startWith(Iterable[T])" -> "[unnecessary because we can just use ++ instead]", - "takeFirst()" -> commentForFirst, + "takeFirst()" -> "first", "takeFirst(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, "takeLast(Int)" -> "takeRight(Int)", "toList()" -> "toSeq", From 5a813d671e238617a38aac17fcc69671e6fc7a53 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 20 Sep 2013 11:07:25 +0200 Subject: [PATCH 047/333] add distinct and distinctUntilChanged, but those with custom equality are not yet there because of issue #395 --- .../main/scala/rx/lang/scala/Observable.scala | 119 +++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 48c1b1bdcf..8b162f8484 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1485,11 +1485,126 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) def first: Observable[T] = { take(1) } - + + /** + * Returns an Observable that forwards all sequentially distinct items emitted from the source Observable. + *

+ * + * + * @return an Observable of sequentially distinct items + */ + def distinctUntilChanged: Observable[T] = { + Observable[T](asJava.distinctUntilChanged) + } + + /** + * Returns an Observable that forwards all items emitted from the source Observable that are sequentially + * distinct according to a key selector function. + *

+ * + * + * @param keySelector + * a function that projects an emitted item to a key value which is used for deciding whether an item is sequentially + * distinct from another one or not + * @return an Observable of sequentially distinct items + */ + def distinctUntilChanged[U](keySelector: T => U): Observable[T] = { + Observable[T](asJava.distinctUntilChanged[U](keySelector)) + } + + /** + * Returns an Observable that forwards all items emitted from the source Observable that are sequentially + * distinct according to an equality function. + *

+ * + * + * @param equality + * an equality function for deciding whether two emitted items are equal or not + * @return an Observable of sequentially distinct items + */ + // def distinctUntilChanged[U](equality: (T, T) => Boolean): Observable[T] = { + // TODO once https://github.com/Netflix/RxJava/issues/395 is fixed + // } + + /** + * Returns an Observable that forwards all items emitted from the source Observable that are sequentially + * distinct according to a key selector function and a comparator. + *

+ * + * + * @param keySelector + * a function that projects an emitted item to a key value which is used for deciding whether an item is sequentially + * distinct from another one or not + * @param equality + * an equality function for deciding whether two emitted item keys are equal or not + * @return an Observable of sequentially distinct items + */ + // def distinctUntilChanged[U](keySelector: T => U, equality: (T, T) => Boolean): Observable[T] = { + // TODO once https://github.com/Netflix/RxJava/issues/395 is fixed + // } + + /** + * Returns an Observable that forwards all distinct items emitted from the source Observable. + *

+ * + * + * @return an Observable of distinct items + */ + def distinct: Observable[T] = { + Observable[T](asJava.distinct()) + } + + /** + * Returns an Observable that forwards all items emitted from the source Observable that are distinct according + * to a comparator. + *

+ * + * + * @param equality + * an equality function for deciding whether two emitted items are equal or not + * @return an Observable of distinct items + */ + // def distinct(equality: (T, T) => Boolean): Observable[T] = { + // TODO once https://github.com/Netflix/RxJava/issues/395 is fixed + // } + + /** + * Returns an Observable that forwards all items emitted from the source Observable that are distinct according + * to a key selector function. + *

+ * + * + * @param keySelector + * a function that projects an emitted item to a key value which is used for deciding whether an item is + * distinct from another one or not + * @return an Observable of distinct items + */ + def distinct[U](keySelector: T => U): Observable[T] = { + Observable[T](asJava.distinct[U](keySelector)) + } + + /** + * Returns an Observable that forwards all items emitted from the source Observable that are distinct according + * to a key selector function and a comparator. + *

+ * + * + * @param keySelector + * a function that projects an emitted item to a key value which is used for deciding whether an item is + * distinct from another one or not + * @param equality + * an equality function for deciding whether two emitted item keys are equal or not + * @return an Observable of distinct items + * @see MSDN: Observable.distinct + */ + // def distinct[U](keySelector: T => U, equality: (T, T) => Boolean): Observable[T] = { + // TODO once https://github.com/Netflix/RxJava/issues/395 is fixed + //} + /** * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking * operators). - * + * * @see Blocking Observable Operators */ def toBlockingObservable: BlockingObservable[T] = { From 149596888928f281f6a768c183fdac080f54302c Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 20 Sep 2013 11:52:15 +0200 Subject: [PATCH 048/333] add concat, length, retry; rename replay()->replay, merge->flatten --- .../main/scala/rx/lang/scala/Observable.scala | 79 +++++++++++++++--- .../rx/lang/scala/examples/Olympics.scala | 80 +++++++++++-------- .../rx/lang/scala/examples/RxScalaDemo.scala | 12 +-- .../rx/lang/scala/CompletenessTest.scala | 13 ++- 4 files changed, 130 insertions(+), 54 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 8b162f8484..cd22eee5bb 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -157,7 +157,23 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) Observable(JObservable.concat(o1, o2)) } - + /** + * Returns an Observable that emits the items emitted by two or more Observables, one after the + * other. + *

+ * + * + * @return an Observable that emits items that are the result of combining the items emitted by + * the source Observables, one after the other + */ + def concat[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { + val o2: Observable[Observable[U]] = this + val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) + val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava + val o5 = rx.Observable.concat[U](o4) + Observable[U](o5) + } + /** * Wraps this Observable in another Observable that ensures that the resulting * Observable is chronologically well-behaved. @@ -905,7 +921,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) * @return a pair of a start function and an {@link Observable} such that when the start function * is called, the Observable starts to emit items to its {@link Observer}s */ - def replay(): (() => Subscription, Observable[T]) = { + def replay: (() => Subscription, Observable[T]) = { val javaCO = asJava.replay() (() => javaCO.connect(), Observable[T](javaCO)) } @@ -1234,8 +1250,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) val o5 = rx.Observable.switchOnNext[U](o4) Observable[U](o5) } - // TODO naming: follow C# (switch) or Java (switchOnNext)? - // public static Observable switchOnNext(Observable> sequenceOfSequences) + // Naming: We follow C# (switch), not Java (switchOnNext), because Java just had to avoid clash with keyword /** * Flattens two Observables into one Observable, without any transformation. @@ -1263,12 +1278,12 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) * *

* You can combine the items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. + * Observable by using this method. * * @return an Observable that emits items that are the result of flattening the items emitted * by the Observables emitted by {@code this} */ - def merge[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { + def flatten[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava @@ -1601,6 +1616,55 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) // TODO once https://github.com/Netflix/RxJava/issues/395 is fixed //} + /** + * Returns an Observable that counts the total number of elements in the source Observable. + *

+ * + * + * @return an Observable emitting the number of counted elements of the source Observable + * as its single item. + */ + def length: Observable[Int] = { + Observable[Integer](asJava.count()).map(_.intValue()) + } + + /** + * Retry subscription to origin Observable upto given retry count. + *

+ * + *

+ * If {@link Observer#onError} is invoked the source Observable will be re-subscribed to as many times as defined by retryCount. + *

+ * Any {@link Observer#onNext} calls received on each attempt will be emitted and concatenated together. + *

+ * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and + * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. + * + * @param retryCount + * Number of retry attempts before failing. + * @return Observable with retry logic. + */ + def retry(retryCount: Int): Observable[T] = { + Observable[T](asJava.retry(retryCount)) + } + + /** + * Retry subscription to origin Observable whenever onError is called (infinite retry count). + *

+ * + *

+ * If {@link Observer#onError} is invoked the source Observable will be re-subscribed to. + *

+ * Any {@link Observer#onNext} calls received on each attempt will be emitted and concatenated together. + *

+ * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and + * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. + * @return Observable with retry logic. + */ + def retry: Observable[T] = { + Observable[T](asJava.retry()) + } + /** * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking * operators). @@ -1756,9 +1820,6 @@ object Observable { def just[T](value: T): Observable[T] = { Observable[T](JObservable.just(value)) } - - // TODO we have merge and concat (++) as binary instance methods, but do we also need them as - // static methods with arity > 2? /** * This behaves like {@link #merge(java.util.List)} except that if any of the merged Observables diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala index a3fa2345bc..d2a0cdcbcb 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala @@ -7,41 +7,51 @@ object Olympics { case class Medal(val year: Int, val games: String, val discipline: String, val medal: String, val athlete: String, val country: String) def mountainBikeMedals: Observable[Medal] = Observable( - Medal(1996, "Atlanta 1996", "cross-country men", "Gold", "Bart BRENTJENS", "Netherlands"), - Medal(1996, "Atlanta 1996", "cross-country women", "Gold", "Paola PEZZO", "Italy"), - Medal(1996, "Atlanta 1996", "cross-country men", "Silver", "Thomas FRISCHKNECHT", "Switzerland"), - Medal(1996, "Atlanta 1996", "cross-country women", "Silver", "Alison SYDOR", "Canada"), - Medal(1996, "Atlanta 1996", "cross-country men", "Bronze", "Miguel MARTINEZ", "France"), - Medal(1996, "Atlanta 1996", "cross-country women", "Bronze", "Susan DEMATTEI", "United States of America") - ) ++ fourYearsEmpty ++ Observable( - Medal(2000, "Sydney 2000", "cross-country women", "Gold", "Paola PEZZO", "Italy"), - Medal(2000, "Sydney 2000", "cross-country women", "Silver", "Barbara BLATTER", "Switzerland"), - Medal(2000, "Sydney 2000", "cross-country women", "Bronze", "Marga FULLANA", "Spain"), - Medal(2000, "Sydney 2000", "cross-country men", "Gold", "Miguel MARTINEZ", "France"), - Medal(2000, "Sydney 2000", "cross-country men", "Silver", "Filip MEIRHAEGHE", "Belgium"), - Medal(2000, "Sydney 2000", "cross-country men", "Bronze", "Christoph SAUSER", "Switzerland") - ) ++ fourYearsEmpty ++ Observable( - Medal(2004, "Athens 2004", "cross-country men", "Gold", "Julien ABSALON", "France"), - Medal(2004, "Athens 2004", "cross-country men", "Silver", "Jose Antonio HERMIDA RAMOS", "Spain"), - Medal(2004, "Athens 2004", "cross-country men", "Bronze", "Bart BRENTJENS", "Netherlands"), - Medal(2004, "Athens 2004", "cross-country women", "Gold", "Gunn-Rita DAHLE", "Norway"), - Medal(2004, "Athens 2004", "cross-country women", "Silver", "Marie-Helene PREMONT", "Canada"), - Medal(2004, "Athens 2004", "cross-country women", "Bronze", "Sabine SPITZ", "Germany") - ) ++ fourYearsEmpty ++ Observable( - Medal(2008, "Beijing 2008", "cross-country women", "Gold", "Sabine SPITZ", "Germany"), - Medal(2008, "Beijing 2008", "cross-country women", "Silver", "Maja WLOSZCZOWSKA", "Poland"), - Medal(2008, "Beijing 2008", "cross-country women", "Bronze", "Irina KALENTYEVA", "Russian Federation"), - Medal(2008, "Beijing 2008", "cross-country men", "Gold", "Julien ABSALON", "France"), - Medal(2008, "Beijing 2008", "cross-country men", "Silver", "Jean-Christophe PERAUD", "France"), - Medal(2008, "Beijing 2008", "cross-country men", "Bronze", "Nino SCHURTER", "Switzerland") - ) ++ fourYearsEmpty ++ Observable( - Medal(2012, "London 2012", "cross-country men", "Gold", "Jaroslav KULHAVY", "Czech Republic"), - Medal(2012, "London 2012", "cross-country men", "Silver", "Nino SCHURTER", "Switzerland"), - Medal(2012, "London 2012", "cross-country men", "Bronze", "Marco Aurelio FONTANA", "Italy"), - Medal(2012, "London 2012", "cross-country women", "Gold", "Julie BRESSET", "France"), - Medal(2012, "London 2012", "cross-country women", "Silver", "Sabine SPITZ", "Germany"), - Medal(2012, "London 2012", "cross-country women", "Bronze", "Georgia GOULD", "United States of America") - ) + Observable( + Medal(1996, "Atlanta 1996", "cross-country men", "Gold", "Bart BRENTJENS", "Netherlands"), + Medal(1996, "Atlanta 1996", "cross-country women", "Gold", "Paola PEZZO", "Italy"), + Medal(1996, "Atlanta 1996", "cross-country men", "Silver", "Thomas FRISCHKNECHT", "Switzerland"), + Medal(1996, "Atlanta 1996", "cross-country women", "Silver", "Alison SYDOR", "Canada"), + Medal(1996, "Atlanta 1996", "cross-country men", "Bronze", "Miguel MARTINEZ", "France"), + Medal(1996, "Atlanta 1996", "cross-country women", "Bronze", "Susan DEMATTEI", "United States of America") + ), + fourYearsEmpty, + Observable( + Medal(2000, "Sydney 2000", "cross-country women", "Gold", "Paola PEZZO", "Italy"), + Medal(2000, "Sydney 2000", "cross-country women", "Silver", "Barbara BLATTER", "Switzerland"), + Medal(2000, "Sydney 2000", "cross-country women", "Bronze", "Marga FULLANA", "Spain"), + Medal(2000, "Sydney 2000", "cross-country men", "Gold", "Miguel MARTINEZ", "France"), + Medal(2000, "Sydney 2000", "cross-country men", "Silver", "Filip MEIRHAEGHE", "Belgium"), + Medal(2000, "Sydney 2000", "cross-country men", "Bronze", "Christoph SAUSER", "Switzerland") + ), + fourYearsEmpty, + Observable( + Medal(2004, "Athens 2004", "cross-country men", "Gold", "Julien ABSALON", "France"), + Medal(2004, "Athens 2004", "cross-country men", "Silver", "Jose Antonio HERMIDA RAMOS", "Spain"), + Medal(2004, "Athens 2004", "cross-country men", "Bronze", "Bart BRENTJENS", "Netherlands"), + Medal(2004, "Athens 2004", "cross-country women", "Gold", "Gunn-Rita DAHLE", "Norway"), + Medal(2004, "Athens 2004", "cross-country women", "Silver", "Marie-Helene PREMONT", "Canada"), + Medal(2004, "Athens 2004", "cross-country women", "Bronze", "Sabine SPITZ", "Germany") + ), + fourYearsEmpty, + Observable( + Medal(2008, "Beijing 2008", "cross-country women", "Gold", "Sabine SPITZ", "Germany"), + Medal(2008, "Beijing 2008", "cross-country women", "Silver", "Maja WLOSZCZOWSKA", "Poland"), + Medal(2008, "Beijing 2008", "cross-country women", "Bronze", "Irina KALENTYEVA", "Russian Federation"), + Medal(2008, "Beijing 2008", "cross-country men", "Gold", "Julien ABSALON", "France"), + Medal(2008, "Beijing 2008", "cross-country men", "Silver", "Jean-Christophe PERAUD", "France"), + Medal(2008, "Beijing 2008", "cross-country men", "Bronze", "Nino SCHURTER", "Switzerland") + ), + fourYearsEmpty, + Observable( + Medal(2012, "London 2012", "cross-country men", "Gold", "Jaroslav KULHAVY", "Czech Republic"), + Medal(2012, "London 2012", "cross-country men", "Silver", "Nino SCHURTER", "Switzerland"), + Medal(2012, "London 2012", "cross-country men", "Bronze", "Marco Aurelio FONTANA", "Italy"), + Medal(2012, "London 2012", "cross-country women", "Gold", "Julie BRESSET", "France"), + Medal(2012, "London 2012", "cross-country women", "Silver", "Sabine SPITZ", "Germany"), + Medal(2012, "London 2012", "cross-country women", "Bronze", "Georgia GOULD", "United States of America") + ) + ).concat // speed it up :D val fourYears = 4000.millis diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index 73e3c9c7ea..5214e2659c 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -107,19 +107,19 @@ class RxScalaDemo extends JUnitSuite { Observable.interval(period.millis).map(n => s"Obs-$period emits $n") } - @Test def mergeManyExample() { + @Test def flattenManyExample() { val o = Observable.interval(500 millis).map(n => myInterval((n+1)*100)) val stopper = Observable.interval(5 seconds) - o.merge.takeUntil(stopper).toBlockingObservable.foreach(println(_)) + o.flatten.takeUntil(stopper).toBlockingObservable.foreach(println(_)) } - @Test def mergeSomeExample() { + @Test def fattenSomeExample() { // To merge some observables which are all known already: Observable( Observable.interval(200 millis), Observable.interval(400 millis), Observable.interval(800 millis) - ).merge.take(12).toBlockingObservable.foreach(println(_)) + ).flatten.take(12).toBlockingObservable.foreach(println(_)) } @Test def rangeAndBufferExample() { @@ -205,7 +205,7 @@ class RxScalaDemo extends JUnitSuite { println("Observable for modulo" + modulo + " started") if (firstOnly) numbers.take(1) else numbers - }).merge.toBlockingObservable.foreach(println(_)) + }).flatten.toBlockingObservable.foreach(println(_)) } @Test def timingTest1() { @@ -217,7 +217,7 @@ class RxScalaDemo extends JUnitSuite { println("Observable for modulo" + modulo + " started at t = " + (System.currentTimeMillis - t0)) numbers.take(1) // <- TODO very unexpected //numbers - }).merge.toBlockingObservable.foreach(println(_)) + }).flatten.toBlockingObservable.foreach(println(_)) } @Test def groupByExample() { diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index 78aa70ccae..48820c5bea 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -13,6 +13,8 @@ class CompletenessTest extends JUnitSuite { val unnecessary = "[considered unnecessary in Scala land]" + val deprecated = "[deprecated in RxJava]" + val averageProblem = "[We can't have a general average method because Scala's Numeric does not have " + "scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum). " + "You can use fold instead to accumulate sum and numberOfElements and divide at the end.]" @@ -26,6 +28,7 @@ class CompletenessTest extends JUnitSuite { "all(Func1[_ >: T, Boolean])" -> "forall(T => Boolean)", "buffer(Long, Long, TimeUnit)" -> "buffer(Duration, Duration)", "buffer(Long, Long, TimeUnit, Scheduler)" -> "buffer(Duration, Duration, Scheduler)", + "count()" -> "length", "dematerialize()" -> "dematerialize(<:<[T, Notification[U]])", "first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, "firstOrDefault(T)" -> "firstOrElse(=> U)", @@ -61,13 +64,14 @@ class CompletenessTest extends JUnitSuite { "averageFloats(Observable[Float])" -> averageProblem, "averageLongs(Observable[Long])" -> averageProblem, "create(OnSubscribeFunc[T])" -> "apply(Observer[T] => Subscription)", + "concat(Observable[_ <: Observable[_ <: T]])" -> "concat(<:<[Observable[T], Observable[Observable[U]]])", "defer(Func0[_ <: Observable[_ <: T]])" -> "defer(=> Observable[T])", "empty()" -> "apply(T*)", "error(Throwable)" -> "apply(Throwable)", "from(Array[T])" -> "apply(T*)", "from(Iterable[_ <: T])" -> "apply(T*)", "merge(Observable[_ <: T], Observable[_ <: T])" -> "merge(Observable[U])", - "merge(Observable[_ <: Observable[_ <: T]])" -> "merge(<:<[Observable[T], Observable[Observable[U]]])", + "merge(Observable[_ <: Observable[_ <: T]])" -> "flatten(<:<[Observable[T], Observable[Observable[U]]])", "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[T])", "range(Int, Int)" -> "apply(Range)", "sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use (first zip second) map (p => p._1 == p._2)]", @@ -77,7 +81,8 @@ class CompletenessTest extends JUnitSuite { "sumFloats(Observable[Float])" -> "sum(Numeric[U])", "sumLongs(Observable[Long])" -> "sum(Numeric[U])", "synchronize(Observable[T])" -> "synchronize", - "switchDo(Observable[_ <: Observable[_ <: T]])" -> "switch", + "switchDo(Observable[_ <: Observable[_ <: T]])" -> deprecated, + "switchOnNext(Observable[_ <: Observable[_ <: T]])" -> "switch(<:<[Observable[T], Observable[Observable[U]]])", "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method zip and map]", "zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use zip in companion object and map]", "zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use zip in companion object and map]" @@ -86,7 +91,7 @@ class CompletenessTest extends JUnitSuite { "startWith(" + _ + ")" -> "[unnecessary because we can just use ++ instead]" ).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( // concat 2-9 - "concat(" + _ + ")" -> "[unnecessary because we can use ++ instead]" + "concat(" + _ + ")" -> "[unnecessary because we can use ++ instead or Observable(o1, o2, ...).concat]" ).drop(1).toMap ++ List.iterate("T", 10)(s => s + ", T").map( // all 10 overloads of from: "from(" + _ + ")" -> "apply(T*)" @@ -97,7 +102,7 @@ class CompletenessTest extends JUnitSuite { ("zip(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", unnecessary) }).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( // merge 3-9: - "merge(" + _ + ")" -> "[unnecessary because we can use Observable(o1, o2, ...).merge instead]" + "merge(" + _ + ")" -> "[unnecessary because we can use Observable(o1, o2, ...).flatten instead]" ).drop(2).toMap def removePackage(s: String) = s.replaceAll("(\\w+\\.)+(\\w+)", "$2") From 55a552a7d4b70a40de78684999a68097a83a6628 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 20 Sep 2013 12:35:06 +0200 Subject: [PATCH 049/333] add `parallel` and example --- .../main/scala/rx/lang/scala/Observable.scala | 28 +++++++++++++++++++ .../rx/lang/scala/examples/RxScalaDemo.scala | 22 +++++++++++++++ .../rx/lang/scala/CompletenessTest.scala | 4 ++- 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index cd22eee5bb..9cc1e71695 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1674,6 +1674,34 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) def toBlockingObservable: BlockingObservable[T] = { new BlockingObservable[T](asJava.toBlockingObservable()) } + + /** + * Perform work in parallel by sharding an {@code Observable} on a {@link Schedulers#threadPoolForComputation()} {@link Scheduler} and return an {@code Observable} with the output. + * + * @param f + * a {@link Func1} that applies Observable operators to {@code Observable} in parallel and returns an {@code Observable} + * @return an Observable with the output of the function executed on a {@link Scheduler} + */ + def parallel[R](f: Observable[T] => Observable[R]): Observable[R] = { + val fJava: Func1[rx.Observable[T], rx.Observable[R]] = + (jo: rx.Observable[T]) => f(Observable[T](jo)).asJava.asInstanceOf[rx.Observable[R]] + Observable[R](asJava.asInstanceOf[rx.Observable[T]].parallel[R](fJava)) + } + + /** + * Perform work in parallel by sharding an {@code Observable} on a {@link Scheduler} and return an {@code Observable} with the output. + * + * @param f + * a {@link Func1} that applies Observable operators to {@code Observable} in parallel and returns an {@code Observable} + * @param s + * a {@link Scheduler} to perform the work on. + * @return an Observable with the output of the {@link Func1} executed on a {@link Scheduler} + */ + def parallel[R](f: Observable[T] => Observable[R], scheduler: Scheduler): Observable[R] = { + val fJava: Func1[rx.Observable[T], rx.Observable[R]] = + (jo: rx.Observable[T]) => f(Observable[T](jo)).asJava.asInstanceOf[rx.Observable[R]] + Observable[R](asJava.asInstanceOf[rx.Observable[T]].parallel[R](fJava, scheduler)) + } def withFilter(p: T => Boolean): WithFilter[T] = { new WithFilter[T](p, asJava) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index 5214e2659c..2ddc53df47 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -346,6 +346,28 @@ class RxScalaDemo extends JUnitSuite { assertEquals(10, Observable(-1, 0, 1).filter(condition).firstOrElse(10).toBlockingObservable.single) } + def square(x: Int): Int = { + println(s"$x*$x is being calculated on thread ${Thread.currentThread().getId()}") + Thread.sleep(100) // calculating a square is heavy work :) + x*x + } + + def work(o1: Observable[Int]): Observable[String] = { + println(s"map() is being called on thread ${Thread.currentThread().getId()}") + o1.map(i => s"The square of $i is ${square(i)}") + } + + @Test def parallelExample() { + val t0 = System.currentTimeMillis() + Observable(1 to 10).parallel(work(_)).toBlockingObservable.foreach(println(_)) + println(s"Work took ${System.currentTimeMillis()-t0} ms") + } + + @Test def exampleWithoutParallel() { + val t0 = System.currentTimeMillis() + work(Observable(1 to 10)).toBlockingObservable.foreach(println(_)) + println(s"Work took ${System.currentTimeMillis()-t0} ms") + } def output(s: String): Unit = println(s) diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index 48820c5bea..25b550e375 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -40,6 +40,8 @@ class CompletenessTest extends JUnitSuite { "onErrorResumeNext(Observable[_ <: T])" -> "onErrorResumeNext(Observable[U])", "onErrorReturn(Func1[Throwable, _ <: T])" -> "onErrorReturn(Throwable => U)", "onExceptionResumeNext(Observable[_ <: T])" -> "onExceptionResumeNext(Observable[U])", + "parallel(Func1[Observable[T], Observable[R]])" -> "parallel(Observable[T] => Observable[R])", + "parallel(Func1[Observable[T], Observable[R]], Scheduler)" -> "parallel(Observable[T] => Observable[R], Scheduler)", "reduce(Func2[T, T, T])" -> "reduce((U, U) => U)", "reduce(R, Func2[R, _ >: T, R])" -> "fold(R)((R, T) => R)", "scan(Func2[T, T, T])" -> unnecessary, @@ -169,7 +171,7 @@ class CompletenessTest extends JUnitSuite { def javaMethodSignatureToScala(s: String): String = { s.replaceAllLiterally("Long, TimeUnit", "Duration") .replaceAll("Action0", "() => Unit") - // nested [] can't be parsed with regex, but we still do it, please forgive us ;-) + // nested [] can't be parsed with regex, so these will have to be added manually .replaceAll("Action1\\[([^]]*)\\]", "$1 => Unit") .replaceAll("Action2\\[([^]]*), ([^]]*)\\]", "($1, $2) => Unit") .replaceAll("Func0\\[([^]]*)\\]", "() => $1") From b4019b9f7b8c23f66c1c0d221f1da8f3298fbb69 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 20 Sep 2013 13:29:48 +0200 Subject: [PATCH 050/333] add mergeDelayError and flattenDelayError --- .../main/scala/rx/lang/scala/Observable.scala | 48 +++++++++++++++++++ .../rx/lang/scala/CompletenessTest.scala | 6 ++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 9cc1e71695..0b5dff61dd 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1271,6 +1271,29 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) Observable[U](rx.Observable.merge(thisJava, thatJava)) } + /** + * This behaves like {@link #merge(Observable)} except that if any of the merged Observables + * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will + * refrain from propagating that error notification until all of the merged Observables have + * finished emitting items. + *

+ * + *

+ * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its + * Observers once. + *

+ * This method allows an Observer to receive all successfully emitted items from all of the + * source Observables without being interrupted by an error notification from one of them. + * + * @param that + * an Observable to be merged + * @return an Observable that emits items that are the result of flattening the items emitted by + * {$code this} and {$code that} + */ + def mergeDelayError[U >: T](that: Observable[U]): Observable[U] = { + Observable[U](rx.Observable.mergeDelayError[U](this.asJava, that.asJava)) + } + /** * Flattens the sequence of Observables emitted by {@code this} into one Observable, without any * transformation. @@ -1291,6 +1314,31 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) Observable[U](o5) } + /** + * This behaves like {@link #flatten(<:<)} except that if any of the merged Observables + * notify of an error via {@link Observer#onError onError}, this method will + * refrain from propagating that error notification until all of the merged Observables have + * finished emitting items. + *

+ * + *

+ * Even if multiple merged Observables send {@code onError} notifications, this method will only invoke the {@code onError} method of its + * Observers once. + *

+ * This method allows an Observer to receive all successfully emitted items from all of the + * source Observables without being interrupted by an error notification from one of them. + * + * @return an Observable that emits items that are the result of flattening the items emitted by + * the Observables emitted by the this Observable + */ + def flattenDelayError[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { + val o2: Observable[Observable[U]] = this + val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) + val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava + val o5 = rx.Observable.mergeDelayError[U](o4) + Observable[U](o5) + } + /** * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. *

diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index 25b550e375..932d7edc9b 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -74,7 +74,8 @@ class CompletenessTest extends JUnitSuite { "from(Iterable[_ <: T])" -> "apply(T*)", "merge(Observable[_ <: T], Observable[_ <: T])" -> "merge(Observable[U])", "merge(Observable[_ <: Observable[_ <: T]])" -> "flatten(<:<[Observable[T], Observable[Observable[U]]])", - "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[T])", + "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[U])", + "mergeDelayError(Observable[_ <: Observable[_ <: T]])" -> "flattenDelayError(<:<[Observable[T], Observable[Observable[U]]])", "range(Int, Int)" -> "apply(Range)", "sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use (first zip second) map (p => p._1 == p._2)]", "sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> "[use (first zip second) map (p => equality(p._1, p._2))]", @@ -105,6 +106,9 @@ class CompletenessTest extends JUnitSuite { }).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( // merge 3-9: "merge(" + _ + ")" -> "[unnecessary because we can use Observable(o1, o2, ...).flatten instead]" + ).drop(2).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( + // mergeDelayError 3-9: + "mergeDelayError(" + _ + ")" -> "[unnecessary because we can use Observable(o1, o2, ...).flattenDelayError instead]" ).drop(2).toMap def removePackage(s: String) = s.replaceAll("(\\w+\\.)+(\\w+)", "$2") From 4e31815a18a7463d4650a37b31ae042c1e75818d Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 20 Sep 2013 14:13:57 +0200 Subject: [PATCH 051/333] add combineLatest, apply(java Future), remove docu without impl --- .../main/scala/rx/lang/scala/Observable.scala | 139 ++++-------------- .../rx/lang/scala/CompletenessTest.scala | 11 +- 2 files changed, 35 insertions(+), 115 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 0b5dff61dd..8ff0ecd437 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -17,11 +17,6 @@ package rx.lang.scala -import org.scalatest.junit.JUnitSuite -import scala.collection.Seq -import rx.lang.scala.observables.BlockingObservable - - /** * The Observable interface that implements the Reactive Pattern. */ @@ -38,6 +33,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) import rx.lang.scala.{Notification, Subscription, Scheduler, Observer} import rx.lang.scala.util._ import rx.lang.scala.subjects.Subject + import rx.lang.scala.observables.BlockingObservable import rx.lang.scala.ImplicitFunctionConversions._ /** @@ -1149,17 +1145,10 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) * @return an Observable that emits items from the source Observable so long as the predicate * continues to return true for each item, then completes */ - // TODO: if we have zipWithIndex, takeWhileWithIndex is not needed any more def takeWhileWithIndex(predicate: (T, Integer) => Boolean): Observable[T] = { Observable[T](asJava.takeWhileWithIndex(predicate)) } - /* TODO zipWithIndex once it's in RxJava - def zipWithIndex: Observable[(T, Int)] = { - ??? - } - */ - /** * Returns an Observable that emits only the last count items emitted by the source * Observable. @@ -1339,6 +1328,20 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) Observable[U](o5) } + /** + * Combines two observables, emitting a pair of the latest values of each of + * the source observables each time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + * + * @param that + * The second source observable. + * @return An Observable that combines the source Observables + */ + def combineLatest[U](that: Observable[U]): Observable[(T, U)] = { + val f: Func2[_ >: T, _ >: U, _ <: (T, U)] = (t: T, u: U) => (t, u) + Observable[(T, U)](rx.Observable.combineLatest[T, U, (T, U)](this.asJava, that.asJava, f)) + } + /** * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. *

@@ -1761,7 +1764,6 @@ object Observable { import scala.collection.JavaConverters._ import scala.collection.immutable.Range import scala.concurrent.duration.Duration - import scala.concurrent.Future import rx.{Observable => JObservable} import rx.lang.scala.{Notification, Subscription, Scheduler, Observer} import rx.lang.scala.util._ @@ -1896,75 +1898,6 @@ object Observable { def just[T](value: T): Observable[T] = { Observable[T](JObservable.just(value)) } - - /** - * This behaves like {@link #merge(java.util.List)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param source - * a list of Observables - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} list of Observables - * @see MSDN: Observable.Merge Method - */ - // public static Observable mergeDelayError(List> source) - // TODO decide if instance method mergeWithDelayError (?) - - /** - * This behaves like {@link #merge(Observable)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param source - * an Observable that emits Observables - * @return an Observable that emits items that are the result of flattening the items emitted by - * the Observables emitted by the {@code source} Observable - * @see MSDN: Observable.Merge Method - */ - // public static Observable mergeDelayError(Observable> source) - // TODO decide if instance method mergeWithDelayError (?) - - /** - * This behaves like {@link #merge(Observable...)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - *

- * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

- * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param source - * a series of Observables - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - // public static Observable mergeDelayError(Observable... source) - // TODO decide if instance method mergeWithDelayError (?) /** * Returns an Observable that never sends any items or notifications to an {@link Observer}. @@ -1979,45 +1912,23 @@ object Observable { Observable[Nothing](JObservable.never()) } - /* + // TODO also support Scala Futures, but think well before. Do we want to Future and Observable + // to share a common base interface? + + // private because it's not RxScala's responsability to provide this alias + private type Future[+T] = java.util.concurrent.Future[_ <: T] + def apply[T](f: Future[T]): Observable[T] = { - ??? // TODO convert Scala Future to Java Future + Observable[T](rx.Observable.from(f)) } - */ - /* def apply[T](f: Future[T], scheduler: Scheduler): Observable[T] = { - ??? // TODO convert Scala Future to Java Future + Observable[T](rx.Observable.from(f, scheduler)) } - */ - /* def apply[T](f: Future[T], duration: Duration): Observable[T] = { - ??? // TODO convert Scala Future to Java Future + Observable[T](rx.Observable.from(f, duration.length, duration.unit)) } - */ - - /** - * Combines the given observables, emitting an event containing an aggregation of the latest values of each of the source observables - * each time an event is received from one of the source observables, where the aggregation is defined by the given function. - *

- * - * - * @param o1 - * The first source observable. - * @param o2 - * The second source observable. - * @param combineFunction - * The aggregation function used to combine the source observable values. - * @return An Observable that combines the source Observables with the given combine function - */ - // public static Observable combineLatest(Observable o1, Observable o2, Func2 combineFunction) - // TODO do we want this as an instance method? - // TODO then decide about combineLatest with > 2 Observables - - // TODO what about these two? - // public static Observable zip(Observable> ws, final FuncN zipFunction) - // public static Observable zip(Collection> ws, FuncN zipFunction) /** * Given a Seq of N observables, returns an observable that emits Seqs of N elements each. @@ -2088,7 +1999,7 @@ class WithFilter[+T] private[scala] (p: T => Boolean, asJava: rx.Observable[_ <: // there is no foreach here, that's only available on BlockingObservable } -class UnitTestSuite extends JUnitSuite { +class UnitTestSuite extends org.scalatest.junit.JUnitSuite { import scala.concurrent.duration._ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index 932d7edc9b..0fc97a5182 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -66,12 +66,16 @@ class CompletenessTest extends JUnitSuite { "averageFloats(Observable[Float])" -> averageProblem, "averageLongs(Observable[Long])" -> averageProblem, "create(OnSubscribeFunc[T])" -> "apply(Observer[T] => Subscription)", + "combineLatest(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "combineLatest(Observable[U])", "concat(Observable[_ <: Observable[_ <: T]])" -> "concat(<:<[Observable[T], Observable[Observable[U]]])", "defer(Func0[_ <: Observable[_ <: T]])" -> "defer(=> Observable[T])", "empty()" -> "apply(T*)", "error(Throwable)" -> "apply(Throwable)", "from(Array[T])" -> "apply(T*)", "from(Iterable[_ <: T])" -> "apply(T*)", + "from(Future[_ <: T])" -> "apply(Future[T])", + "from(Future[_ <: T], Long, TimeUnit)" -> "apply(Future[T], Duration)", + "from(Future[_ <: T], Scheduler)" -> "apply(Future[T], Scheduler)", "merge(Observable[_ <: T], Observable[_ <: T])" -> "merge(Observable[U])", "merge(Observable[_ <: Observable[_ <: T]])" -> "flatten(<:<[Observable[T], Observable[Observable[U]]])", "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[U])", @@ -109,7 +113,12 @@ class CompletenessTest extends JUnitSuite { ).drop(2).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( // mergeDelayError 3-9: "mergeDelayError(" + _ + ")" -> "[unnecessary because we can use Observable(o1, o2, ...).flattenDelayError instead]" - ).drop(2).toMap + ).drop(2).toMap ++ (3 to 9).map(i => { + // combineLatest 3-9: + val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") + val funcParams = (1 to i).map(j => s"_ >: T$j, ").mkString("") + ("combineLatest(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", "[If C# doesn't need it, Scala doesn't need it either ;-)]") + }).toMap def removePackage(s: String) = s.replaceAll("(\\w+\\.)+(\\w+)", "$2") From cff6ae19399d07db93d00492b98533c26d0bf6a4 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 20 Sep 2013 14:28:40 +0200 Subject: [PATCH 052/333] update Scala TODO list --- language-adaptors/rxjava-scala/TODO.md | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/language-adaptors/rxjava-scala/TODO.md b/language-adaptors/rxjava-scala/TODO.md index 4b8bcab64e..017c3be381 100644 --- a/language-adaptors/rxjava-scala/TODO.md +++ b/language-adaptors/rxjava-scala/TODO.md @@ -4,17 +4,14 @@ TODOs for Scala Adapter This is a (probably incomplete) list of what still needs to be done in the Scala adaptor: -- [ ] ConnectableObservable: Implement adaptor. Note that it cannot extend Scala Observable, since value classes are final. -- [ ] more methods of BlockingObservable -- [ ] multicast, publish, replay once we have ConnectableObservable -- [ ] groupBy and GroupedObservable -- [ ] mirror complete Java package structure in Scala -- [ ] convert Java futures to Scala futures -- [ ] Add methods present in Scala collections library, but not in RxJava, e.g. zipWithIndex, aggregate à la Scala -- [ ] mergeDelayError, combineLatest, merge, concat, zip: decide if instance method or static or both, decide about arities > 2 -- [ ] naming: switch() or switchOnNext()? -- [ ] decide where the MovieLib/MovieLibUsage (use Scala code from Java code) example should live and make sure gradle builds it in the right order -- [ ] Avoid text duplication in scaladoc using templates, add examples, distinction between use case signature and full signature -- [ ] other small TODOs +* mirror complete Java package structure in Scala +* objects for classes with static methods or singletons (e.g. Schedulers, Subscriptions) +* Notification as a case class +* integrating Scala Futures, should there be a common base interface for Futures and Observables? +* Add methods present in Scala collections library, but not in RxJava, e.g. aggregate à la Scala, collect, exists, tails, ... +* combineLatest with about arities > 2 +* decide where the MovieLib/MovieLibUsage (use Scala code from Java code) example should live and make sure gradle builds it in the right order +* Avoid text duplication in scaladoc using templates, add examples, distinction between use case signature and full signature +* other small TODOs From 51c544d1a762b33e038c94fd4f4ea4224da7b624 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 20 Sep 2013 17:43:54 +0200 Subject: [PATCH 053/333] add markdown export to CompletenessTest --- .../rx/lang/scala/examples/RxScalaDemo.scala | 6 ++ .../rx/lang/scala/CompletenessTest.scala | 96 +++++++++++++++---- 2 files changed, 83 insertions(+), 19 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index 2ddc53df47..05c328d2dc 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -369,6 +369,12 @@ class RxScalaDemo extends JUnitSuite { println(s"Work took ${System.currentTimeMillis()-t0} ms") } + @Test def toSortedList() { + assertEquals(Seq(7, 8, 9, 10), Observable(10, 7, 8, 9).toSeq.map(_.sorted).toBlockingObservable.single) + val f = (a: Int, b: Int) => b < a + assertEquals(Seq(10, 9, 8, 7), Observable(10, 7, 8, 9).toSeq.map(_.sortWith(f)).toBlockingObservable.single) + } + def output(s: String): Unit = println(s) // blocks until obs has completed diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index 0fc97a5182..60a2f3768f 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -8,6 +8,8 @@ import scala.collection.SortedSet import scala.collection.SortedMap import org.junit.Ignore import java.lang.reflect.Modifier +import java.util.Date +import java.util.Calendar class CompletenessTest extends JUnitSuite { @@ -15,11 +17,11 @@ class CompletenessTest extends JUnitSuite { val deprecated = "[deprecated in RxJava]" - val averageProblem = "[We can't have a general average method because Scala's Numeric does not have " + - "scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum). " + - "You can use fold instead to accumulate sum and numberOfElements and divide at the end.]" + val averageProblem = "[We can't have a general average method because Scala's `Numeric` does not have " + + "scalar multiplication (we would need to calculate `(1.0/numberOfElements)*sum`). " + + "You can use `fold` instead to accumulate `sum` and `numberOfElements` and divide at the end.]" - val commentForFirstWithPredicate = "[use .filter(condition).first]" + val commentForFirstWithPredicate = "[use `.filter(condition).first`]" val correspondence = defaultMethodCorrespondence ++ Map( // manually added entries for Java instance methods @@ -32,10 +34,10 @@ class CompletenessTest extends JUnitSuite { "dematerialize()" -> "dematerialize(<:<[T, Notification[U]])", "first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, "firstOrDefault(T)" -> "firstOrElse(=> U)", - "firstOrDefault(Func1[_ >: T, Boolean], T)" -> "[use .filter(condition).firstOrElse(default)]", - "groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> "groupBy(T => K)", + "firstOrDefault(Func1[_ >: T, Boolean], T)" -> "[use `.filter(condition).firstOrElse(default)`]", + "groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> "[use `groupBy` and `map`]", "mapMany(Func1[_ >: T, _ <: Observable[_ <: R]])" -> "flatMap(T => Observable[R])", - "mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> "[combine zipWithIndex with map or with a for comprehension]", + "mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> "[combine `zipWithIndex` with `map` or with a for comprehension]", "onErrorResumeNext(Func1[Throwable, _ <: Observable[_ <: T]])" -> "onErrorResumeNext(Throwable => Observable[U])", "onErrorResumeNext(Observable[_ <: T])" -> "onErrorResumeNext(Observable[U])", "onErrorReturn(Func1[Throwable, _ <: T])" -> "onErrorReturn(Throwable => U)", @@ -49,13 +51,13 @@ class CompletenessTest extends JUnitSuite { "skip(Int)" -> "drop(Int)", "skipWhile(Func1[_ >: T, Boolean])" -> "dropWhile(T => Boolean)", "skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary, - "startWith(Iterable[T])" -> "[unnecessary because we can just use ++ instead]", + "startWith(Iterable[T])" -> "[unnecessary because we can just use `++` instead]", "takeFirst()" -> "first", "takeFirst(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, "takeLast(Int)" -> "takeRight(Int)", "toList()" -> "toSeq", - "toSortedList()" -> unnecessary, - "toSortedList(Func2[_ >: T, _ >: T, Integer])" -> unnecessary, + "toSortedList()" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sorted)`]", + "toSortedList(Func2[_ >: T, _ >: T, Integer])" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sortWith(f))`]", "where(Func1[_ >: T, Boolean])" -> "filter(T => Boolean)", "window(Long, Long, TimeUnit)" -> "window(Duration, Duration)", "window(Long, Long, TimeUnit, Scheduler)" -> "window(Duration, Duration, Scheduler)", @@ -81,8 +83,8 @@ class CompletenessTest extends JUnitSuite { "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[U])", "mergeDelayError(Observable[_ <: Observable[_ <: T]])" -> "flattenDelayError(<:<[Observable[T], Observable[Observable[U]]])", "range(Int, Int)" -> "apply(Range)", - "sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use (first zip second) map (p => p._1 == p._2)]", - "sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> "[use (first zip second) map (p => equality(p._1, p._2))]", + "sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use `(first zip second) map (p => p._1 == p._2)`]", + "sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> "[use `(first zip second) map (p => equality(p._1, p._2))`]", "sum(Observable[Integer])" -> "sum(Numeric[U])", "sumDoubles(Observable[Double])" -> "sum(Numeric[U])", "sumFloats(Observable[Float])" -> "sum(Numeric[U])", @@ -90,15 +92,15 @@ class CompletenessTest extends JUnitSuite { "synchronize(Observable[T])" -> "synchronize", "switchDo(Observable[_ <: Observable[_ <: T]])" -> deprecated, "switchOnNext(Observable[_ <: Observable[_ <: T]])" -> "switch(<:<[Observable[T], Observable[Observable[U]]])", - "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method zip and map]", - "zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use zip in companion object and map]", - "zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use zip in companion object and map]" + "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method `zip` and `map`]", + "zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]", + "zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]" ) ++ List.iterate("T", 9)(s => s + ", T").map( // all 9 overloads of startWith: - "startWith(" + _ + ")" -> "[unnecessary because we can just use ++ instead]" + "startWith(" + _ + ")" -> "[unnecessary because we can just use `++` instead]" ).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( // concat 2-9 - "concat(" + _ + ")" -> "[unnecessary because we can use ++ instead or Observable(o1, o2, ...).concat]" + "concat(" + _ + ")" -> "[unnecessary because we can use `++` instead or `Observable(o1, o2, ...).concat`]" ).drop(1).toMap ++ List.iterate("T", 10)(s => s + ", T").map( // all 10 overloads of from: "from(" + _ + ")" -> "apply(T*)" @@ -109,10 +111,10 @@ class CompletenessTest extends JUnitSuite { ("zip(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", unnecessary) }).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( // merge 3-9: - "merge(" + _ + ")" -> "[unnecessary because we can use Observable(o1, o2, ...).flatten instead]" + "merge(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flatten` instead]" ).drop(2).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( // mergeDelayError 3-9: - "mergeDelayError(" + _ + ")" -> "[unnecessary because we can use Observable(o1, o2, ...).flattenDelayError instead]" + "mergeDelayError(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flattenDelayError` instead]" ).drop(2).toMap ++ (3 to 9).map(i => { // combineLatest 3-9: val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") @@ -257,6 +259,12 @@ class CompletenessTest extends JUnitSuite { val status = if (bad == 0) "SUCCESS" else "BAD" println(s"\n$status: $bad out of ${bad+good} methods were not found in Scala Observable") } + + def setTodoForMissingMethods(corresp: Map[String, String]): Map[String, String] = { + val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable[_]]).toSet + for ((javaM, scalaM) <- corresp) yield + (javaM, if (actualMethods.contains(scalaM) || scalaM.charAt(0) == '[') scalaM else "[**TODO: missing**]") + } @Test def checkJavaMethodPresence: Unit = { println("\nTesting that all mentioned Java methods exist") @@ -269,4 +277,54 @@ class CompletenessTest extends JUnitSuite { checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable[_]]) } + def scalaToJavaSignature(s: String) = + s.replaceAllLiterally("_ <:", "? extends") + .replaceAllLiterally("_ >:", "? super") + .replaceAllLiterally("[", "<") + .replaceAllLiterally("]", ">") + .replaceAllLiterally("Array", "T[]") + + def escapeJava(s: String) = + s.replaceAllLiterally("<", "<") + .replaceAllLiterally(">", ">") + + @Test def printMarkdownCorrespondenceTable() { + def isInteresting(p: (String, String)): Boolean = + p._1.replaceAllLiterally("()", "") != p._2 + def groupingKey(p: (String, String)): (String, String) = + (if (p._1.startsWith("average")) "average" else p._1.takeWhile(_ != '('), p._2) + def formatJavaCol(name: String, alternatives: Iterable[String]): String = { + alternatives.toList.sorted.map(scalaToJavaSignature(_)).map(s => { + if (s.length > 50) { + val toolTip = escapeJava(s) + "" + name + "(...)" + } else { + "`" + s + "`" + } + }).mkString("
") + } + def formatScalaCol(s: String): String = + if (s.startsWith("[") && s.endsWith("]")) s.drop(1).dropRight(1) else "`" + s + "`" + def escape(s: String) = s.replaceAllLiterally("[", "<").replaceAllLiterally("]", ">") + + println(""" +## Comparison of Scala Observable and Java Observable + +Note: +* This table contains both static methods and instance methods. +* If a signature is too long, move your mouse over it to get the full signature. + + +| Java Method | Scala Method | +|-------------|--------------|""") + + val ps = setTodoForMissingMethods(correspondence) + + (for (((javaName, scalaCol), pairs) <- ps.groupBy(groupingKey(_)).toList.sortBy(_._1._1)) yield { + "| " + formatJavaCol(javaName, pairs.map(_._1)) + " | " + formatScalaCol(scalaCol) + " |" + }).foreach(println(_)) + println(s"\nThis table was generated on ${Calendar.getInstance().getTime()}.") + println(s"**Do not edit**. Instead, edit `${getClass().getCanonicalName()}`.") + } + } From 3eb92fd07a12743fa44fc2f44a42f6e87018b761 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 20 Sep 2013 17:47:01 +0200 Subject: [PATCH 054/333] add Scala/Java Observable comparison --- language-adaptors/rxjava-scala/comparison.md | 146 ++++++++++++++++++ .../rx/lang/scala/CompletenessTest.scala | 3 +- 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 language-adaptors/rxjava-scala/comparison.md diff --git a/language-adaptors/rxjava-scala/comparison.md b/language-adaptors/rxjava-scala/comparison.md new file mode 100644 index 0000000000..786b63bae9 --- /dev/null +++ b/language-adaptors/rxjava-scala/comparison.md @@ -0,0 +1,146 @@ + +## Comparison of Scala Observable and Java Observable + +Note: +* This table contains both static methods and instance methods. +* If a signature is too long, move your mouse over it to get the full signature. + + +| Java Method | Scala Method | +|-------------|--------------| +| `aggregate(R, Func2)` | `fold(R)((R, T) => R)` | +| `aggregate(Func2)` | `reduce((U, U) => U)` | +| `all(Func1)` | `forall(T => Boolean)` | +| `average(Observable)`
`averageDoubles(Observable)`
`averageFloats(Observable)`
`averageLongs(Observable)` | We can't have a general average method because Scala's `Numeric` does not have scalar multiplication (we would need to calculate `(1.0/numberOfElements)*sum`). You can use `fold` instead to accumulate `sum` and `numberOfElements` and divide at the end. | +| `buffer(Int, Int)` | `buffer(Int, Int)` | +| `buffer(Long, TimeUnit, Int)` | `buffer(Duration, Int)` | +| `buffer(Long, Long, TimeUnit, Scheduler)` | `buffer(Duration, Duration, Scheduler)` | +| `buffer(Long, Long, TimeUnit)` | `buffer(Duration, Duration)` | +| buffer(...) | `buffer(Observable[Opening], Opening => Observable[Closing])` | +| buffer(...) | `buffer(() => Observable[Closing])` | +| `buffer(Long, TimeUnit)` | `buffer(Duration)` | +| `buffer(Long, TimeUnit, Int, Scheduler)` | `buffer(Duration, Int, Scheduler)` | +| `buffer(Int)` | `buffer(Int)` | +| `buffer(Long, TimeUnit, Scheduler)` | `buffer(Duration, Scheduler)` | +| `cache()` | `cache` | +| combineLatest(...) | `combineLatest(Observable[U])` | +| combineLatest(...)
combineLatest(...)
combineLatest(...)
combineLatest(...)
combineLatest(...)
combineLatest(...)
combineLatest(...) | If C# doesn't need it, Scala doesn't need it either ;-) | +| concat(...) | `concat(<:<[Observable[T], Observable[Observable[U]]])` | +| concat(...)
concat(...)
concat(...)
concat(...)
concat(...)
concat(...)
concat(...)
concat(...) | unnecessary because we can use `++` instead or `Observable(o1, o2, ...).concat` | +| `count()` | `length` | +| `create(OnSubscribeFunc)` | `apply(Observer[T] => Subscription)` | +| `debounce(Long, TimeUnit)` | `debounce(Duration)` | +| `debounce(Long, TimeUnit, Scheduler)` | `debounce(Duration, Scheduler)` | +| `defer(Func0>)` | `defer(=> Observable[T])` | +| `dematerialize()` | `dematerialize(<:<[T, Notification[U]])` | +| `distinct(Comparator)`
distinct(...) | **TODO: missing** | +| `distinct(Func1)` | `distinct(T => U)` | +| `distinct()` | `distinct` | +| `distinctUntilChanged()` | `distinctUntilChanged` | +| `distinctUntilChanged(Comparator)`
distinctUntilChanged(...) | **TODO: missing** | +| distinctUntilChanged(...) | `distinctUntilChanged(T => U)` | +| `empty()` | `apply(T*)` | +| `error(Throwable)` | `apply(Throwable)` | +| `filter(Func1)` | `filter(T => Boolean)` | +| `finallyDo(Action0)` | `finallyDo(() => Unit)` | +| `first()` | `first` | +| `first(Func1)` | use `.filter(condition).first` | +| `firstOrDefault(Func1, T)` | use `.filter(condition).firstOrElse(default)` | +| `firstOrDefault(T)` | `firstOrElse(=> U)` | +| flatMap(...) | `flatMap(T => Observable[R])` | +| `from(Future, Long, TimeUnit)` | `apply(Future[T], Duration)` | +| `from(Future)` | `apply(Future[T])` | +| `from(Future, Scheduler)` | `apply(Future[T], Scheduler)` | +| `from(T[])`
`from(Iterable)`
`from(T)`
`from(T, T)`
`from(T, T, T)`
`from(T, T, T, T)`
`from(T, T, T, T, T)`
`from(T, T, T, T, T, T)`
`from(T, T, T, T, T, T, T)`
`from(T, T, T, T, T, T, T, T)`
`from(T, T, T, T, T, T, T, T, T)`
`from(T, T, T, T, T, T, T, T, T, T)` | `apply(T*)` | +| `groupBy(Func1)` | `groupBy(T => K)` | +| groupBy(...) | use `groupBy` and `map` | +| `interval(Long, TimeUnit)` | `interval(Duration)` | +| `interval(Long, TimeUnit, Scheduler)` | `interval(Duration, Scheduler)` | +| `just(T)` | `just(T)` | +| `map(Func1)` | `map(T => R)` | +| mapMany(...) | `flatMap(T => Observable[R])` | +| mapWithIndex(...) | combine `zipWithIndex` with `map` or with a for comprehension | +| `materialize()` | `materialize` | +| merge(...)
merge(...)
merge(...)
merge(...)
merge(...)
merge(...)
merge(...) | unnecessary because we can use `Observable(o1, o2, ...).flatten` instead | +| merge(...) | `flatten(<:<[Observable[T], Observable[Observable[U]]])` | +| merge(...) | `merge(Observable[U])` | +| mergeDelayError(...) | `flattenDelayError(<:<[Observable[T], Observable[Observable[U]]])` | +| mergeDelayError(...)
mergeDelayError(...)
mergeDelayError(...)
mergeDelayError(...)
mergeDelayError(...)
mergeDelayError(...)
mergeDelayError(...) | unnecessary because we can use `Observable(o1, o2, ...).flattenDelayError` instead | +| mergeDelayError(...) | `mergeDelayError(Observable[U])` | +| `multicast(Subject)` | `multicast(Subject[T, R])` | +| `never()` | `never` | +| `observeOn(Scheduler)` | `observeOn(Scheduler)` | +| onErrorResumeNext(...) | `onErrorResumeNext(Throwable => Observable[U])` | +| `onErrorResumeNext(Observable)` | `onErrorResumeNext(Observable[U])` | +| `onErrorReturn(Func1)` | `onErrorReturn(Throwable => U)` | +| `onExceptionResumeNext(Observable)` | `onExceptionResumeNext(Observable[U])` | +| `parallel(Func1, Observable>)` | `parallel(Observable[T] => Observable[R])` | +| parallel(...) | `parallel(Observable[T] => Observable[R], Scheduler)` | +| `publish()` | `publish` | +| `range(Int, Int)` | `apply(Range)` | +| `reduce(Func2)` | `reduce((U, U) => U)` | +| `reduce(R, Func2)` | `fold(R)((R, T) => R)` | +| `replay()` | `replay` | +| `retry(Int)` | `retry(Int)` | +| `retry()` | `retry` | +| `sample(Long, TimeUnit)` | `sample(Duration)` | +| `sample(Long, TimeUnit, Scheduler)` | `sample(Duration, Scheduler)` | +| `scan(Func2)` | considered unnecessary in Scala land | +| `scan(R, Func2)` | `scan(R)((R, T) => R)` | +| sequenceEqual(...) | use `(first zip second) map (p => equality(p._1, p._2))` | +| sequenceEqual(...) | use `(first zip second) map (p => p._1 == p._2)` | +| `skip(Int)` | `drop(Int)` | +| `skipWhile(Func1)` | `dropWhile(T => Boolean)` | +| skipWhileWithIndex(...) | considered unnecessary in Scala land | +| `startWith(Iterable)`
`startWith(T)`
`startWith(T, T)`
`startWith(T, T, T)`
`startWith(T, T, T, T)`
`startWith(T, T, T, T, T)`
`startWith(T, T, T, T, T, T)`
`startWith(T, T, T, T, T, T, T)`
`startWith(T, T, T, T, T, T, T, T)`
`startWith(T, T, T, T, T, T, T, T, T)` | unnecessary because we can just use `++` instead | +| `subscribe(Observer, Scheduler)` | `subscribe(Observer[T], Scheduler)` | +| `subscribe(Action1, Action1)` | `subscribe(T => Unit, Throwable => Unit)` | +| subscribe(...) | `subscribe(T => Unit, Throwable => Unit, () => Unit)` | +| subscribe(...) | `subscribe(T => Unit, Throwable => Unit, () => Unit, Scheduler)` | +| `subscribe(Action1)` | `subscribe(T => Unit)` | +| subscribe(...) | `subscribe(T => Unit, Throwable => Unit, Scheduler)` | +| `subscribe(Action1, Scheduler)` | `subscribe(T => Unit, Scheduler)` | +| `subscribe(Observer)` | `subscribe(Observer[T])` | +| `subscribeOn(Scheduler)` | `subscribeOn(Scheduler)` | +| `sum(Observable)` | `sum(Numeric[U])` | +| `sumDoubles(Observable)` | `sum(Numeric[U])` | +| `sumFloats(Observable)` | `sum(Numeric[U])` | +| `sumLongs(Observable)` | `sum(Numeric[U])` | +| switchDo(...) | deprecated in RxJava | +| switchOnNext(...) | `switch(<:<[Observable[T], Observable[Observable[U]]])` | +| `synchronize()`
`synchronize(Observable)` | `synchronize` | +| `take(Int)` | `take(Int)` | +| `takeFirst(Func1)` | use `.filter(condition).first` | +| `takeFirst()` | `first` | +| `takeLast(Int)` | `takeRight(Int)` | +| `takeUntil(Observable)` | `takeUntil(Observable[E])` | +| `takeWhile(Func1)` | `takeWhile(T => Boolean)` | +| takeWhileWithIndex(...) | `takeWhileWithIndex((T, Integer) => Boolean)` | +| `throttleFirst(Long, TimeUnit)` | `throttleFirst(Duration)` | +| `throttleFirst(Long, TimeUnit, Scheduler)` | `throttleFirst(Duration, Scheduler)` | +| `throttleLast(Long, TimeUnit)` | `throttleLast(Duration)` | +| `throttleLast(Long, TimeUnit, Scheduler)` | `throttleLast(Duration, Scheduler)` | +| `throttleWithTimeout(Long, TimeUnit, Scheduler)` | `throttleWithTimeout(Duration, Scheduler)` | +| `throttleWithTimeout(Long, TimeUnit)` | `throttleWithTimeout(Duration)` | +| `timestamp()` | `timestamp` | +| `toBlockingObservable()` | `toBlockingObservable` | +| `toList()` | `toSeq` | +| `toSortedList(Func2)` | Sorting is already done in Scala's collection library, use `.toSeq.map(_.sortWith(f))` | +| `toSortedList()` | Sorting is already done in Scala's collection library, use `.toSeq.map(_.sorted)` | +| `where(Func1)` | `filter(T => Boolean)` | +| `window(Long, TimeUnit)` | `window(Duration)` | +| `window(Long, TimeUnit, Int)` | `window(Duration, Int)` | +| `window(Long, Long, TimeUnit, Scheduler)` | `window(Duration, Duration, Scheduler)` | +| `window(Int)` | `window(Int)` | +| `window(Int, Int)` | `window(Int, Int)` | +| `window(Long, Long, TimeUnit)` | `window(Duration, Duration)` | +| window(...) | `window(Observable[Opening], Opening => Observable[Closing])` | +| window(...) | `window(() => Observable[Closing])` | +| `window(Long, TimeUnit, Int, Scheduler)` | `window(Duration, Int, Scheduler)` | +| `window(Long, TimeUnit, Scheduler)` | `window(Duration, Scheduler)` | +| zip(...) | use instance method `zip` and `map` | +| zip(...)
zip(...) | use `zip` in companion object and `map` | +| zip(...)
zip(...)
zip(...)
zip(...)
zip(...)
zip(...)
zip(...) | considered unnecessary in Scala land | + +This table was generated on Fri Sep 20 17:38:03 CEST 2013. +**Do not edit**. Instead, edit `rx.lang.scala.CompletenessTest`. diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index 60a2f3768f..abf0439b75 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -287,7 +287,8 @@ class CompletenessTest extends JUnitSuite { def escapeJava(s: String) = s.replaceAllLiterally("<", "<") .replaceAllLiterally(">", ">") - + + @Ignore // because spams output @Test def printMarkdownCorrespondenceTable() { def isInteresting(p: (String, String)): Boolean = p._1.replaceAllLiterally("()", "") != p._2 From a2ab731ce1a867e1a2902c27062bcea2923ef303 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 20 Sep 2013 18:16:08 +0200 Subject: [PATCH 055/333] delete comparison.md because tooltips do not work on github --- language-adaptors/rxjava-scala/TODO.md | 2 +- language-adaptors/rxjava-scala/comparison.md | 146 ------------------- 2 files changed, 1 insertion(+), 147 deletions(-) delete mode 100644 language-adaptors/rxjava-scala/comparison.md diff --git a/language-adaptors/rxjava-scala/TODO.md b/language-adaptors/rxjava-scala/TODO.md index 017c3be381..d4136236da 100644 --- a/language-adaptors/rxjava-scala/TODO.md +++ b/language-adaptors/rxjava-scala/TODO.md @@ -9,7 +9,7 @@ This is a (probably incomplete) list of what still needs to be done in the Scala * Notification as a case class * integrating Scala Futures, should there be a common base interface for Futures and Observables? * Add methods present in Scala collections library, but not in RxJava, e.g. aggregate à la Scala, collect, exists, tails, ... -* combineLatest with about arities > 2 +* combineLatest with arities > 2 * decide where the MovieLib/MovieLibUsage (use Scala code from Java code) example should live and make sure gradle builds it in the right order * Avoid text duplication in scaladoc using templates, add examples, distinction between use case signature and full signature * other small TODOs diff --git a/language-adaptors/rxjava-scala/comparison.md b/language-adaptors/rxjava-scala/comparison.md deleted file mode 100644 index 786b63bae9..0000000000 --- a/language-adaptors/rxjava-scala/comparison.md +++ /dev/null @@ -1,146 +0,0 @@ - -## Comparison of Scala Observable and Java Observable - -Note: -* This table contains both static methods and instance methods. -* If a signature is too long, move your mouse over it to get the full signature. - - -| Java Method | Scala Method | -|-------------|--------------| -| `aggregate(R, Func2)` | `fold(R)((R, T) => R)` | -| `aggregate(Func2)` | `reduce((U, U) => U)` | -| `all(Func1)` | `forall(T => Boolean)` | -| `average(Observable)`
`averageDoubles(Observable)`
`averageFloats(Observable)`
`averageLongs(Observable)` | We can't have a general average method because Scala's `Numeric` does not have scalar multiplication (we would need to calculate `(1.0/numberOfElements)*sum`). You can use `fold` instead to accumulate `sum` and `numberOfElements` and divide at the end. | -| `buffer(Int, Int)` | `buffer(Int, Int)` | -| `buffer(Long, TimeUnit, Int)` | `buffer(Duration, Int)` | -| `buffer(Long, Long, TimeUnit, Scheduler)` | `buffer(Duration, Duration, Scheduler)` | -| `buffer(Long, Long, TimeUnit)` | `buffer(Duration, Duration)` | -| buffer(...) | `buffer(Observable[Opening], Opening => Observable[Closing])` | -| buffer(...) | `buffer(() => Observable[Closing])` | -| `buffer(Long, TimeUnit)` | `buffer(Duration)` | -| `buffer(Long, TimeUnit, Int, Scheduler)` | `buffer(Duration, Int, Scheduler)` | -| `buffer(Int)` | `buffer(Int)` | -| `buffer(Long, TimeUnit, Scheduler)` | `buffer(Duration, Scheduler)` | -| `cache()` | `cache` | -| combineLatest(...) | `combineLatest(Observable[U])` | -| combineLatest(...)
combineLatest(...)
combineLatest(...)
combineLatest(...)
combineLatest(...)
combineLatest(...)
combineLatest(...) | If C# doesn't need it, Scala doesn't need it either ;-) | -| concat(...) | `concat(<:<[Observable[T], Observable[Observable[U]]])` | -| concat(...)
concat(...)
concat(...)
concat(...)
concat(...)
concat(...)
concat(...)
concat(...) | unnecessary because we can use `++` instead or `Observable(o1, o2, ...).concat` | -| `count()` | `length` | -| `create(OnSubscribeFunc)` | `apply(Observer[T] => Subscription)` | -| `debounce(Long, TimeUnit)` | `debounce(Duration)` | -| `debounce(Long, TimeUnit, Scheduler)` | `debounce(Duration, Scheduler)` | -| `defer(Func0>)` | `defer(=> Observable[T])` | -| `dematerialize()` | `dematerialize(<:<[T, Notification[U]])` | -| `distinct(Comparator)`
distinct(...) | **TODO: missing** | -| `distinct(Func1)` | `distinct(T => U)` | -| `distinct()` | `distinct` | -| `distinctUntilChanged()` | `distinctUntilChanged` | -| `distinctUntilChanged(Comparator)`
distinctUntilChanged(...) | **TODO: missing** | -| distinctUntilChanged(...) | `distinctUntilChanged(T => U)` | -| `empty()` | `apply(T*)` | -| `error(Throwable)` | `apply(Throwable)` | -| `filter(Func1)` | `filter(T => Boolean)` | -| `finallyDo(Action0)` | `finallyDo(() => Unit)` | -| `first()` | `first` | -| `first(Func1)` | use `.filter(condition).first` | -| `firstOrDefault(Func1, T)` | use `.filter(condition).firstOrElse(default)` | -| `firstOrDefault(T)` | `firstOrElse(=> U)` | -| flatMap(...) | `flatMap(T => Observable[R])` | -| `from(Future, Long, TimeUnit)` | `apply(Future[T], Duration)` | -| `from(Future)` | `apply(Future[T])` | -| `from(Future, Scheduler)` | `apply(Future[T], Scheduler)` | -| `from(T[])`
`from(Iterable)`
`from(T)`
`from(T, T)`
`from(T, T, T)`
`from(T, T, T, T)`
`from(T, T, T, T, T)`
`from(T, T, T, T, T, T)`
`from(T, T, T, T, T, T, T)`
`from(T, T, T, T, T, T, T, T)`
`from(T, T, T, T, T, T, T, T, T)`
`from(T, T, T, T, T, T, T, T, T, T)` | `apply(T*)` | -| `groupBy(Func1)` | `groupBy(T => K)` | -| groupBy(...) | use `groupBy` and `map` | -| `interval(Long, TimeUnit)` | `interval(Duration)` | -| `interval(Long, TimeUnit, Scheduler)` | `interval(Duration, Scheduler)` | -| `just(T)` | `just(T)` | -| `map(Func1)` | `map(T => R)` | -| mapMany(...) | `flatMap(T => Observable[R])` | -| mapWithIndex(...) | combine `zipWithIndex` with `map` or with a for comprehension | -| `materialize()` | `materialize` | -| merge(...)
merge(...)
merge(...)
merge(...)
merge(...)
merge(...)
merge(...) | unnecessary because we can use `Observable(o1, o2, ...).flatten` instead | -| merge(...) | `flatten(<:<[Observable[T], Observable[Observable[U]]])` | -| merge(...) | `merge(Observable[U])` | -| mergeDelayError(...) | `flattenDelayError(<:<[Observable[T], Observable[Observable[U]]])` | -| mergeDelayError(...)
mergeDelayError(...)
mergeDelayError(...)
mergeDelayError(...)
mergeDelayError(...)
mergeDelayError(...)
mergeDelayError(...) | unnecessary because we can use `Observable(o1, o2, ...).flattenDelayError` instead | -| mergeDelayError(...) | `mergeDelayError(Observable[U])` | -| `multicast(Subject)` | `multicast(Subject[T, R])` | -| `never()` | `never` | -| `observeOn(Scheduler)` | `observeOn(Scheduler)` | -| onErrorResumeNext(...) | `onErrorResumeNext(Throwable => Observable[U])` | -| `onErrorResumeNext(Observable)` | `onErrorResumeNext(Observable[U])` | -| `onErrorReturn(Func1)` | `onErrorReturn(Throwable => U)` | -| `onExceptionResumeNext(Observable)` | `onExceptionResumeNext(Observable[U])` | -| `parallel(Func1, Observable>)` | `parallel(Observable[T] => Observable[R])` | -| parallel(...) | `parallel(Observable[T] => Observable[R], Scheduler)` | -| `publish()` | `publish` | -| `range(Int, Int)` | `apply(Range)` | -| `reduce(Func2)` | `reduce((U, U) => U)` | -| `reduce(R, Func2)` | `fold(R)((R, T) => R)` | -| `replay()` | `replay` | -| `retry(Int)` | `retry(Int)` | -| `retry()` | `retry` | -| `sample(Long, TimeUnit)` | `sample(Duration)` | -| `sample(Long, TimeUnit, Scheduler)` | `sample(Duration, Scheduler)` | -| `scan(Func2)` | considered unnecessary in Scala land | -| `scan(R, Func2)` | `scan(R)((R, T) => R)` | -| sequenceEqual(...) | use `(first zip second) map (p => equality(p._1, p._2))` | -| sequenceEqual(...) | use `(first zip second) map (p => p._1 == p._2)` | -| `skip(Int)` | `drop(Int)` | -| `skipWhile(Func1)` | `dropWhile(T => Boolean)` | -| skipWhileWithIndex(...) | considered unnecessary in Scala land | -| `startWith(Iterable)`
`startWith(T)`
`startWith(T, T)`
`startWith(T, T, T)`
`startWith(T, T, T, T)`
`startWith(T, T, T, T, T)`
`startWith(T, T, T, T, T, T)`
`startWith(T, T, T, T, T, T, T)`
`startWith(T, T, T, T, T, T, T, T)`
`startWith(T, T, T, T, T, T, T, T, T)` | unnecessary because we can just use `++` instead | -| `subscribe(Observer, Scheduler)` | `subscribe(Observer[T], Scheduler)` | -| `subscribe(Action1, Action1)` | `subscribe(T => Unit, Throwable => Unit)` | -| subscribe(...) | `subscribe(T => Unit, Throwable => Unit, () => Unit)` | -| subscribe(...) | `subscribe(T => Unit, Throwable => Unit, () => Unit, Scheduler)` | -| `subscribe(Action1)` | `subscribe(T => Unit)` | -| subscribe(...) | `subscribe(T => Unit, Throwable => Unit, Scheduler)` | -| `subscribe(Action1, Scheduler)` | `subscribe(T => Unit, Scheduler)` | -| `subscribe(Observer)` | `subscribe(Observer[T])` | -| `subscribeOn(Scheduler)` | `subscribeOn(Scheduler)` | -| `sum(Observable)` | `sum(Numeric[U])` | -| `sumDoubles(Observable)` | `sum(Numeric[U])` | -| `sumFloats(Observable)` | `sum(Numeric[U])` | -| `sumLongs(Observable)` | `sum(Numeric[U])` | -| switchDo(...) | deprecated in RxJava | -| switchOnNext(...) | `switch(<:<[Observable[T], Observable[Observable[U]]])` | -| `synchronize()`
`synchronize(Observable)` | `synchronize` | -| `take(Int)` | `take(Int)` | -| `takeFirst(Func1)` | use `.filter(condition).first` | -| `takeFirst()` | `first` | -| `takeLast(Int)` | `takeRight(Int)` | -| `takeUntil(Observable)` | `takeUntil(Observable[E])` | -| `takeWhile(Func1)` | `takeWhile(T => Boolean)` | -| takeWhileWithIndex(...) | `takeWhileWithIndex((T, Integer) => Boolean)` | -| `throttleFirst(Long, TimeUnit)` | `throttleFirst(Duration)` | -| `throttleFirst(Long, TimeUnit, Scheduler)` | `throttleFirst(Duration, Scheduler)` | -| `throttleLast(Long, TimeUnit)` | `throttleLast(Duration)` | -| `throttleLast(Long, TimeUnit, Scheduler)` | `throttleLast(Duration, Scheduler)` | -| `throttleWithTimeout(Long, TimeUnit, Scheduler)` | `throttleWithTimeout(Duration, Scheduler)` | -| `throttleWithTimeout(Long, TimeUnit)` | `throttleWithTimeout(Duration)` | -| `timestamp()` | `timestamp` | -| `toBlockingObservable()` | `toBlockingObservable` | -| `toList()` | `toSeq` | -| `toSortedList(Func2)` | Sorting is already done in Scala's collection library, use `.toSeq.map(_.sortWith(f))` | -| `toSortedList()` | Sorting is already done in Scala's collection library, use `.toSeq.map(_.sorted)` | -| `where(Func1)` | `filter(T => Boolean)` | -| `window(Long, TimeUnit)` | `window(Duration)` | -| `window(Long, TimeUnit, Int)` | `window(Duration, Int)` | -| `window(Long, Long, TimeUnit, Scheduler)` | `window(Duration, Duration, Scheduler)` | -| `window(Int)` | `window(Int)` | -| `window(Int, Int)` | `window(Int, Int)` | -| `window(Long, Long, TimeUnit)` | `window(Duration, Duration)` | -| window(...) | `window(Observable[Opening], Opening => Observable[Closing])` | -| window(...) | `window(() => Observable[Closing])` | -| `window(Long, TimeUnit, Int, Scheduler)` | `window(Duration, Int, Scheduler)` | -| `window(Long, TimeUnit, Scheduler)` | `window(Duration, Scheduler)` | -| zip(...) | use instance method `zip` and `map` | -| zip(...)
zip(...) | use `zip` in companion object and `map` | -| zip(...)
zip(...)
zip(...)
zip(...)
zip(...)
zip(...)
zip(...) | considered unnecessary in Scala land | - -This table was generated on Fri Sep 20 17:38:03 CEST 2013. -**Do not edit**. Instead, edit `rx.lang.scala.CompletenessTest`. From db2e08ca039c59d51349255c4b8b3c65b26d52de Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 00:01:11 -0700 Subject: [PATCH 056/333] Observable API for Apache HttpAsyncClient 4.0 --- .../rxjava-apache-http/build.gradle | 20 ++ .../java/rx/apache/http/ObservableHttp.java | 171 ++++++++++++++++++ .../apache/http/ObservableHttpResponse.java | 51 ++++++ .../http/consumers/ExpandableByteBuffer.java | 65 +++++++ .../http/consumers/ResponseConsumerBasic.java | 102 +++++++++++ .../consumers/ResponseConsumerDelegate.java | 84 +++++++++ .../ResponseConsumerEventStream.java | 121 +++++++++++++ .../http/consumers/ResponseDelegate.java | 43 +++++ .../http/examples/ExampleObservableHttp.java | 117 ++++++++++++ settings.gradle | 3 +- 10 files changed, 776 insertions(+), 1 deletion(-) create mode 100644 rxjava-contrib/rxjava-apache-http/build.gradle create mode 100644 rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttp.java create mode 100644 rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttpResponse.java create mode 100644 rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ExpandableByteBuffer.java create mode 100644 rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerBasic.java create mode 100644 rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java create mode 100644 rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerEventStream.java create mode 100644 rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseDelegate.java create mode 100644 rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java diff --git a/rxjava-contrib/rxjava-apache-http/build.gradle b/rxjava-contrib/rxjava-apache-http/build.gradle new file mode 100644 index 0000000000..b51cb6c213 --- /dev/null +++ b/rxjava-contrib/rxjava-apache-http/build.gradle @@ -0,0 +1,20 @@ +apply plugin: 'osgi' + +sourceCompatibility = JavaVersion.VERSION_1_6 +targetCompatibility = JavaVersion.VERSION_1_6 + +dependencies { + compile project(':rxjava-core') + compile 'org.apache.httpcomponents:httpclient:4.3' + compile 'org.apache.httpcomponents:httpcore-nio:4.3' + compile 'org.apache.httpcomponents:httpasyncclient:4.0-beta4' +} + +jar { + manifest { + name = 'rxjava-apache-http' + instruction 'Bundle-Vendor', 'Netflix' + instruction 'Bundle-DocURL', 'https://github.com/Netflix/RxJava' + instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*' + } +} diff --git a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttp.java b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttp.java new file mode 100644 index 0000000000..c8d87c8334 --- /dev/null +++ b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttp.java @@ -0,0 +1,171 @@ +/** + * Copyright 2013 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.apache.http; + +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.concurrent.FutureCallback; +import org.apache.http.nio.client.HttpAsyncClient; +import org.apache.http.nio.client.methods.HttpAsyncMethods; +import org.apache.http.nio.protocol.HttpAsyncRequestProducer; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.apache.http.consumers.ResponseConsumerDelegate; +import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.Subscriptions; + +/** + * An {@link Observable} interface to Apache {@link HttpAsyncClient}. + *

+ * The initial {@link HttpResponse} is returned via {@link Observer#onNext} wrapped in a {@link ObservableHttpResponse}. + *

+ * The content stream is retrieved from {@link ObservableHttpResponse#getContent()}. + *

+ * It is aware of Content-Type text/event-stream and will stream each event via {@link Observer#onNext}. + *

+ * Other Content-Types will be returned as a single call to {@link Observer#onNext}. + *

+ * Examples: + *

+ *

 {@code
+ * ObservableHttp.createGet("http://www.wikipedia.com", httpClient).toObservable();
+ * } 
+ *

+ *

 {@code
+ * ObservableHttp.createRequest(HttpAsyncMethods.createGet("http://www.wikipedia.com"), httpClient).toObservable();
+ * } 
+ * + * An {@link HttpClient} can be created like this: + * + *
 {@code
+ * CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault();
+ * httpClient.start(); // start it
+ * httpClient.stop(); // stop it
+ * } 
+ *

+ * A client with custom configurations can be created like this: + *

+ *
 {@code
+ * final RequestConfig requestConfig = RequestConfig.custom()
+ *     .setSocketTimeout(1000)
+ *     .setConnectTimeout(200).build();
+ * final CloseableHttpAsyncClient httpClient = HttpAsyncClients.custom()
+ *     .setDefaultRequestConfig(requestConfig)
+ *     .setMaxConnPerRoute(20)
+ *     .setMaxConnTotal(50)
+ *     .build();
+ * httpClient.start();
+ * }
+ *

+ * + * @param + */ +public class ObservableHttp { + + private final OnSubscribeFunc onSubscribe; + + private ObservableHttp(OnSubscribeFunc onSubscribe) { + this.onSubscribe = onSubscribe; + } + + private static ObservableHttp create(OnSubscribeFunc onSubscribe) { + return new ObservableHttp(onSubscribe); + } + + public Observable toObservable() { + return Observable.create(new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(Observer observer) { + return onSubscribe.onSubscribe(observer); + } + }); + } + + public static ObservableHttp createGet(String uri, final HttpAsyncClient client) { + return createRequest(HttpAsyncMethods.createGet(uri), client); + } + + /** + * Execute request using {@link HttpAsyncRequestProducer} to define HTTP Method, URI and payload (if applicable). + *

+ * If the response is chunked (or flushed progressively such as with text/event-stream Server-Sent Events) this will call + * {@link Observer#onNext} multiple times. + *

+ * Use {@code HttpAsyncMethods.create* } factory methods to create {@link HttpAsyncRequestProducer} instances. + *

+ * A client can be retrieved like this: + *

+ *

 {@code      CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault(); } 

+ *

+ * A client with custom configurations can be created like this: + *

+ *
 {@code
+     * final RequestConfig requestConfig = RequestConfig.custom()
+     *     .setSocketTimeout(3000)
+     *     .setConnectTimeout(3000).build();
+     * final CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
+     *     .setDefaultRequestConfig(requestConfig)
+     *     .setMaxConnPerRoute(20)
+     *     .setMaxConnTotal(50)
+     *     .build();
+     * httpclient.start();
+     * }
+ * + * + * @param requestProducer + * @param client + * @return + */ + public static ObservableHttp createRequest(final HttpAsyncRequestProducer requestProducer, final HttpAsyncClient client) { + + return ObservableHttp.create(new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(final Observer observer) { + + final CompositeSubscription parentSubscription = new CompositeSubscription(); + + // return a Subscription that wraps the Future so it can be cancelled + parentSubscription.add(Subscriptions.create(client.execute(requestProducer, new ResponseConsumerDelegate(observer, parentSubscription), + new FutureCallback() { + + @Override + public void completed(HttpResponse result) { + observer.onCompleted(); + } + + @Override + public void failed(Exception ex) { + observer.onError(ex); + } + + @Override + public void cancelled() { + observer.onCompleted(); + } + + }))); + + return parentSubscription; + } + }); + } + +} diff --git a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttpResponse.java b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttpResponse.java new file mode 100644 index 0000000000..8077152ccc --- /dev/null +++ b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttpResponse.java @@ -0,0 +1,51 @@ +/** + * Copyright 2013 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.apache.http; + +import org.apache.http.HttpResponse; + +import rx.Observable; + +/** + * The {@link HttpResponse} for the entire request and accessor to {@link Observable} of the content stream. + */ +public class ObservableHttpResponse { + + private final HttpResponse response; + private final Observable contentSubscription; + + public ObservableHttpResponse(HttpResponse response, Observable contentSubscription) { + this.response = response; + this.contentSubscription = contentSubscription; + } + + /** + * The {@link HttpResponse} returned by the Apache client at the beginning of the response. + * + * @return {@link HttpResponse} with HTTP status codes, headers, etc + */ + public HttpResponse getResponse() { + return response; + } + + /** + * If the response is not chunked then only a single array will be returned. If chunked then multiple arrays. + */ + public Observable getContent() { + return contentSubscription; + } + +} diff --git a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ExpandableByteBuffer.java b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ExpandableByteBuffer.java new file mode 100644 index 0000000000..da6c81e7f2 --- /dev/null +++ b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ExpandableByteBuffer.java @@ -0,0 +1,65 @@ +/** + * Copyright 2013 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.apache.http.consumers; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.http.nio.util.ExpandableBuffer; +import org.apache.http.nio.util.HeapByteBufferAllocator; + +class ExpandableByteBuffer extends ExpandableBuffer { + public ExpandableByteBuffer(int size) { + super(size, HeapByteBufferAllocator.INSTANCE); + } + + public ExpandableByteBuffer() { + super(4 * 1024, HeapByteBufferAllocator.INSTANCE); + } + + public void addByte(byte b) { + if (this.buffer.remaining() == 0) { + expand(); + } + this.buffer.put(b); + } + + public boolean hasContent() { + return this.buffer.position() > 0; + } + + public byte[] getBytes() { + byte[] data = new byte[this.buffer.position()]; + this.buffer.position(0); + this.buffer.get(data); + return data; + } + + public void reset() { + clear(); + } + + public void consumeInputStream(InputStream content) throws IOException { + try { + int b = -1; + while ((b = content.read()) != -1) { + addByte((byte) b); + } + } finally { + content.close(); + } + } +} \ No newline at end of file diff --git a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerBasic.java b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerBasic.java new file mode 100644 index 0000000000..8101c9ad8d --- /dev/null +++ b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerBasic.java @@ -0,0 +1,102 @@ +/** + * Copyright 2013 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.apache.http.consumers; + +import java.io.IOException; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpException; +import org.apache.http.HttpResponse; +import org.apache.http.entity.ContentType; +import org.apache.http.nio.ContentDecoder; +import org.apache.http.nio.IOControl; +import org.apache.http.nio.protocol.BasicAsyncResponseConsumer; +import org.apache.http.protocol.HttpContext; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.apache.http.ObservableHttpResponse; +import rx.subscriptions.CompositeSubscription; + +/** + * Delegate wrapper around {@link BasicAsyncResponseConsumer} so it works with {@link ResponseConsumerDelegate} + */ +class ResponseConsumerBasic extends BasicAsyncResponseConsumer implements ResponseDelegate { + + private final Observer observer; + private final CompositeSubscription parentSubscription; + + public ResponseConsumerBasic(final Observer observer, CompositeSubscription parentSubscription) { + this.observer = observer; + this.parentSubscription = parentSubscription; + } + + @Override + public void _onResponseReceived(HttpResponse response) throws HttpException, IOException { + onResponseReceived(response); + } + + @Override + public void _onContentReceived(ContentDecoder decoder, IOControl ioctrl) throws IOException { + if (parentSubscription.isUnsubscribed()) { + ioctrl.shutdown(); + } + onContentReceived(decoder, ioctrl); + } + + @Override + public void _onEntityEnclosed(HttpEntity entity, ContentType contentType) throws IOException { + onEntityEnclosed(entity, contentType); + } + + @Override + public HttpResponse _buildResult(HttpContext context) throws Exception { + final HttpResponse response = buildResult(context); + + Observable contentObservable = Observable.create(new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(Observer o) { + long length = response.getEntity().getContentLength(); + if (length > Integer.MAX_VALUE) { + o.onError(new IllegalStateException("Content Length too large for a byte[] => " + length)); + } else { + ExpandableByteBuffer buf = new ExpandableByteBuffer((int) length); + try { + buf.consumeInputStream(response.getEntity().getContent()); + o.onNext(buf.getBytes()); + o.onCompleted(); + } catch (Throwable e) { + o.onError(e); + } + } + + return parentSubscription; + } + }); + + observer.onNext(new ObservableHttpResponse(response, contentObservable)); + return response; + } + + @Override + public void _releaseResources() { + releaseResources(); + } + +} diff --git a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java new file mode 100644 index 0000000000..3060d67bca --- /dev/null +++ b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java @@ -0,0 +1,84 @@ +/** + * Copyright 2013 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.apache.http.consumers; + +import java.io.IOException; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpException; +import org.apache.http.HttpResponse; +import org.apache.http.entity.ContentType; +import org.apache.http.nio.ContentDecoder; +import org.apache.http.nio.IOControl; +import org.apache.http.nio.protocol.AbstractAsyncResponseConsumer; +import org.apache.http.protocol.HttpContext; + +import rx.Observer; +import rx.apache.http.ObservableHttpResponse; +import rx.subscriptions.CompositeSubscription; + +/** + * AbstractAsyncResponseConsumer that chooses different implementations based on return headers. + *

+ *

    + *
  • Content-Type:text/event-stream == {@link ResponseConsumerEventStream}
  • + *
  • All others == {@link ResponseConsumerBasic}
  • + *
+ */ +public class ResponseConsumerDelegate extends AbstractAsyncResponseConsumer { + + private volatile ResponseDelegate consumer = null; + final Observer observer; + final CompositeSubscription subscription; + + public ResponseConsumerDelegate(final Observer observer, CompositeSubscription subscription) { + this.observer = observer; + this.subscription = subscription; + } + + @Override + protected void onResponseReceived(HttpResponse response) throws HttpException, IOException { + // when we receive the response with headers we evaluate what type of consumer we want + if (response.getFirstHeader("Content-Type").getValue().equals("text/event-stream")) { + consumer = new ResponseConsumerEventStream(observer, subscription); + } else { + consumer = new ResponseConsumerBasic(observer, subscription); + } + // forward 'response' to actual consumer + consumer._onResponseReceived(response); + } + + @Override + protected void onContentReceived(ContentDecoder decoder, IOControl ioctrl) throws IOException { + consumer._onContentReceived(decoder, ioctrl); + } + + @Override + protected void onEntityEnclosed(HttpEntity entity, ContentType contentType) throws IOException { + consumer._onEntityEnclosed(entity, contentType); + } + + @Override + protected HttpResponse buildResult(HttpContext context) throws Exception { + return consumer._buildResult(context); + } + + @Override + protected void releaseResources() { + consumer._releaseResources(); + } + +} diff --git a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerEventStream.java b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerEventStream.java new file mode 100644 index 0000000000..be8407aecf --- /dev/null +++ b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerEventStream.java @@ -0,0 +1,121 @@ +/** + * Copyright 2013 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.apache.http.consumers; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpException; +import org.apache.http.HttpResponse; +import org.apache.http.entity.ContentType; +import org.apache.http.nio.ContentDecoder; +import org.apache.http.nio.IOControl; +import org.apache.http.nio.client.methods.AsyncByteConsumer; +import org.apache.http.nio.protocol.HttpAsyncResponseConsumer; +import org.apache.http.protocol.HttpContext; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.apache.http.ObservableHttpResponse; +import rx.subjects.PublishSubject; +import rx.subscriptions.CompositeSubscription; + +/** + * {@link HttpAsyncResponseConsumer} for Content-Type:text/event-stream + *

+ * It will emit a byte[] via {@link Observer#onNext} for each non-empty line. + */ +class ResponseConsumerEventStream extends AsyncByteConsumer implements ResponseDelegate { + + private final Observer observer; + private final PublishSubject contentSubject = PublishSubject. create(); + private final CompositeSubscription parentSubscription; + + public ResponseConsumerEventStream(final Observer observer, CompositeSubscription parentSubscription) { + this.observer = observer; + this.parentSubscription = parentSubscription; + } + + final ExpandableByteBuffer dataBuffer = new ExpandableByteBuffer(); + + @Override + public void _onResponseReceived(HttpResponse response) throws HttpException, IOException { + onResponseReceived(response); + } + + @Override + protected void onByteReceived(ByteBuffer buf, IOControl ioctrl) throws IOException { + if (parentSubscription.isUnsubscribed()) { + ioctrl.shutdown(); + } + while (buf.position() < buf.limit()) { + byte b = buf.get(); + if (b == 10 || b == 13) { + if (dataBuffer.hasContent()) { + contentSubject.onNext(dataBuffer.getBytes()); + } + dataBuffer.reset(); + } else { + dataBuffer.addByte(b); + } + } + } + + @Override + protected void onResponseReceived(HttpResponse response) throws HttpException, IOException { + + // wrap the contentSubject so we can chain the Subscription between parent and child + Observable contentObservable = Observable.create(new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(Observer observer) { + parentSubscription.add(contentSubject.subscribe(observer)); + return parentSubscription; + } + }); + observer.onNext(new ObservableHttpResponse(response, contentObservable)); + } + + @Override + protected HttpResponse buildResult(HttpContext context) throws Exception { + // streaming results, so not returning anything here + return null; + } + + @Override + public void _onContentReceived(ContentDecoder decoder, IOControl ioctrl) throws IOException { + onContentReceived(decoder, ioctrl); + } + + @Override + public void _onEntityEnclosed(HttpEntity entity, ContentType contentType) throws IOException { + onEntityEnclosed(entity, contentType); + } + + @Override + public HttpResponse _buildResult(HttpContext context) throws Exception { + return buildResult(context); + } + + @Override + public void _releaseResources() { + releaseResources(); + } + +} \ No newline at end of file diff --git a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseDelegate.java b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseDelegate.java new file mode 100644 index 0000000000..566237a9d1 --- /dev/null +++ b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseDelegate.java @@ -0,0 +1,43 @@ +/** + * Copyright 2013 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.apache.http.consumers; + +import java.io.IOException; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpException; +import org.apache.http.HttpResponse; +import org.apache.http.entity.ContentType; +import org.apache.http.nio.ContentDecoder; +import org.apache.http.nio.IOControl; +import org.apache.http.nio.protocol.HttpAsyncResponseConsumer; +import org.apache.http.protocol.HttpContext; + +/** + * Delegate methods for getting access to protected methods. + */ +abstract interface ResponseDelegate extends HttpAsyncResponseConsumer { + + public void _onResponseReceived(HttpResponse response) throws HttpException, IOException; + + public void _onContentReceived(ContentDecoder decoder, IOControl ioctrl) throws IOException; + + public void _onEntityEnclosed(HttpEntity entity, ContentType contentType) throws IOException; + + public HttpResponse _buildResult(HttpContext context) throws Exception; + + public void _releaseResources(); +} diff --git a/rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java b/rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java new file mode 100644 index 0000000000..763916f861 --- /dev/null +++ b/rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java @@ -0,0 +1,117 @@ +package rx.apache.http.examples; + +import java.io.IOException; +import java.net.URISyntaxException; + +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.apache.http.impl.nio.client.HttpAsyncClients; +import org.apache.http.nio.client.HttpAsyncClient; +import org.apache.http.nio.client.methods.HttpAsyncMethods; + +import rx.Observable; +import rx.apache.http.ObservableHttp; +import rx.apache.http.ObservableHttpResponse; +import rx.util.functions.Action1; +import rx.util.functions.Func1; + +public class ExampleObservableHttp { + + public static void main(String args[]) { + CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault(); + + // final RequestConfig requestConfig = RequestConfig.custom() + // .setSocketTimeout(3000) + // .setConnectTimeout(3000).build(); + // final CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() + // .setDefaultRequestConfig(requestConfig) + // .setMaxConnPerRoute(20) + // .setMaxConnTotal(50) + // .build(); + + try { + httpclient.start(); + executeViaObservableHttpWithForEach(httpclient); + executeStreamingViaObservableHttpWithForEach(httpclient); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + httpclient.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + + CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault(); + ObservableHttp.createGet("http://www.wikipedia.com", httpClient).toObservable(); + ObservableHttp.createRequest(HttpAsyncMethods.createGet("http://www.wikipedia.com"), httpClient).toObservable(); + } + + protected static void executeViaObservableHttpWithForEach(final HttpAsyncClient client) throws URISyntaxException, IOException, InterruptedException { + System.out.println("---- executeViaObservableHttpWithForEach"); + ObservableHttp.createRequest(HttpAsyncMethods.createGet("http://www.wikipedia.com"), client) + .toObservable() + .flatMap(new Func1>() { + + @Override + public Observable call(ObservableHttpResponse response) { + return response.getContent().map(new Func1() { + + @Override + public String call(byte[] bb) { + return new String(bb); + } + + }); + } + }) + .toBlockingObservable() + .forEach(new Action1() { + + @Override + public void call(String resp) { + System.out.println(resp); + } + }); + } + + protected static void executeStreamingViaObservableHttpWithForEach(final HttpAsyncClient client) throws URISyntaxException, IOException, InterruptedException { + System.out.println("---- executeStreamingViaObservableHttpWithForEach"); + for (int i = 0; i < 5; i++) { + final int c = i + 1; + ObservableHttp.createRequest(HttpAsyncMethods.createGet("http://ec2-54-211-91-164.compute-1.amazonaws.com:8077/eventbus.stream?topic=hystrix-metrics"), client) + .toObservable() + .flatMap(new Func1>() { + + @Override + public Observable call(ObservableHttpResponse response) { + return response.getContent().map(new Func1() { + + @Override + public String call(byte[] bb) { + return new String(bb); + } + + }); + } + }) + .filter(new Func1() { + + @Override + public Boolean call(String t1) { + return !t1.startsWith(": ping"); + } + }) + .take(3) + .toBlockingObservable() + .forEach(new Action1() { + + @Override + public void call(String resp) { + System.out.println("Response [" + c + "]: " + resp + " (" + resp.length() + ")"); + } + }); + } + } + +} diff --git a/settings.gradle b/settings.gradle index 700c698e27..8750fab727 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,4 +5,5 @@ include 'rxjava-core', \ 'language-adaptors:rxjava-scala', \ 'language-adaptors:rxjava-scala-java', \ 'rxjava-contrib:rxjava-swing', \ -'rxjava-contrib:rxjava-android' +'rxjava-contrib:rxjava-android', \ +'rxjava-contrib:rxjava-apache-http' From fbf713dd041726524878203c1617f487cded4e08 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Sat, 21 Sep 2013 07:13:52 +0000 Subject: [PATCH 057/333] [Gradle Release Plugin] - pre tag commit: '0.13.5'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 086739b427..8605a5bbfb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.13.5-SNAPSHOT +version=0.13.5 From 75d8182d1fe00a35f21f2468d7ee110836fad110 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Sat, 21 Sep 2013 07:13:56 +0000 Subject: [PATCH 058/333] [Gradle Release Plugin] - new version commit: '0.13.6-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8605a5bbfb..2c9b97acbb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.13.5 +version=0.13.6-SNAPSHOT From d5dd873e70a67611e2b5fcef388ef29622075632 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 13:29:52 -0700 Subject: [PATCH 059/333] Change 'any' to 'isEmpty'/'exists' --- rxjava-core/src/main/java/rx/Observable.java | 61 ++++---- .../main/java/rx/operators/OperationAny.java | 130 +++++++++++++----- .../main/java/rx/subjects/AsyncSubject.java | 4 +- .../java/rx/subjects/BehaviorSubject.java | 2 +- .../main/java/rx/subjects/PublishSubject.java | 10 +- .../main/java/rx/subjects/ReplaySubject.java | 4 +- 6 files changed, 132 insertions(+), 79 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 26187c67af..456ac92d9e 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -3139,6 +3139,22 @@ public Observable distinct(Func1 keySelector, Com return create(OperationDistinct.distinct(this, keySelector, equalityComparator)); } + /** + * Returns an {@link Observable} that emits true if any element of the source {@link Observable} satisfies + * the given condition, otherwise false. Note: always emit false if the source {@link Observable} is empty. + *

+ * In Rx.Net this is the any operator but renamed in RxJava to better match Java naming idioms. + * + * @param predicate + * The condition to test every element. + * @return A subscription function for creating the target Observable. + * @see MSDN: Observable.Any Note: the description in this page is + * wrong. + */ + public Observable exists(Func1 predicate) { + return create(OperationAny.exists(this, predicate)); + } + /** * Registers an {@link Action0} to be called when this Observable invokes {@link Observer#onCompleted onCompleted} or {@link Observer#onError onError}. *

@@ -4320,7 +4336,19 @@ public Observable> groupBy(final Func1 Observable> groupBy(final Func1 keySelector) { return create(OperationGroupBy.groupBy(this, keySelector)); } - + + /** + * Returns an {@link Observable} that emits true if the source {@link Observable} is empty, otherwise false. + *

+ * In Rx.Net this is negated as the any operator but renamed in RxJava to better match Java naming idioms. + * + * @return A subscription function for creating the target Observable. + * @see MSDN: Observable.Any + */ + public Observable isEmpty() { + return create(OperationAny.isEmpty(this)); + } + /** * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking * operators). @@ -4353,35 +4381,4 @@ private boolean isInternalImplementation(Object o) { return p != null && p.getName().startsWith("rx.operators"); } - /** - * Returns an {@link Observable} that emits true if the source - * {@link Observable} is not empty, otherwise false. - * - * @return A subscription function for creating the target Observable. - * @see MSDN: Observable.Any - */ - public Observable any() { - return create(OperationAny.any(this)); - } - - /** - * Returns an {@link Observable} that emits true if any element - * of the source {@link Observable} satisfies the given condition, otherwise - * false. Note: always emit false if the source - * {@link Observable} is empty. - * - * @param predicate - * The condition to test every element. - * @return A subscription function for creating the target Observable. - * @see MSDN: Observable.Any Note: the description in this page is - * wrong. - */ - public Observable any(Func1 predicate) { - return create(OperationAny.any(this, predicate)); - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationAny.java b/rxjava-core/src/main/java/rx/operators/OperationAny.java index 5423af2f8d..cc4cfd2a64 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAny.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAny.java @@ -1,10 +1,7 @@ package rx.operators; -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 static rx.util.functions.Functions.alwaysTrue; +import static org.mockito.Mockito.*; +import static rx.util.functions.Functions.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -23,23 +20,24 @@ public final class OperationAny { /** - * Returns an {@link Observable} that emits true if the source - * {@link Observable} is not empty, otherwise false. + * Returns an {@link Observable} that emits true if the source {@link Observable} is not empty, otherwise false. * * @param source * The source {@link Observable} to check if not empty. * @return A subscription function for creating the target Observable. */ - public static OnSubscribeFunc any( - Observable source) { - return new Any(source, alwaysTrue()); + public static OnSubscribeFunc any(Observable source) { + return new Any(source, alwaysTrue(), false); + } + + public static OnSubscribeFunc isEmpty(Observable source) { + return new Any(source, alwaysTrue(), true); } /** * Returns an {@link Observable} that emits true if any element * of the source {@link Observable} satisfies the given condition, otherwise - * false. Note: always emit false if the source - * {@link Observable} is empty. + * false. Note: always emit false if the source {@link Observable} is empty. * * @param source * The source {@link Observable} to check if any element @@ -48,20 +46,24 @@ public static OnSubscribeFunc any( * The condition to test every element. * @return A subscription function for creating the target Observable. */ - public static OnSubscribeFunc any( - Observable source, Func1 predicate) { - return new Any(source, predicate); + public static OnSubscribeFunc any(Observable source, Func1 predicate) { + return new Any(source, predicate, false); + } + + public static OnSubscribeFunc exists(Observable source, Func1 predicate) { + return any(source, predicate); } private static class Any implements OnSubscribeFunc { private final Observable source; private final Func1 predicate; + private final boolean returnOnEmpty; - private Any(Observable source, - Func1 predicate) { + private Any(Observable source, Func1 predicate, boolean returnOnEmpty) { this.source = source; this.predicate = predicate; + this.returnOnEmpty = returnOnEmpty; } @Override @@ -69,8 +71,7 @@ public Subscription onSubscribe(final Observer observer) { final SafeObservableSubscription subscription = new SafeObservableSubscription(); return subscription.wrap(source.subscribe(new Observer() { - private final AtomicBoolean hasEmitted = new AtomicBoolean( - false); + private final AtomicBoolean hasEmitted = new AtomicBoolean(false); @Override public void onNext(T value) { @@ -78,12 +79,10 @@ public void onNext(T value) { if (hasEmitted.get() == false) { if (predicate.call(value) == true && hasEmitted.getAndSet(true) == false) { - observer.onNext(true); + observer.onNext(!returnOnEmpty); observer.onCompleted(); - // this will work if the sequence is - // asynchronous, it - // will have no effect on a synchronous - // observable + // this will work if the sequence is asynchronous, it + // will have no effect on a synchronous observable subscription.unsubscribe(); } } @@ -104,7 +103,7 @@ public void onError(Throwable ex) { @Override public void onCompleted() { if (!hasEmitted.get()) { - observer.onNext(false); + observer.onNext(returnOnEmpty); observer.onCompleted(); } } @@ -125,8 +124,21 @@ public void testAnyWithTwoItems() { observable.subscribe(aObserver); verify(aObserver, never()).onNext(false); verify(aObserver, times(1)).onNext(true); - verify(aObserver, never()).onError( - org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testIsEmptyWithTwoItems() { + Observable w = Observable.from(1, 2); + Observable observable = Observable.create(isEmpty(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(true); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } @@ -140,8 +152,21 @@ public void testAnyWithOneItem() { observable.subscribe(aObserver); verify(aObserver, never()).onNext(false); verify(aObserver, times(1)).onNext(true); - verify(aObserver, never()).onError( - org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testIsEmptyWithOneItem() { + Observable w = Observable.from(1); + Observable observable = Observable.create(isEmpty(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(true); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } @@ -155,8 +180,21 @@ public void testAnyWithEmpty() { observable.subscribe(aObserver); verify(aObserver, times(1)).onNext(false); verify(aObserver, never()).onNext(true); - verify(aObserver, never()).onError( - org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testIsEmptyWithEmpty() { + Observable w = Observable.empty(); + Observable observable = Observable.create(isEmpty(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onNext(false); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } @@ -177,11 +215,31 @@ public Boolean call(Integer t1) { observable.subscribe(aObserver); verify(aObserver, never()).onNext(false); verify(aObserver, times(1)).onNext(true); - verify(aObserver, never()).onError( - org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } + @Test + public void testExists1() { + Observable w = Observable.from(1, 2, 3); + Observable observable = Observable.create(exists(w, + new Func1() { + + @Override + public Boolean call(Integer t1) { + return t1 < 2; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(false); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + @Test public void testAnyWithPredicate2() { Observable w = Observable.from(1, 2, 3); @@ -199,8 +257,7 @@ public Boolean call(Integer t1) { observable.subscribe(aObserver); verify(aObserver, times(1)).onNext(false); verify(aObserver, never()).onNext(true); - verify(aObserver, never()).onError( - org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } @@ -222,8 +279,7 @@ public Boolean call(Integer t1) { observable.subscribe(aObserver); verify(aObserver, times(1)).onNext(false); verify(aObserver, never()).onNext(true); - verify(aObserver, never()).onError( - org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } } diff --git a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java index b9080ad341..f50ff21322 100644 --- a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java @@ -169,7 +169,7 @@ public void testCompleted() { private void assertCompletedObserver(Observer aObserver) { verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } @@ -222,7 +222,7 @@ public void testUnsubscribeBeforeCompleted() { private void assertNoOnNextEventsReceived(Observer aObserver) { verify(aObserver, Mockito.never()).onNext(anyString()); - verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); verify(aObserver, Mockito.never()).onCompleted(); } diff --git a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java index 015e071643..1efe3571bd 100644 --- a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java @@ -199,7 +199,7 @@ private void assertCompletedObserver(Observer aObserver) { verify(aObserver, times(1)).onNext("default"); verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } diff --git a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java index 89f3509229..39c19d9b7f 100644 --- a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java @@ -273,7 +273,7 @@ private void assertCompletedObserver(Observer aObserver) verify(aObserver, times(1)).onNext("one"); verify(aObserver, times(1)).onNext("two"); verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } @@ -340,7 +340,7 @@ private void assertCompletedStartingWithThreeObserver(Observer aObserver verify(aObserver, Mockito.never()).onNext("one"); verify(aObserver, Mockito.never()).onNext("two"); verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } @@ -374,7 +374,7 @@ private void assertObservedUntilTwo(Observer aObserver) verify(aObserver, times(1)).onNext("one"); verify(aObserver, times(1)).onNext("two"); verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); verify(aObserver, Mockito.never()).onCompleted(); } @@ -404,7 +404,7 @@ public void testUnsubscribeAfterOnCompleted() { inOrder.verify(anObserver, times(1)).onNext("one"); inOrder.verify(anObserver, times(1)).onNext("two"); inOrder.verify(anObserver, times(1)).onCompleted(); - inOrder.verify(anObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); + inOrder.verify(anObserver, Mockito.never()).onError(any(Throwable.class)); @SuppressWarnings("unchecked") Observer anotherObserver = mock(Observer.class); @@ -414,7 +414,7 @@ public void testUnsubscribeAfterOnCompleted() { inOrder.verify(anotherObserver, Mockito.never()).onNext("one"); inOrder.verify(anotherObserver, Mockito.never()).onNext("two"); inOrder.verify(anotherObserver, times(1)).onCompleted(); - inOrder.verify(anotherObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); + inOrder.verify(anotherObserver, Mockito.never()).onError(any(Throwable.class)); } @Test diff --git a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java index c4dc5c5449..3c106e30bc 100644 --- a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java +++ b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java @@ -215,7 +215,7 @@ private void assertCompletedObserver(Observer aObserver) inOrder.verify(aObserver, times(1)).onNext("one"); inOrder.verify(aObserver, times(1)).onNext("two"); inOrder.verify(aObserver, times(1)).onNext("three"); - inOrder.verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); + inOrder.verify(aObserver, Mockito.never()).onError(any(Throwable.class)); inOrder.verify(aObserver, times(1)).onCompleted(); inOrder.verifyNoMoreInteractions(); } @@ -307,7 +307,7 @@ private void assertObservedUntilTwo(Observer aObserver) verify(aObserver, times(1)).onNext("one"); verify(aObserver, times(1)).onNext("two"); verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, Mockito.never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); verify(aObserver, Mockito.never()).onCompleted(); } From 0a14e458638b69f4e1e576477461edba288bed22 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 13:39:13 -0700 Subject: [PATCH 060/333] Added missing license header --- .../src/main/java/rx/operators/OperationAny.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rxjava-core/src/main/java/rx/operators/OperationAny.java b/rxjava-core/src/main/java/rx/operators/OperationAny.java index cc4cfd2a64..a5f12c224f 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAny.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAny.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; From bb7b7a3283b5ad1f370b290bfc152e82eda00cfe Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 14:10:17 -0700 Subject: [PATCH 061/333] Removing 'comparator` overloads of `distinct` Removing these fairly recently added overloads as they turn out to not be the best approach. Discussion ongoing as to how to implement them at https://github.com/Netflix/RxJava/issues/395 --- rxjava-core/src/main/java/rx/Observable.java | 66 -------------------- 1 file changed, 66 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 1a81e61be7..c21913f86a 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -3046,39 +3046,6 @@ public Observable distinctUntilChanged(Func1 keyS return create(OperationDistinctUntilChanged.distinctUntilChanged(this, keySelector)); } - /** - * Returns an Observable that forwards all items emitted from the source Observable that are sequentially - * distinct according to a comparator. - *

- * - * - * @param equalityComparator - * a comparator for deciding whether two emitted items are equal or not - * @return an Observable of sequentially distinct items - * @see MSDN: Observable.distinctUntilChanged - */ - public Observable distinctUntilChanged(Comparator equalityComparator) { - return create(OperationDistinctUntilChanged.distinctUntilChanged(this, equalityComparator)); - } - - /** - * Returns an Observable that forwards all items emitted from the source Observable that are sequentially - * distinct according to a key selector function and a comparator. - *

- * - * - * @param keySelector - * a function that projects an emitted item to a key value which is used for deciding whether an item is sequentially - * distinct from another one or not - * @param equalityComparator - * a comparator for deciding whether two emitted item keys are equal or not - * @return an Observable of sequentially distinct items - * @see MSDN: Observable.distinctUntilChanged - */ - public Observable distinctUntilChanged(Func1 keySelector, Comparator equalityComparator) { - return create(OperationDistinctUntilChanged.distinctUntilChanged(this, keySelector, equalityComparator)); - } - /** * Returns an Observable that forwards all distinct items emitted from the source Observable. *

@@ -3091,21 +3058,6 @@ public Observable distinct() { return create(OperationDistinct.distinct(this)); } - /** - * Returns an Observable that forwards all items emitted from the source Observable that are distinct according - * to a comparator. - *

- * - * - * @param equalityComparator - * a comparator for deciding whether two emitted items are equal or not - * @return an Observable of distinct items - * @see MSDN: Observable.distinct - */ - public Observable distinct(Comparator equalityComparator) { - return create(OperationDistinct.distinct(this, equalityComparator)); - } - /** * Returns an Observable that forwards all items emitted from the source Observable that are distinct according * to a key selector function. @@ -3122,24 +3074,6 @@ public Observable distinct(Func1 keySelector) { return create(OperationDistinct.distinct(this, keySelector)); } - /** - * Returns an Observable that forwards all items emitted from the source Observable that are distinct according - * to a key selector function and a comparator. - *

- * - * - * @param keySelector - * a function that projects an emitted item to a key value which is used for deciding whether an item is - * distinct from another one or not - * @param equalityComparator - * a comparator for deciding whether two emitted item keys are equal or not - * @return an Observable of distinct items - * @see MSDN: Observable.distinct - */ - public Observable distinct(Func1 keySelector, Comparator equalityComparator) { - return create(OperationDistinct.distinct(this, keySelector, equalityComparator)); - } - /** * Returns the element at a specified index in a sequence. * From aaaf33616fed977fe56ba84812dbd4319e0e5361 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 14:11:32 -0700 Subject: [PATCH 062/333] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index f356aecb7f..4f7e4bafaa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # RxJava Releases # +### Version 0.13.5 + +* Upload to Sonatype failed so version skipped + ### Version 0.13.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.13.4%22)) ### * [Pull 393](https://github.com/Netflix/RxJava/pull/393) Parallel Operator & ObserveOn/ScheduledObserver Fixes From c033ecc02ad972e595313215f85943e49c0f7fe0 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 14:11:56 -0700 Subject: [PATCH 063/333] 0.14.0-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2c9b97acbb..083f66520c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.13.6-SNAPSHOT +version=0.14.0-SNAPSHOT From 25cd11f3dcca48c9aee3b6fbca79bb0f866cb716 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Sun, 22 Sep 2013 00:12:57 +0000 Subject: [PATCH 064/333] [Gradle Release Plugin] - pre tag commit: '0.14.0'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 083f66520c..3cbaf2aad3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.0-SNAPSHOT +version=0.14.0 From 8068cb16166b37676e4cf9e36b9f931ba62a635b Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Sun, 22 Sep 2013 00:13:01 +0000 Subject: [PATCH 065/333] [Gradle Release Plugin] - new version commit: '0.14.1-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3cbaf2aad3..072faeb2e3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.0 +version=0.14.1-SNAPSHOT From c465a5c819ff847cb2583cfd965240603eb3235f Mon Sep 17 00:00:00 2001 From: zsxwing Date: Sun, 22 Sep 2013 10:29:22 +0800 Subject: [PATCH 066/333] Implemented the 'DefaultIfEmpty' operator. See #34 --- rxjava-core/src/main/java/rx/Observable.java | 14 ++ .../rx/operators/OperationDefaultIfEmpty.java | 122 ++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index c21913f86a..e3dc10f9f9 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -36,6 +36,7 @@ import rx.operators.OperationCombineLatest; import rx.operators.OperationConcat; import rx.operators.OperationDebounce; +import rx.operators.OperationDefaultIfEmpty; import rx.operators.OperationDefer; import rx.operators.OperationDematerialize; import rx.operators.OperationDistinct; @@ -3864,6 +3865,19 @@ public Observable firstOrDefault(Func1 predicate, T defau return create(OperationFirstOrDefault.firstOrDefault(this, predicate, defaultValue)); } + /** + * Returns the elements of the specified sequence or the specified default + * value in a singleton sequence if the sequence is empty. + * + * @param defaultValue + * The value to return if the sequence is empty. + * @return An observable sequence that contains the specified default value + * if the source is empty; otherwise, the elements of the source + * itself. + */ + public Observable defaultIfEmpty(T defaultValue) { + return create(OperationDefaultIfEmpty.defaultIfEmpty(this, defaultValue)); + } /** * Returns an Observable that emits only the first num items emitted by the source diff --git a/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java b/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java new file mode 100644 index 0000000000..195422dadc --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java @@ -0,0 +1,122 @@ +package rx.operators; + +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 org.junit.Test; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; + +/** + * Returns the elements of the specified sequence or the specified default value + * in a singleton sequence if the sequence is empty. + */ +public class OperationDefaultIfEmpty { + + /** + * Returns the elements of the specified sequence or the specified default + * value in a singleton sequence if the sequence is empty. + * + * @param source + * The sequence to return the specified value for if it is empty. + * @param defaultValue + * The value to return if the sequence is empty. + * @return An observable sequence that contains the specified default value + * if the source is empty; otherwise, the elements of the source + * itself. + */ + public static OnSubscribeFunc defaultIfEmpty( + Observable source, T defaultValue) { + return new DefaultIfEmpty(source, defaultValue); + } + + private static class DefaultIfEmpty implements OnSubscribeFunc { + + private final Observable source; + private final T defaultValue; + + private DefaultIfEmpty(Observable source, T defaultValue) { + this.source = source; + this.defaultValue = defaultValue; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + final SafeObservableSubscription subscription = new SafeObservableSubscription(); + return subscription.wrap(source.subscribe(new Observer() { + + private volatile boolean hasEmitted = false; + + @Override + public void onNext(T value) { + try { + hasEmitted = true; + observer.onNext(value); + } catch (Throwable ex) { + observer.onError(ex); + // this will work if the sequence is asynchronous, it + // will have no effect on a synchronous observable + subscription.unsubscribe(); + } + } + + @Override + public void onError(Throwable ex) { + observer.onError(ex); + } + + @Override + public void onCompleted() { + if (hasEmitted) { + observer.onCompleted(); + } else { + observer.onNext(defaultValue); + observer.onCompleted(); + } + } + })); + } + } + + public static class UnitTest { + + @Test + public void testDefaultIfEmpty() { + Observable source = Observable.from(1, 2, 3); + Observable observable = Observable.create(defaultIfEmpty( + source, 10)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(10); + verify(aObserver, times(1)).onNext(1); + verify(aObserver, times(1)).onNext(2); + verify(aObserver, times(1)).onNext(3); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testDefaultIfEmptyWithEmpty() { + Observable source = Observable.empty(); + Observable observable = Observable.create(defaultIfEmpty( + source, 10)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(10); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + } +} From 4d0ac16b1261ef9537c26e1296cab8d6478ed81b Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 20:33:10 -0700 Subject: [PATCH 067/333] Version 0.14.0 --- CHANGES.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 4f7e4bafaa..d8fbe02092 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,19 @@ # RxJava Releases # +### Version 0.14.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.0%22)) ### + +Further progress to the Scala adaptor and a handful of new operators. + +Bump to 0.14.0 due to small breaking change to `distinct` operator removing overloaded methods with `Comparator`. These methods were added in 0.13.2 and determined to be incorrect. + +This release also includes a new contrib module, [rxjava-apache-http](https://github.com/Netflix/RxJava/tree/master/rxjava-contrib/rxjava-apache-http) that provides an Observable API to the Apache HttpAsyncClient. + +* [Pull 396](https://github.com/Netflix/RxJava/pull/396) Add missing methods to Scala Adaptor +* [Pull 390](https://github.com/Netflix/RxJava/pull/390) Operators: ElementAt and ElementAtOrDefault +* [Pull 398](https://github.com/Netflix/RxJava/pull/398) Operators: IsEmpty and Exists (instead of Any) +* [Pull 397](https://github.com/Netflix/RxJava/pull/397) Observable API for Apache HttpAsyncClient 4.0 +* [Pull 400](https://github.com/Netflix/RxJava/pull/400) Removing `comparator` overloads of `distinct` + ### Version 0.13.5 * Upload to Sonatype failed so version skipped From 17170e4d7c564c4e361b52521cee627bddf94a23 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 20:47:11 -0700 Subject: [PATCH 068/333] Create README.md --- rxjava-contrib/rxjava-apache-http/README.md | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 rxjava-contrib/rxjava-apache-http/README.md diff --git a/rxjava-contrib/rxjava-apache-http/README.md b/rxjava-contrib/rxjava-apache-http/README.md new file mode 100644 index 0000000000..26b0521b83 --- /dev/null +++ b/rxjava-contrib/rxjava-apache-http/README.md @@ -0,0 +1,35 @@ +# rxjava-apache-http + +Observable API for Apache [HttpAsyncClient](http://hc.apache.org/httpcomponents-asyncclient-dev/) + +It is aware of Content-Type `text/event-stream` and will stream each event via `Observer.onNext`. + +Other Content-Types will be returned as a single call to `Observer.onNext`. + +Main Classes: + +- (ObservableHttp)[https://github.com/Netflix/RxJava/blob/master/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttp.java] +- (ObservableHttpResponse)[https://github.com/Netflix/RxJava/blob/master/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttpResponse.java] + + +# Binaries + +Binaries and dependency information for Maven, Ivy, Gradle and others can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Ccom.netflix.rxjava). + +Example for [Maven](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22rxjava-apache-http%22): + +```xml + + com.netflix.rxjava + rxjava-apache-http + x.y.z + +``` + +and for Ivy: + +```xml + +``` + +# Sample usage From 60c58d80b878440ad2a5b4a9ab3179101b6f2d18 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 20:47:48 -0700 Subject: [PATCH 069/333] Update README.md --- rxjava-contrib/rxjava-apache-http/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rxjava-contrib/rxjava-apache-http/README.md b/rxjava-contrib/rxjava-apache-http/README.md index 26b0521b83..5a8b8cfe0d 100644 --- a/rxjava-contrib/rxjava-apache-http/README.md +++ b/rxjava-contrib/rxjava-apache-http/README.md @@ -8,8 +8,8 @@ Other Content-Types will be returned as a single call to `Observer.onNext`. Main Classes: -- (ObservableHttp)[https://github.com/Netflix/RxJava/blob/master/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttp.java] -- (ObservableHttpResponse)[https://github.com/Netflix/RxJava/blob/master/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttpResponse.java] +- [ObservableHttp](https://github.com/Netflix/RxJava/blob/master/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttp.java) +- [ObservableHttpResponse](https://github.com/Netflix/RxJava/blob/master/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttpResponse.java) # Binaries From 4b676ae4a6d3f88acb9c9fe2c4213cba59e90f33 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 21:04:01 -0700 Subject: [PATCH 070/333] Update README.md --- rxjava-contrib/rxjava-apache-http/README.md | 53 ++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/rxjava-contrib/rxjava-apache-http/README.md b/rxjava-contrib/rxjava-apache-http/README.md index 5a8b8cfe0d..f14b22f718 100644 --- a/rxjava-contrib/rxjava-apache-http/README.md +++ b/rxjava-contrib/rxjava-apache-http/README.md @@ -32,4 +32,55 @@ and for Ivy: ``` -# Sample usage +# Sample Usage + +### Create a Request + +```java +ObservableHttp.createGet("http://www.wikipedia.com", httpClient).toObservable(); +ObservableHttp.createRequest(HttpAsyncMethods.createGet("http://www.wikipedia.com"), httpClient).toObservable(); +``` + +### Http Client + +A basic default client: + +```java +CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault(); +``` + +or a custom client with configuration options: + +```java +final RequestConfig requestConfig = RequestConfig.custom() + .setSocketTimeout(3000) + .setConnectTimeout(3000).build(); +final CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() + .setDefaultRequestConfig(requestConfig) + .setMaxConnPerRoute(20) + .setMaxConnTotal(50) + .build(); +``` + +### Normal Http GET + +Execute a request and transform the `byte[]` reponse to a `String`: + +```groovy + ObservableHttp.createRequest(HttpAsyncMethods.createGet("http://www.wikipedia.com"), client) + .toObservable() + .flatMap({ ObservableHttpResponse response -> + return response.getContent().map({ byte[] bb -> + return new String(bb); + }); + }) + .toBlockingObservable() + .forEach({ String resp -> + println(resp); + }); +``` + + + + + From 15fd9ddd6d0b645b89bc8a99713822f741e9c239 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 21:15:03 -0700 Subject: [PATCH 071/333] Update README.md --- rxjava-contrib/rxjava-apache-http/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/rxjava-contrib/rxjava-apache-http/README.md b/rxjava-contrib/rxjava-apache-http/README.md index f14b22f718..e6d8b405a9 100644 --- a/rxjava-contrib/rxjava-apache-http/README.md +++ b/rxjava-contrib/rxjava-apache-http/README.md @@ -76,10 +76,29 @@ Execute a request and transform the `byte[]` reponse to a `String`: }) .toBlockingObservable() .forEach({ String resp -> + // this will be invoked once with the response println(resp); }); ``` +### Streaming Http GET with ServerSentEvents (text/event-stream) Response + +Execute a request and transform the `byte[]` response of each event to a `String`: + +```groovy + ObservableHttp.createRequest(HttpAsyncMethods.createGet("http://hostname/event.stream"), client) + .toObservable() + .flatMap({ ObservableHttpResponse response -> + return response.getContent().map({ byte[] bb -> + return new String(bb); + }); + }) + .toBlockingObservable() + .forEach({ String resp -> + // this will be invoked for each event + println(resp); + }); +``` From 3282dffec0b507997a0735d9e856f86d3b451ebc Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 21:16:12 -0700 Subject: [PATCH 072/333] Update README.md --- rxjava-contrib/rxjava-apache-http/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rxjava-contrib/rxjava-apache-http/README.md b/rxjava-contrib/rxjava-apache-http/README.md index e6d8b405a9..d0bae861e1 100644 --- a/rxjava-contrib/rxjava-apache-http/README.md +++ b/rxjava-contrib/rxjava-apache-http/README.md @@ -81,7 +81,7 @@ Execute a request and transform the `byte[]` reponse to a `String`: }); ``` -### Streaming Http GET with ServerSentEvents (text/event-stream) Response +### Streaming Http GET with [Server-Sent Events (text/event-stream)](http://www.w3.org/TR/eventsource/) Response Execute a request and transform the `byte[]` response of each event to a `String`: From d92b15197958d61be70f265dbe272280dae761f8 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 21:26:49 -0700 Subject: [PATCH 073/333] Fixes to rxjava-apache-http - made Content-Type inspection more reliable - other small improvments --- .../java/rx/apache/http/ObservableHttp.java | 2 +- .../consumers/ResponseConsumerDelegate.java | 4 +- .../http/examples/ExampleObservableHttp.java | 69 +++++++++---------- 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttp.java b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttp.java index c8d87c8334..7f34365eb4 100644 --- a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttp.java +++ b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/ObservableHttp.java @@ -143,7 +143,7 @@ public Subscription onSubscribe(final Observer o final CompositeSubscription parentSubscription = new CompositeSubscription(); // return a Subscription that wraps the Future so it can be cancelled - parentSubscription.add(Subscriptions.create(client.execute(requestProducer, new ResponseConsumerDelegate(observer, parentSubscription), + parentSubscription.add(Subscriptions.from(client.execute(requestProducer, new ResponseConsumerDelegate(observer, parentSubscription), new FutureCallback() { @Override diff --git a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java index 3060d67bca..7eab30b441 100644 --- a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java +++ b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java @@ -52,7 +52,9 @@ public ResponseConsumerDelegate(final Observer o @Override protected void onResponseReceived(HttpResponse response) throws HttpException, IOException { // when we receive the response with headers we evaluate what type of consumer we want - if (response.getFirstHeader("Content-Type").getValue().equals("text/event-stream")) { + if (response.getFirstHeader("Content-Type").getValue().contains("text/event-stream")) { + // use 'contains' instead of equals since Content-Type can contain additional information + // such as charset ... see here: http://www.w3.org/International/O-HTTP-charset consumer = new ResponseConsumerEventStream(observer, subscription); } else { consumer = new ResponseConsumerBasic(observer, subscription); diff --git a/rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java b/rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java index 763916f861..3f396a3894 100644 --- a/rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java +++ b/rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java @@ -77,41 +77,40 @@ public void call(String resp) { protected static void executeStreamingViaObservableHttpWithForEach(final HttpAsyncClient client) throws URISyntaxException, IOException, InterruptedException { System.out.println("---- executeStreamingViaObservableHttpWithForEach"); - for (int i = 0; i < 5; i++) { - final int c = i + 1; - ObservableHttp.createRequest(HttpAsyncMethods.createGet("http://ec2-54-211-91-164.compute-1.amazonaws.com:8077/eventbus.stream?topic=hystrix-metrics"), client) - .toObservable() - .flatMap(new Func1>() { - - @Override - public Observable call(ObservableHttpResponse response) { - return response.getContent().map(new Func1() { - - @Override - public String call(byte[] bb) { - return new String(bb); - } - - }); - } - }) - .filter(new Func1() { - - @Override - public Boolean call(String t1) { - return !t1.startsWith(": ping"); - } - }) - .take(3) - .toBlockingObservable() - .forEach(new Action1() { - - @Override - public void call(String resp) { - System.out.println("Response [" + c + "]: " + resp + " (" + resp.length() + ")"); - } - }); - } + // URL against https://github.com/Netflix/Hystrix/tree/master/hystrix-examples-webapp + // More information at https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-metrics-event-stream + ObservableHttp.createRequest(HttpAsyncMethods.createGet("http://localhost:8989/hystrix-examples-webapp/hystrix.stream"), client) + .toObservable() + .flatMap(new Func1>() { + + @Override + public Observable call(ObservableHttpResponse response) { + return response.getContent().map(new Func1() { + + @Override + public String call(byte[] bb) { + return new String(bb); + } + + }); + } + }) + .filter(new Func1() { + + @Override + public Boolean call(String t1) { + return !t1.startsWith(": ping"); + } + }) + .take(3) + .toBlockingObservable() + .forEach(new Action1() { + + @Override + public void call(String resp) { + System.out.println(resp); + } + }); } } From 6cfcb229c718924cd5be43eaf9bc5274e6839914 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Sun, 22 Sep 2013 04:36:10 +0000 Subject: [PATCH 074/333] [Gradle Release Plugin] - pre tag commit: '0.14.1'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 072faeb2e3..e701908e30 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.1-SNAPSHOT +version=0.14.1 From 4b38044c2f1d693aa5b5282de279d0977e1c4003 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Sun, 22 Sep 2013 04:36:14 +0000 Subject: [PATCH 075/333] [Gradle Release Plugin] - new version commit: '0.14.2-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e701908e30..d3291d5969 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.1 +version=0.14.2-SNAPSHOT From ead8dc4ab8511855b8f06295e5d6b34c084013dd Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 21:49:03 -0700 Subject: [PATCH 076/333] Version 0.14.1 --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index d8fbe02092..e5b919e7f2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # RxJava Releases # +### Version 0.14.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.1%22)) ### + +* [Pull 402](https://github.com/Netflix/RxJava/pull/402) rxjava-apache-http improvements + ### Version 0.14.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.0%22)) ### Further progress to the Scala adaptor and a handful of new operators. From 93c1c9d5602948509d7e4af79e43cd3124f300c4 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 21:52:26 -0700 Subject: [PATCH 077/333] Update README.md --- rxjava-contrib/rxjava-apache-http/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rxjava-contrib/rxjava-apache-http/README.md b/rxjava-contrib/rxjava-apache-http/README.md index d0bae861e1..98c99a9f2f 100644 --- a/rxjava-contrib/rxjava-apache-http/README.md +++ b/rxjava-contrib/rxjava-apache-http/README.md @@ -100,6 +100,12 @@ Execute a request and transform the `byte[]` response of each event to a `String }); ``` +An example event-stream is from [Hystrix](https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-metrics-event-stream) used for streaming metrics. An [example webapp](https://github.com/Netflix/Hystrix/tree/master/hystrix-examples-webapp) can be used to test. +Output looks like: +``` +data: {"type":"HystrixCommand","name":"CreditCardCommand","group":"CreditCard","currentTime":1379823924934,"isCircuitBreakerOpen":false,"errorPercentage":0,"errorCount":0,"requestCount":0,"rollingCountCollapsedRequests":0,"rollingCountExceptionsThrown":0,"rollingCountFailure":0,"rollingCountFallbackFailure":0,"rollingCountFallbackRejection":0,"rollingCountFallbackSuccess":0,"rollingCountResponsesFromCache":0,"rollingCountSemaphoreRejected":0,"rollingCountShortCircuited":0,"rollingCountSuccess":0,"rollingCountThreadPoolRejected":0,"rollingCountTimeout":0,"currentConcurrentExecutionCount":0,"latencyExecute_mean":0,"latencyExecute":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"latencyTotal_mean":0,"latencyTotal":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"propertyValue_circuitBreakerRequestVolumeThreshold":20,"propertyValue_circuitBreakerSleepWindowInMilliseconds":5000,"propertyValue_circuitBreakerErrorThresholdPercentage":50,"propertyValue_circuitBreakerForceOpen":false,"propertyValue_circuitBreakerForceClosed":false,"propertyValue_circuitBreakerEnabled":true,"propertyValue_executionIsolationStrategy":"THREAD","propertyValue_executionIsolationThreadTimeoutInMilliseconds":3000,"propertyValue_executionIsolationThreadInterruptOnTimeout":true,"propertyValue_executionIsolationThreadPoolKeyOverride":null,"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,"propertyValue_requestCacheEnabled":true,"propertyValue_requestLogEnabled":true,"reportingHosts":1} +data: {"type":"HystrixCommand","name":"GetPaymentInformationCommand","group":"PaymentInformation","currentTime":1379823924934,"isCircuitBreakerOpen":false,"errorPercentage":0,"errorCount":0,"requestCount":0,"rollingCountCollapsedRequests":0,"rollingCountExceptionsThrown":0,"rollingCountFailure":0,"rollingCountFallbackFailure":0,"rollingCountFallbackRejection":0,"rollingCountFallbackSuccess":0,"rollingCountResponsesFromCache":0,"rollingCountSemaphoreRejected":0,"rollingCountShortCircuited":0,"rollingCountSuccess":0,"rollingCountThreadPoolRejected":0,"rollingCountTimeout":0,"currentConcurrentExecutionCount":0,"latencyExecute_mean":0,"latencyExecute":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"latencyTotal_mean":0,"latencyTotal":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"propertyValue_circuitBreakerRequestVolumeThreshold":20,"propertyValue_circuitBreakerSleepWindowInMilliseconds":5000,"propertyValue_circuitBreakerErrorThresholdPercentage":50,"propertyValue_circuitBreakerForceOpen":false,"propertyValue_circuitBreakerForceClosed":false,"propertyValue_circuitBreakerEnabled":true,"propertyValue_executionIsolationStrategy":"THREAD","propertyValue_executionIsolationThreadTimeoutInMilliseconds":1000,"propertyValue_executionIsolationThreadInterruptOnTimeout":true,"propertyValue_executionIsolationThreadPoolKeyOverride":null,"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,"propertyValue_requestCacheEnabled":true,"propertyValue_requestLogEnabled":true,"reportingHosts":1} +``` From fa72a27727573ab1c4892a3c24f0b65613ce5686 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Sat, 21 Sep 2013 22:07:16 -0700 Subject: [PATCH 078/333] Update README.md --- rxjava-contrib/rxjava-apache-http/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rxjava-contrib/rxjava-apache-http/README.md b/rxjava-contrib/rxjava-apache-http/README.md index 98c99a9f2f..d921b3047e 100644 --- a/rxjava-contrib/rxjava-apache-http/README.md +++ b/rxjava-contrib/rxjava-apache-http/README.md @@ -54,7 +54,7 @@ or a custom client with configuration options: ```java final RequestConfig requestConfig = RequestConfig.custom() .setSocketTimeout(3000) - .setConnectTimeout(3000).build(); + .setConnectTimeout(500).build(); final CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() .setDefaultRequestConfig(requestConfig) .setMaxConnPerRoute(20) From c42be1dcbedd103f27bd8a81e71cfd01ee3d5634 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 23 Sep 2013 13:12:08 +0800 Subject: [PATCH 079/333] Implemented 'cast' operator --- rxjava-core/src/main/java/rx/Observable.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index c21913f86a..19cea34b99 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4332,6 +4332,23 @@ public BlockingObservable toBlockingObservable() { return BlockingObservable.from(this); } + /** + * Converts the elements of an observable sequence to the specified type. + * + * @return An observable sequence that contains each element of the source + * sequence converted to the specified type. + * + * @see MSDN: Observable.Cast + */ + public Observable cast() { + return map(new Func1() { + @SuppressWarnings("unchecked") + public R call(T t) { + return (R) t; + } + }); + } + /** * Whether a given {@link Function} is an internal implementation inside rx.* packages or not. *

From 97cabf5a6b82d8b2b034f8d05a52df4f577ac028 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 23 Sep 2013 13:14:46 +0800 Subject: [PATCH 080/333] Added the MSDN link --- rxjava-core/src/main/java/rx/Observable.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index e3dc10f9f9..667ec0db79 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -3874,6 +3874,8 @@ public Observable firstOrDefault(Func1 predicate, T defau * @return An observable sequence that contains the specified default value * if the source is empty; otherwise, the elements of the source * itself. + * + * @see MSDN: Observable.DefaultIfEmpty */ public Observable defaultIfEmpty(T defaultValue) { return create(OperationDefaultIfEmpty.defaultIfEmpty(this, defaultValue)); From 6788900bd336b5fc16230fc4f20d0ae1b52a82aa Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 23 Sep 2013 14:20:27 +0800 Subject: [PATCH 081/333] Added a klass parameter --- rxjava-core/src/main/java/rx/Observable.java | 17 ++--- .../main/java/rx/operators/OperationCast.java | 67 +++++++++++++++++++ 2 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationCast.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 19cea34b99..edb2222a38 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -33,6 +33,7 @@ import rx.operators.OperationAverage; import rx.operators.OperationBuffer; import rx.operators.OperationCache; +import rx.operators.OperationCast; import rx.operators.OperationCombineLatest; import rx.operators.OperationConcat; import rx.operators.OperationDebounce; @@ -4335,18 +4336,18 @@ public BlockingObservable toBlockingObservable() { /** * Converts the elements of an observable sequence to the specified type. * + * @param klass + * The target class type which the elements will be converted to. + * * @return An observable sequence that contains each element of the source * sequence converted to the specified type. * - * @see MSDN: Observable.Cast + * @see MSDN: + * Observable.Cast */ - public Observable cast() { - return map(new Func1() { - @SuppressWarnings("unchecked") - public R call(T t) { - return (R) t; - } - }); + public Observable cast(final Class klass) { + return create(OperationCast.cast(this, klass)); } /** diff --git a/rxjava-core/src/main/java/rx/operators/OperationCast.java b/rxjava-core/src/main/java/rx/operators/OperationCast.java new file mode 100644 index 0000000000..e0a20a57ea --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationCast.java @@ -0,0 +1,67 @@ +package rx.operators; + +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 org.junit.Test; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.util.functions.Func1; + +/** + * Converts the elements of an observable sequence to the specified type. + */ +public class OperationCast { + + public static OnSubscribeFunc cast( + Observable source, final Class klass) { + return OperationMap.map(source, new Func1() { + @SuppressWarnings("unchecked") + public R call(T t) { + if (klass.isAssignableFrom(t.getClass())) { + return (R) t; + } else { + throw new ClassCastException(t.getClass() + + " cannot be cast to " + klass); + } + } + }); + } + + public static class UnitTest { + + @Test + public void testCast() { + Observable source = Observable.from(1, 2); + Observable observable = Observable.create(cast(source, + Integer.class)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(1); + verify(aObserver, times(1)).onNext(1); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testCastWithWrongType() { + Observable source = Observable.from(1, 2); + Observable observable = Observable.create(cast(source, + Boolean.class)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onError( + org.mockito.Matchers.any(ClassCastException.class)); + } + } + +} From cde74498f3bb397ce1301ff2031f4678b58d8d83 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 23 Sep 2013 14:39:06 +0800 Subject: [PATCH 082/333] Implemented the 'ofType' operator --- rxjava-core/src/main/java/rx/Observable.java | 21 +++++++++++++++++++ .../src/test/java/rx/ObservableTests.java | 17 +++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index edb2222a38..9d2ded57f2 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4350,6 +4350,27 @@ public Observable cast(final Class klass) { return create(OperationCast.cast(this, klass)); } + /** + * Filters the elements of an observable sequence based on the specified + * type. + * + * @param klass + * The class type to filter the elements in the source sequence + * on. + * + * @return An observable sequence that contains elements from the input + * sequence of type klass. + * + * @see MSDN: Observable.OfType + */ + public Observable ofType(final Class klass) { + return filter(new Func1() { + public Boolean call(T t) { + return klass.isAssignableFrom(t.getClass()); + } + }).cast(klass); + } + /** * Whether a given {@link Function} is an internal implementation inside rx.* packages or not. *

diff --git a/rxjava-core/src/test/java/rx/ObservableTests.java b/rxjava-core/src/test/java/rx/ObservableTests.java index 92af02b1dd..e278276b39 100644 --- a/rxjava-core/src/test/java/rx/ObservableTests.java +++ b/rxjava-core/src/test/java/rx/ObservableTests.java @@ -701,4 +701,21 @@ public void onNext(String v) { fail("It should be a NumberFormatException"); } } + + @Test + public void testOfType() { + Observable observable = Observable.from(1, "abc", false, 2L).ofType(String.class); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(1); + verify(aObserver, times(1)).onNext("abc"); + verify(aObserver, never()).onNext(false); + verify(aObserver, never()).onNext(2L); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + } \ No newline at end of file From e0caeeea9e5d2c7e6c25342503a1e80d3217076d Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 23 Sep 2013 18:56:20 +0800 Subject: [PATCH 083/333] Used 'cast' to remove SuppressWarnings --- rxjava-core/src/main/java/rx/operators/OperationCast.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationCast.java b/rxjava-core/src/main/java/rx/operators/OperationCast.java index e0a20a57ea..a4887c3afe 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationCast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationCast.java @@ -20,14 +20,8 @@ public class OperationCast { public static OnSubscribeFunc cast( Observable source, final Class klass) { return OperationMap.map(source, new Func1() { - @SuppressWarnings("unchecked") public R call(T t) { - if (klass.isAssignableFrom(t.getClass())) { - return (R) t; - } else { - throw new ClassCastException(t.getClass() - + " cannot be cast to " + klass); - } + return klass.cast(t); } }); } From 02fd9524a93d4fbb22353e9a3d15b6ef7653434b Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 23 Sep 2013 19:09:18 +0800 Subject: [PATCH 084/333] Used 'isInstance' to replace 'isAssignableFrom' and added a unit test --- rxjava-core/src/main/java/rx/Observable.java | 2 +- .../src/test/java/rx/ObservableTests.java | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 9d2ded57f2..35f3d0fe21 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4366,7 +4366,7 @@ public Observable cast(final Class klass) { public Observable ofType(final Class klass) { return filter(new Func1() { public Boolean call(T t) { - return klass.isAssignableFrom(t.getClass()); + return klass.isInstance(t); } }).cast(klass); } diff --git a/rxjava-core/src/test/java/rx/ObservableTests.java b/rxjava-core/src/test/java/rx/ObservableTests.java index e278276b39..297bb1ff47 100644 --- a/rxjava-core/src/test/java/rx/ObservableTests.java +++ b/rxjava-core/src/test/java/rx/ObservableTests.java @@ -20,6 +20,8 @@ import static org.mockito.Mockito.*; import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -718,4 +720,25 @@ public void testOfType() { verify(aObserver, times(1)).onCompleted(); } + @Test + public void testOfTypeWithPolymorphism() { + ArrayList l1 = new ArrayList(); + l1.add(1); + LinkedList l2 = new LinkedList(); + l2.add(2); + + @SuppressWarnings("rawtypes") + Observable observable = Observable.from(l1, l2, "123").ofType(List.class); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(l1); + verify(aObserver, times(1)).onNext(l2); + verify(aObserver, never()).onNext("123"); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + } \ No newline at end of file From 63ae2b19de891e021dae2497f6829b70494475c6 Mon Sep 17 00:00:00 2001 From: John Marks Date: Tue, 24 Sep 2013 12:11:49 +0100 Subject: [PATCH 085/333] Finished RefCount - all tests pass --- .../src/main/java/rx/observables/ConnectableObservable.java | 1 + 1 file changed, 1 insertion(+) diff --git a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java index c5970435e5..a208673028 100644 --- a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java +++ b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java @@ -61,6 +61,7 @@ public Observable refCount() { * Returns an observable sequence that stays connected to the source as long * as there is at least one subscription to the observable sequence. * @return a {@link Observable} + * @param that a {@link ConnectableObservable} */ public static Observable refCount(ConnectableObservable that) { return Observable.create(OperationRefCount.refCount(that)); From 1402dbfed5b8768992d95b227b852b4338b6b5fc Mon Sep 17 00:00:00 2001 From: John Marks Date: Tue, 24 Sep 2013 12:22:02 +0100 Subject: [PATCH 086/333] Removed classes dir --- .../rxjava-core/rx/operators/README.txt | 5 ---- .../rxjava-core/rx/operators/package.html | 23 ------------------- classes/test/rxjava-core/README.md | 8 ------- .../test/rxjava-core/rx/operators/README.txt | 5 ---- .../rxjava-core/rx/operators/package.html | 23 ------------------- 5 files changed, 64 deletions(-) delete mode 100644 classes/production/rxjava-core/rx/operators/README.txt delete mode 100644 classes/production/rxjava-core/rx/operators/package.html delete mode 100644 classes/test/rxjava-core/README.md delete mode 100644 classes/test/rxjava-core/rx/operators/README.txt delete mode 100644 classes/test/rxjava-core/rx/operators/package.html diff --git a/classes/production/rxjava-core/rx/operators/README.txt b/classes/production/rxjava-core/rx/operators/README.txt deleted file mode 100644 index c2d441a10c..0000000000 --- a/classes/production/rxjava-core/rx/operators/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -This package "rx.operators" is for internal implementation details and can change at any time. - -It is excluded from the public Javadocs (http://netflix.github.io/RxJava/javadoc/) and should not be relied upon by any code. - -In short, changes to public signatures of these classes will not be accounted for in the versioning of RxJava. \ No newline at end of file diff --git a/classes/production/rxjava-core/rx/operators/package.html b/classes/production/rxjava-core/rx/operators/package.html deleted file mode 100644 index 80ba7542bf..0000000000 --- a/classes/production/rxjava-core/rx/operators/package.html +++ /dev/null @@ -1,23 +0,0 @@ - - -

Operators that allow composing Observables to transform and - manipulate data in an asynchronous, functional and thread-safe manner.

-

The operators are all exposed via the ObservableExtensions class

- - \ No newline at end of file diff --git a/classes/test/rxjava-core/README.md b/classes/test/rxjava-core/README.md deleted file mode 100644 index 74b6c91536..0000000000 --- a/classes/test/rxjava-core/README.md +++ /dev/null @@ -1,8 +0,0 @@ -This test folder only contains performance and functional/integration style tests. - -The unit tests themselves are embedded as inner classes of the Java code (such as here: [rxjava-core/src/main/java/rx/operators](https://github.com/Netflix/RxJava/tree/master/rxjava-core/src/main/java/rx/operators)). - -* For an explanation of this design choice see -Ben J. Christensen's [JUnit Tests as Inner Classes](http://benjchristensen.com/2011/10/23/junit-tests-as-inner-classes/). - -Also, each of the language adaptors has a /src/test/ folder which further testing (see Groovy for an example: [language-adaptors/rxjava-groovy/src/test](https://github.com/Netflix/RxJava/tree/master/language-adaptors/rxjava-groovy/src/test)). diff --git a/classes/test/rxjava-core/rx/operators/README.txt b/classes/test/rxjava-core/rx/operators/README.txt deleted file mode 100644 index c2d441a10c..0000000000 --- a/classes/test/rxjava-core/rx/operators/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -This package "rx.operators" is for internal implementation details and can change at any time. - -It is excluded from the public Javadocs (http://netflix.github.io/RxJava/javadoc/) and should not be relied upon by any code. - -In short, changes to public signatures of these classes will not be accounted for in the versioning of RxJava. \ No newline at end of file diff --git a/classes/test/rxjava-core/rx/operators/package.html b/classes/test/rxjava-core/rx/operators/package.html deleted file mode 100644 index 80ba7542bf..0000000000 --- a/classes/test/rxjava-core/rx/operators/package.html +++ /dev/null @@ -1,23 +0,0 @@ - - -

Operators that allow composing Observables to transform and - manipulate data in an asynchronous, functional and thread-safe manner.

-

The operators are all exposed via the ObservableExtensions class

- - \ No newline at end of file From e0f57f17cc18ff4d2824984afa648e4ebdf6bc4b Mon Sep 17 00:00:00 2001 From: John Marks Date: Tue, 24 Sep 2013 12:23:13 +0100 Subject: [PATCH 087/333] Minor tweak --- rxjava-core/src/main/java/rx/Observable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 4d40ce4350..64fdf2ad62 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4339,7 +4339,7 @@ public BlockingObservable toBlockingObservable() { * * NOTE: If strong reasons for not depending on package names comes up then the implementation of this method can change to looking for a marker interface. * - * @param f + * @param o * @return {@code true} if the given function is an internal implementation, and {@code false} otherwise. */ private boolean isInternalImplementation(Object o) { From d4b04d88396bfd0e3932e5c53219cf40adc7fb81 Mon Sep 17 00:00:00 2001 From: John Marks Date: Tue, 24 Sep 2013 13:01:25 +0100 Subject: [PATCH 088/333] Implemented publishLast --- rxjava-core/src/main/java/rx/Observable.java | 9 ++++ .../src/test/java/rx/ObservableTests.java | 42 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 64fdf2ad62..d72201accb 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -83,6 +83,7 @@ import rx.plugins.RxJavaErrorHandler; import rx.plugins.RxJavaObservableExecutionHook; import rx.plugins.RxJavaPlugins; +import rx.subjects.AsyncSubject; import rx.subjects.PublishSubject; import rx.subjects.ReplaySubject; import rx.subjects.Subject; @@ -3634,6 +3635,14 @@ public ConnectableObservable publish() { return OperationMulticast.multicast(this, PublishSubject. create()); } + /** + * Returns a {@link ConnectableObservable} that shares a single subscription that contains the last notification only. + * @return a {@link ConnectableObservable} + */ + public ConnectableObservable publishLast() { + return OperationMulticast.multicast(this, AsyncSubject. create()); + } + /** * Synonymous with reduce(). *

diff --git a/rxjava-core/src/test/java/rx/ObservableTests.java b/rxjava-core/src/test/java/rx/ObservableTests.java index 92af02b1dd..d70a5de8ce 100644 --- a/rxjava-core/src/test/java/rx/ObservableTests.java +++ b/rxjava-core/src/test/java/rx/ObservableTests.java @@ -487,6 +487,48 @@ public void call(String v) { } } + @Test + public void testPublishLast() throws InterruptedException { + final AtomicInteger count = new AtomicInteger(); + ConnectableObservable connectable = Observable.create(new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(final Observer observer) { + count.incrementAndGet(); + final BooleanSubscription subscription = new BooleanSubscription(); + new Thread(new Runnable() { + @Override + public void run() { + observer.onNext("first"); + observer.onNext("last"); + observer.onCompleted(); + } + }).start(); + return subscription; + } + }).publishLast(); + + // subscribe once + final CountDownLatch latch = new CountDownLatch(1); + connectable.subscribe(new Action1() { + @Override + public void call(String value) { + assertEquals("last", value); + latch.countDown(); + } + }); + + // subscribe twice + connectable.subscribe(new Action1() { + @Override + public void call(String _) {} + }); + + 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(); From 28fe2e2183ac86ae27e062b899a4776b974d45a8 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 24 Sep 2013 20:16:42 +0200 Subject: [PATCH 089/333] do not run RxScalaDemo on each build, because it does not do automatic testing --- .../src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala index 05c328d2dc..c01528fb7a 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -23,7 +23,7 @@ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ import rx.lang.scala.concurrency.NewThreadScheduler -//@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily +@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @Test def intervalExample() { From 8bd705d73de36bc824b84016f81270e2b42d4991 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Wed, 25 Sep 2013 11:38:41 +0800 Subject: [PATCH 090/333] Implemented 'Synchronize' with 'lock' --- rxjava-core/src/main/java/rx/Observable.java | 21 ++ .../rx/operators/OperationSynchronize.java | 31 ++- .../rx/operators/SynchronizedObserver.java | 230 +++++++++++++++++- 3 files changed, 274 insertions(+), 8 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index c21913f86a..0d36d682ef 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -1822,6 +1822,27 @@ public Observable synchronize() { return create(OperationSynchronize.synchronize(this)); } + /** + * Accepts an Observable and wraps it in another Observable that ensures that the resulting + * Observable is chronologically well-behaved. This is accomplished by acquiring a mutual-exclusion lock for the object provided as the lock parameter. + *

+ * + *

+ * A well-behaved Observable does not interleave its invocations of the {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, and {@link Observer#onError onError} methods of + * its {@link Observer}s; it invokes {@code onCompleted} or {@code onError} only once; and it never invokes {@code onNext} after invoking either {@code onCompleted} or {@code onError}. + * {@code synchronize} enforces this, and the Observable it returns invokes {@code onNext} and {@code onCompleted} or {@code onError} synchronously. + * + * @param lock + * The lock object to synchronize each observer call on + * @param + * the type of item emitted by the source Observable + * @return an Observable that is a chronologically well-behaved version of the source + * Observable, and that synchronously notifies its {@link Observer}s + */ + public Observable synchronize(Object lock) { + return create(OperationSynchronize.synchronize(this, lock)); + } + /** * @deprecated Replaced with instance method. */ diff --git a/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java b/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java index 129728c303..b7cd689102 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java @@ -58,21 +58,46 @@ public final class OperationSynchronize { * @return the wrapped synchronized observable sequence */ public static OnSubscribeFunc synchronize(Observable observable) { - return new Synchronize(observable); + return new Synchronize(observable, null); + } + + /** + * Accepts an observable and wraps it in another observable which ensures that the resulting observable is well-behaved. + * This is accomplished by acquiring a mutual-exclusion lock for the object provided as the lock parameter. + * + * A well-behaved observable ensures onNext, onCompleted, or onError calls to its subscribers are + * not interleaved, onCompleted and onError are only called once respectively, and no + * onNext calls follow onCompleted and onError calls. + * + * @param observable + * @param lock + * The lock object to synchronize each observer call on + * @param + * @return the wrapped synchronized observable sequence + */ + public static OnSubscribeFunc synchronize(Observable observable, Object lock) { + return new Synchronize(observable, lock); } private static class Synchronize implements OnSubscribeFunc { - public Synchronize(Observable innerObservable) { + public Synchronize(Observable innerObservable, Object lock) { this.innerObservable = innerObservable; + this.lock = lock; } private Observable innerObservable; private SynchronizedObserver atomicObserver; + private Object lock; public Subscription onSubscribe(Observer observer) { SafeObservableSubscription subscription = new SafeObservableSubscription(); - atomicObserver = new SynchronizedObserver(observer, subscription); + if(lock == null) { + atomicObserver = new SynchronizedObserver(observer, subscription); + } + else { + atomicObserver = new SynchronizedObserver(observer, subscription, lock); + } return subscription.wrap(innerObservable.subscribe(atomicObserver)); } diff --git a/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java b/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java index fff3fccbf8..9a6b14d09f 100644 --- a/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java +++ b/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java @@ -19,6 +19,7 @@ import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; +import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -68,10 +69,18 @@ public final class SynchronizedObserver implements Observer { private final SafeObservableSubscription subscription; private volatile boolean finishRequested = false; private volatile boolean finished = false; + private volatile Object lock; public SynchronizedObserver(Observer Observer, SafeObservableSubscription subscription) { this.observer = Observer; this.subscription = subscription; + this.lock = this; + } + + public SynchronizedObserver(Observer Observer, SafeObservableSubscription subscription, Object lock) { + this.observer = Observer; + this.subscription = subscription; + this.lock = lock; } /** @@ -80,8 +89,7 @@ public SynchronizedObserver(Observer Observer, SafeObservableSubscrip * @param Observer */ public SynchronizedObserver(Observer Observer) { - this.observer = Observer; - this.subscription = new SafeObservableSubscription(); + this(Observer, new SafeObservableSubscription()); } public void onNext(T arg) { @@ -89,7 +97,7 @@ public void onNext(T arg) { // if we're already stopped, or a finish request has been received, we won't allow further onNext requests return; } - synchronized (this) { + synchronized (lock) { // check again since this could have changed while waiting if (finished || finishRequested || subscription.isUnsubscribed()) { // if we're already stopped, or a finish request has been received, we won't allow further onNext requests @@ -105,7 +113,7 @@ public void onError(Throwable e) { return; } finishRequested = true; - synchronized (this) { + synchronized (lock) { // check again since this could have changed while waiting if (finished || subscription.isUnsubscribed()) { return; @@ -121,7 +129,7 @@ public void onCompleted() { return; } finishRequested = true; - synchronized (this) { + synchronized (lock) { // check again since this could have changed while waiting if (finished || subscription.isUnsubscribed()) { return; @@ -188,6 +196,46 @@ public void testMultiThreadedBasic() { assertEquals(1, busyObserver.maxConcurrentThreads.get()); } + @Test + public void testMultiThreadedBasicWithLock() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three"); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + + Object lock = new Object(); + ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); + + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); + + externalBusyThread.start(); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + try { + externalBusyThread.join(10000); + assertFalse(externalBusyThread.isAlive()); + assertFalse(externalBusyThread.fail); + } catch (InterruptedException e) { + // ignore + } + + assertEquals(3, busyObserver.onNextCount.get()); + assertFalse(busyObserver.onError); + assertTrue(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); + } + @Test public void testMultiThreadedWithNPE() { Subscription s = mock(Subscription.class); @@ -220,6 +268,52 @@ public void testMultiThreadedWithNPE() { assertEquals(1, busyObserver.maxConcurrentThreads.get()); } + @Test + public void testMultiThreadedWithNPEAndLock() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + + Object lock = new Object(); + ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); + + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); + + externalBusyThread.start(); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + try { + externalBusyThread.join(10000); + assertFalse(externalBusyThread.isAlive()); + assertFalse(externalBusyThread.fail); + } catch (InterruptedException e) { + // ignore + } + + System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); + + // we can't know how many onNext calls will occur since they each run on a separate thread + // that depends on thread scheduling so 0, 1, 2 and 3 are all valid options + // assertEquals(3, busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 4); + assertTrue(busyObserver.onError); + // no onCompleted because onError was invoked + assertFalse(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + //verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); + } + @Test public void testMultiThreadedWithNPEinMiddle() { Subscription s = mock(Subscription.class); @@ -250,6 +344,50 @@ public void testMultiThreadedWithNPEinMiddle() { assertEquals(1, busyObserver.maxConcurrentThreads.get()); } + @Test + public void testMultiThreadedWithNPEinMiddleAndLock() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + + Object lock = new Object(); + ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); + + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); + + externalBusyThread.start(); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + try { + externalBusyThread.join(10000); + assertFalse(externalBusyThread.isAlive()); + assertFalse(externalBusyThread.fail); + } catch (InterruptedException e) { + // ignore + } + + System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); + // this should not be the full number of items since the error should stop it before it completes all 9 + System.out.println("onNext count: " + busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 9); + assertTrue(busyObserver.onError); + // no onCompleted because onError was invoked + assertFalse(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); + } + /** * A non-realistic use case that tries to expose thread-safety issues by throwing lots of out-of-order * events on many threads. @@ -617,14 +755,32 @@ private static class BusyObserver implements Observer { @Override public void onCompleted() { + threadsRunning.incrementAndGet(); + System.out.println(">>> BusyObserver received onCompleted"); onCompleted = true; + + int concurrentThreads = threadsRunning.get(); + int maxThreads = maxConcurrentThreads.get(); + if (concurrentThreads > maxThreads) { + maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + } + threadsRunning.decrementAndGet(); } @Override public void onError(Throwable e) { + threadsRunning.incrementAndGet(); + System.out.println(">>> BusyObserver received onError: " + e.getMessage()); onError = true; + + int concurrentThreads = threadsRunning.get(); + int maxThreads = maxConcurrentThreads.get(); + if (concurrentThreads > maxThreads) { + maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + } + threadsRunning.decrementAndGet(); } @Override @@ -652,6 +808,70 @@ public void onNext(String args) { } + private static class ExternalBusyThread extends Thread { + + private BusyObserver observer; + private Object lock; + private int lockTimes; + private int waitTime; + public volatile boolean fail; + + public ExternalBusyThread(BusyObserver observer, Object lock, int lockTimes, int waitTime) { + this.observer = observer; + this.lock = lock; + this.lockTimes = lockTimes; + this.waitTime = waitTime; + this.fail = false; + } + + @Override + public void run() { + Random r = new Random(); + for (int i = 0; i < lockTimes; i++) { + synchronized (lock) { + int oldOnNextCount = observer.onNextCount.get(); + boolean oldOnCompleted = observer.onCompleted; + boolean oldOnError = observer.onError; + try { + Thread.sleep(r.nextInt(waitTime)); + } catch (InterruptedException e) { + // ignore + } + // Since we own the lock, onNextCount, onCompleted and + // onError must not be changed. + int newOnNextCount = observer.onNextCount.get(); + boolean newOnCompleted = observer.onCompleted; + boolean newOnError = observer.onError; + if (oldOnNextCount != newOnNextCount) { + System.out.println(">>> ExternalBusyThread received different onNextCount: " + + oldOnNextCount + + " -> " + + newOnNextCount); + fail = true; + break; + } + if (oldOnCompleted != newOnCompleted) { + System.out.println(">>> ExternalBusyThread received different onCompleted: " + + oldOnCompleted + + " -> " + + newOnCompleted); + fail = true; + break; + } + if (oldOnError != newOnError) { + System.out.println(">>> ExternalBusyThread received different onError: " + + oldOnError + + " -> " + + newOnError); + fail = true; + break; + } + } + } + } + + } + } } \ No newline at end of file From 88560776c6caea430420af665925611f5bc62697 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 25 Sep 2013 12:08:20 +0200 Subject: [PATCH 091/333] add Documentation section to README --- language-adaptors/rxjava-scala/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/language-adaptors/rxjava-scala/README.md b/language-adaptors/rxjava-scala/README.md index c4ad66d0af..9cf23dde6e 100644 --- a/language-adaptors/rxjava-scala/README.md +++ b/language-adaptors/rxjava-scala/README.md @@ -65,6 +65,17 @@ Scala code using Rx should only import members from `rx.lang.scala` and below. Work on this adaptor is still in progress, and for the moment, the best source of documentation are the comments in the source code of [`rx.lang.scala.Observable`](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala). +## Documentation + +You can build the documentation as follows: In the RxJava root directory, run + + ./gradlew :language-adaptors:rxjava-scala:scaladoc + +Then navigate to + + RxJava/language-adaptors/rxjava-scala/build/docs/scaladoc/index.html + + ## Binaries Binaries and dependency information for Maven, Ivy, Gradle and others can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22rxjava-scala%22). From cc8b756cb46c4c997e0501ea35bf97bdaaa41d1d Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 25 Sep 2013 15:19:20 +0200 Subject: [PATCH 092/333] move examples from `src/main/scala` to `src/examples/scala` and tweak build.gradle to make this work --- language-adaptors/rxjava-scala/build.gradle | 16 ++++++++++++++++ .../scala/rx/lang/scala/examples/MovieLib.scala | 0 .../scala/rx/lang/scala/examples/Olympics.scala | 0 .../rx/lang/scala/examples/RxScalaDemo.scala | 0 .../main/scala/rx/lang/scala/Observable.scala | 3 ++- 5 files changed, 18 insertions(+), 1 deletion(-) rename language-adaptors/rxjava-scala/src/{main => examples}/scala/rx/lang/scala/examples/MovieLib.scala (100%) rename language-adaptors/rxjava-scala/src/{main => examples}/scala/rx/lang/scala/examples/Olympics.scala (100%) rename language-adaptors/rxjava-scala/src/{main => examples}/scala/rx/lang/scala/examples/RxScalaDemo.scala (100%) diff --git a/language-adaptors/rxjava-scala/build.gradle b/language-adaptors/rxjava-scala/build.gradle index 753c2749e1..8abd48dbaa 100644 --- a/language-adaptors/rxjava-scala/build.gradle +++ b/language-adaptors/rxjava-scala/build.gradle @@ -13,9 +13,21 @@ tasks.withType(ScalaCompile) { } sourceSets { + main { + scala { + srcDir 'src/main/scala' + } + } test { scala { srcDir 'src/main/scala' + srcDir 'src/test/scala' + srcDir 'src/examples/scala' + } + } + examples { + scala { + srcDir 'src/examples/scala' } } } @@ -34,6 +46,10 @@ tasks.compileScala { classpath = classpath + (configurations.compile + configurations.provided) } +tasks.compileExamplesScala { + classpath = classpath + files(compileScala.destinationDir) + (configurations.compile + configurations.provided) +} + task test(overwrite: true, dependsOn: testClasses) << { ant.taskdef(name: 'scalatest', classname: 'org.scalatest.tools.ScalaTestAntTask', diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/MovieLib.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/MovieLib.scala rename to language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/Olympics.scala rename to language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala rename to language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 8ff0ecd437..43a6288c86 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -2049,7 +2049,7 @@ class UnitTestSuite extends org.scalatest.junit.JUnitSuite { } @Test def testFirstOrElse() { - def mustNotBeCalled: String = error("this method should not be called") + def mustNotBeCalled: String = sys.error("this method should not be called") def mustBeCalled: String = "this is the default value" assertEquals("hello", Observable("hello").firstOrElse(mustNotBeCalled).toBlockingObservable.single) assertEquals("this is the default value", Observable().firstOrElse(mustBeCalled).toBlockingObservable.single) @@ -2069,6 +2069,7 @@ class UnitTestSuite extends org.scalatest.junit.JUnitSuite { @Test def testTest() = { val a: Observable[Int] = Observable() assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) + println("This UnitTestSuite.testTest() for rx.lang.scala.Observable") } } From 01634bc7b68b482972cf078d6eb0558572b8b5fa Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 25 Sep 2013 15:58:30 +0200 Subject: [PATCH 093/333] move MovieLibUsage.java from project rxjava-scala-java to project rxjava-scala and delete project rxjava-scala-java --- language-adaptors/rxjava-scala-java/README.md | 5 --- .../rxjava-scala-java/build.gradle | 32 ------------------- language-adaptors/rxjava-scala/build.gradle | 10 +++++- .../rx/lang/scala/examples/MovieLibUsage.java | 0 settings.gradle | 1 - 5 files changed, 9 insertions(+), 39 deletions(-) delete mode 100644 language-adaptors/rxjava-scala-java/README.md delete mode 100644 language-adaptors/rxjava-scala-java/build.gradle rename language-adaptors/{rxjava-scala-java/src/main => rxjava-scala/src/examples}/java/rx/lang/scala/examples/MovieLibUsage.java (100%) diff --git a/language-adaptors/rxjava-scala-java/README.md b/language-adaptors/rxjava-scala-java/README.md deleted file mode 100644 index 54d7086366..0000000000 --- a/language-adaptors/rxjava-scala-java/README.md +++ /dev/null @@ -1,5 +0,0 @@ - -rxjava-scala-java ------------------ - -Contains examples illustrating how RxScala code can be used from Java. diff --git a/language-adaptors/rxjava-scala-java/build.gradle b/language-adaptors/rxjava-scala-java/build.gradle deleted file mode 100644 index d6be5aaeb7..0000000000 --- a/language-adaptors/rxjava-scala-java/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ - -apply plugin: 'osgi' - - -project(':language-adaptors:rxjava-scala-java') { - //sourceSets.test.java.srcDir 'src/examples/java' - sourceSets.main.java.srcDir 'src/main/java' -} - -dependencies { - compile 'org.scala-lang:scala-library:2.10.+' - - compile project(':rxjava-core') - - compile project(':language-adaptors:rxjava-scala') - - provided 'junit:junit-dep:4.10' - provided 'org.mockito:mockito-core:1.8.5' - provided 'org.scalatest:scalatest_2.10:1.9.1' -} - -jar { - manifest { - name = 'rxjava-scala-java' - instruction 'Bundle-Vendor', 'Netflix' - instruction 'Bundle-DocURL', 'https://github.com/Netflix/RxJava' - instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*' - instruction 'Fragment-Host', 'com.netflix.rxjava.core' - } -} - - diff --git a/language-adaptors/rxjava-scala/build.gradle b/language-adaptors/rxjava-scala/build.gradle index 8abd48dbaa..579a5a43b6 100644 --- a/language-adaptors/rxjava-scala/build.gradle +++ b/language-adaptors/rxjava-scala/build.gradle @@ -23,12 +23,20 @@ sourceSets { srcDir 'src/main/scala' srcDir 'src/test/scala' srcDir 'src/examples/scala' + srcDir 'src/examples/java' } + java.srcDirs = [] } - examples { + examples { + // It seems that in Gradle, the dependency "compileScala depends on compileJava" is hardcoded, + // or at least not meant to be removed. + // However, compileScala also runs javac at the very end, so we just add the Java sources to + // the scala source set: scala { srcDir 'src/examples/scala' + srcDir 'src/examples/java' } + java.srcDirs = [] } } diff --git a/language-adaptors/rxjava-scala-java/src/main/java/rx/lang/scala/examples/MovieLibUsage.java b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java similarity index 100% rename from language-adaptors/rxjava-scala-java/src/main/java/rx/lang/scala/examples/MovieLibUsage.java rename to language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java diff --git a/settings.gradle b/settings.gradle index 8750fab727..32cef40c9b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,7 +3,6 @@ include 'rxjava-core', \ 'language-adaptors:rxjava-groovy', \ 'language-adaptors:rxjava-clojure', \ 'language-adaptors:rxjava-scala', \ -'language-adaptors:rxjava-scala-java', \ 'rxjava-contrib:rxjava-swing', \ 'rxjava-contrib:rxjava-android', \ 'rxjava-contrib:rxjava-apache-http' From 450be124584a29d1558323f3c84ab2995b037f49 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 25 Sep 2013 19:04:48 +0200 Subject: [PATCH 094/333] how to add RxJava core to scaladoc input --- language-adaptors/rxjava-scala/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/language-adaptors/rxjava-scala/build.gradle b/language-adaptors/rxjava-scala/build.gradle index 579a5a43b6..db194a6d28 100644 --- a/language-adaptors/rxjava-scala/build.gradle +++ b/language-adaptors/rxjava-scala/build.gradle @@ -58,6 +58,11 @@ tasks.compileExamplesScala { classpath = classpath + files(compileScala.destinationDir) + (configurations.compile + configurations.provided) } +// Add RxJava core to Scaladoc input: +// tasks.scaladoc.source(project(':rxjava-core').tasks.getByPath(':rxjava-core:compileJava').source) +// println("-------") +// println(tasks.scaladoc.source.asPath) + task test(overwrite: true, dependsOn: testClasses) << { ant.taskdef(name: 'scalatest', classname: 'org.scalatest.tools.ScalaTestAntTask', From f20a8eb3855ce4306b67eaccaef75cd39b1568b5 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 25 Sep 2013 19:06:09 +0200 Subject: [PATCH 095/333] make Subscription an implicit value class --- .../lang/scala/ImplicitFunctionConversions.scala | 4 ++-- .../src/main/scala/rx/lang/scala/package.scala | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index 80adb8d22a..1043231bbe 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -29,8 +29,8 @@ object ImplicitFunctionConversions { implicit def scalaFunction1ToOnSubscribeFunc[T](f: rx.lang.scala.Observer[T] => Subscription) = new rx.Observable.OnSubscribeFunc[T] { - def onSubscribe(obs: Observer[_ >: T]): Subscription = { - f(obs) + def onSubscribe(obs: Observer[_ >: T]): rx.Subscription = { + f(obs).asJava } } diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala index 0f6ea79d34..81af61241a 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala @@ -35,8 +35,20 @@ package object scala { type Observer[-T] = rx.Observer[_ >: T] type Scheduler = rx.Scheduler - type Subscription = rx.Subscription + /** + * Subscriptions are returned from all Observable.subscribe methods to allow unsubscribing. + * + * This interface is the RxJava equivalent of IDisposable in Microsoft's Rx implementation. + */ + implicit class Subscription(val asJava: rx.Subscription) extends AnyVal { + /** + * Call this to stop receiving notifications on the Observer that was registered when + * this Subscription was received. + */ + def unsubscribe(): Unit = asJava.unsubscribe() + } + } /* From a5fe8dcf2622e5b32117d68f99738daeb39fcb25 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 25 Sep 2013 20:09:21 +0200 Subject: [PATCH 096/333] Opening/Closing, Timestamped with unapply, BlockingObservable with WithFilter --- .../rx/lang/scala/examples/RxScalaDemo.scala | 8 ++++ .../main/scala/rx/lang/scala/Observable.scala | 2 +- .../observables/BlockingObservable.scala | 35 ++++++++++++-- .../scala/rx/lang/scala/util/package.scala | 48 ++++++++++++------- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index c01528fb7a..1653bd8b2b 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -22,6 +22,7 @@ import scala.concurrent.duration._ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ import rx.lang.scala.concurrency.NewThreadScheduler +import rx.lang.scala.util.Timestamped @Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @@ -375,6 +376,13 @@ class RxScalaDemo extends JUnitSuite { assertEquals(Seq(10, 9, 8, 7), Observable(10, 7, 8, 9).toSeq.map(_.sortWith(f)).toBlockingObservable.single) } + @Test def timestampExample() { + val timestamped = Observable.interval(100 millis).take(3).timestamp.toBlockingObservable + for (Timestamped(millis, value) <- timestamped if value > 0) { + println(value + " at t = " + millis) + } + } + def output(s: String): Unit = println(s) // blocks until obs has completed diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 43a6288c86..1dace17c59 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -199,7 +199,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) * @return an Observable that emits timestamped items from the source Observable */ def timestamp: Observable[Timestamped[T]] = { - Observable[Timestamped[T]](asJava.timestamp()) + Observable[rx.util.Timestamped[_ <: T]](asJava.timestamp()).map(Timestamped(_)) } /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala index 5470f6f1cb..4be27d0ae0 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala @@ -18,6 +18,11 @@ package rx.lang.scala.observables import scala.collection.JavaConverters._ import rx.lang.scala.ImplicitFunctionConversions._ +/** + * An Observable that provides blocking operators. + * + * You can obtain a BlockingObservable from an Observable using [[Observable.toBlockingObservable]] + */ class BlockingObservable[+T](val asJava: rx.observables.BlockingObservable[_ <: T]) extends AnyVal { @@ -25,12 +30,12 @@ class BlockingObservable[+T](val asJava: rx.observables.BlockingObservable[_ <: /** * Invoke a method on each item emitted by the {@link Observable}; block until the Observable * completes. - *

+ * * NOTE: This will block even if the Observable is asynchronous. - *

+ * * This is similar to {@link Observable#subscribe(Observer)}, but it blocks. Because it blocks it does * not need the {@link Observer#onCompleted()} or {@link Observer#onError(Throwable)} methods. - *

+ * * * * @param onNext @@ -41,6 +46,10 @@ class BlockingObservable[+T](val asJava: rx.observables.BlockingObservable[_ <: def foreach(f: T => Unit): Unit = { asJava.forEach(f); } + + def withFilter(p: T => Boolean): WithFilter[T] = { + new WithFilter[T](p, asJava) + } // last -> use toIterable.last // lastOrDefault -> use toIterable.lastOption @@ -118,3 +127,23 @@ class BlockingObservable[+T](val asJava: rx.observables.BlockingObservable[_ <: } } + +// Cannot yet have inner class because of this error message: +// "implementation restriction: nested class is not allowed in value class. +// This restriction is planned to be removed in subsequent releases." +class WithFilter[+T] private[observables] (p: T => Boolean, asJava: rx.observables.BlockingObservable[_ <: T]) { + import rx.lang.scala.ImplicitFunctionConversions._ + + // there's no map and flatMap here, they're only available on Observable + + def withFilter(q: T => Boolean) = new WithFilter[T]((x: T) => p(x) && q(x), asJava) + + def foreach(f: T => Unit): Unit = { + asJava.forEach((e: T) => { + if (p(e)) f(e) + }) + } + +} + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala index 6dced72f8e..bee0992981 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala @@ -16,33 +16,45 @@ package rx.lang.scala package object util { - type Closing = rx.util.Closing - - object Closings { - def create(): Closing = rx.util.Closings.create() - } - - type CompositeException = rx.util.CompositeException - - // TODO not sure if we need this in Scala - object Exceptions { - def propageate(ex: Throwable) = rx.util.Exceptions.propagate(ex) - } - - // rx.util.OnErrorNotImplementedException TODO what's this? + /** + * Tagging interface for objects which can open buffers. + * @see [[Observable.buffer]] + */ type Opening = rx.util.Opening - object Openings { - def create(): Opening = rx.util.Openings.create() - } + /** + * Creates an object which can open buffers. + * @see [[Observable.buffer]] + */ + def Opening() = rx.util.Openings.create() + + /** + * Tagging interface for objects which can close buffers. + * @see [[Observable.buffer]] + */ + type Closing = rx.util.Closing + /** + * Creates an object which can close buffers. + * @see [[Observable.buffer]] + */ + def Closing() = rx.util.Closings.create() + // rx.util.Range not needed because there's a standard Scala Range - type Timestamped[+T] = rx.util.Timestamped[_ <: T] + implicit class Timestamped[+T](val asJava: rx.util.Timestamped[_ <: T]) {} + object Timestamped { def apply[T](timestampMillis: Long, value: T): Timestamped[T] = { new rx.util.Timestamped(timestampMillis, value) } + + def unapply[T](v: Timestamped[T]): Option[(Long, T)] = unapply(v.asJava) + + def unapply[T](v: rx.util.Timestamped[_ <: T]): Option[(Long, T)] = { + Some((v.getTimestampMillis, v.getValue)) + } } + } \ No newline at end of file From 6f56788071b8e2efd0465f8cb29f0ca2cb0d44cf Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 25 Sep 2013 20:42:42 +0200 Subject: [PATCH 097/333] remove `implicit` from Timestamped --- .../src/main/scala/rx/lang/scala/util/package.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala index bee0992981..d03f2bc58d 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala @@ -43,11 +43,15 @@ package object util { // rx.util.Range not needed because there's a standard Scala Range - implicit class Timestamped[+T](val asJava: rx.util.Timestamped[_ <: T]) {} + class Timestamped[+T](val asJava: rx.util.Timestamped[_ <: T]) {} object Timestamped { def apply[T](timestampMillis: Long, value: T): Timestamped[T] = { - new rx.util.Timestamped(timestampMillis, value) + new Timestamped(new rx.util.Timestamped(timestampMillis, value)) + } + + def apply[T](asJava: rx.util.Timestamped[_ <: T]): Timestamped[T] = { + new Timestamped(asJava) } def unapply[T](v: Timestamped[T]): Option[(Long, T)] = unapply(v.asJava) From de7ac4246e84ca5ee2e39ee8cd91c0afd966cdfa Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 25 Sep 2013 20:55:32 +0200 Subject: [PATCH 098/333] work around scalac bug by renaming companion object `Timestamped` to `TimestampedObject` --- .../examples/scala/rx/lang/scala/examples/RxScalaDemo.scala | 4 ++-- .../src/main/scala/rx/lang/scala/Observable.scala | 2 +- .../src/main/scala/rx/lang/scala/util/package.scala | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index 1653bd8b2b..a8d728149a 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -22,7 +22,7 @@ import scala.concurrent.duration._ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ import rx.lang.scala.concurrency.NewThreadScheduler -import rx.lang.scala.util.Timestamped +import rx.lang.scala.util.{Timestamped, TimestampedObject} @Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @@ -378,7 +378,7 @@ class RxScalaDemo extends JUnitSuite { @Test def timestampExample() { val timestamped = Observable.interval(100 millis).take(3).timestamp.toBlockingObservable - for (Timestamped(millis, value) <- timestamped if value > 0) { + for (TimestampedObject(millis, value) <- timestamped if value > 0) { println(value + " at t = " + millis) } } diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 1dace17c59..74da23b137 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -199,7 +199,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) * @return an Observable that emits timestamped items from the source Observable */ def timestamp: Observable[Timestamped[T]] = { - Observable[rx.util.Timestamped[_ <: T]](asJava.timestamp()).map(Timestamped(_)) + Observable[rx.util.Timestamped[_ <: T]](asJava.timestamp()).map(TimestampedObject(_)) } /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala index d03f2bc58d..aa900ed24a 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala @@ -45,7 +45,8 @@ package object util { class Timestamped[+T](val asJava: rx.util.Timestamped[_ <: T]) {} - object Timestamped { + // TODO rename this to Timestamped without making scalac crash + object TimestampedObject { def apply[T](timestampMillis: Long, value: T): Timestamped[T] = { new Timestamped(new rx.util.Timestamped(timestampMillis, value)) } From 490ef8663965a016270b74a293c8e739ec5d98d1 Mon Sep 17 00:00:00 2001 From: John Marks Date: Wed, 25 Sep 2013 21:00:24 +0100 Subject: [PATCH 099/333] Removed static variant of refCount --- .../rx/observables/ConnectableObservable.java | 12 +-- .../src/test/java/rx/RefCountTests.java | 84 ++++++------------- 2 files changed, 26 insertions(+), 70 deletions(-) diff --git a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java index a208673028..1595c31195 100644 --- a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java +++ b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java @@ -54,16 +54,6 @@ protected ConnectableObservable(OnSubscribeFunc onSubscribe) { * @return a {@link Observable} */ public Observable refCount() { - return refCount(this); - } - - /** - * Returns an observable sequence that stays connected to the source as long - * as there is at least one subscription to the observable sequence. - * @return a {@link Observable} - * @param that a {@link ConnectableObservable} - */ - public static Observable refCount(ConnectableObservable that) { - return Observable.create(OperationRefCount.refCount(that)); + return Observable.create(OperationRefCount.refCount(this)); } } diff --git a/rxjava-core/src/test/java/rx/RefCountTests.java b/rxjava-core/src/test/java/rx/RefCountTests.java index dae95bf265..bf035e0aa4 100644 --- a/rxjava-core/src/test/java/rx/RefCountTests.java +++ b/rxjava-core/src/test/java/rx/RefCountTests.java @@ -3,10 +3,13 @@ import org.junit.Before; import org.junit.Test; import org.mockito.MockitoAnnotations; -import rx.observables.ConnectableObservable; import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; -import static org.mockito.Mockito.*; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; public class RefCountTests { @@ -16,67 +19,30 @@ public void setUp() { } @Test - public void subscriptionToUnderlyingOnFirstSubscription() { - @SuppressWarnings("unchecked") - ConnectableObservable connectable = mock(ConnectableObservable.class); - Observable refCounted = ConnectableObservable.refCount(connectable); - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - when(connectable.subscribe(any(Observer.class))).thenReturn(Subscriptions.empty()); - when(connectable.connect()).thenReturn(Subscriptions.empty()); - refCounted.subscribe(observer); - verify(connectable, times(1)).subscribe(any(Observer.class)); - verify(connectable, times(1)).connect(); - } - - @Test - public void noSubscriptionToUnderlyingOnSecondSubscription() { - @SuppressWarnings("unchecked") - ConnectableObservable connectable = mock(ConnectableObservable.class); - Observable refCounted = ConnectableObservable.refCount(connectable); - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - when(connectable.subscribe(any(Observer.class))).thenReturn(Subscriptions.empty()); - when(connectable.connect()).thenReturn(Subscriptions.empty()); - refCounted.subscribe(observer); - refCounted.subscribe(observer); - verify(connectable, times(2)).subscribe(any(Observer.class)); - verify(connectable, times(1)).connect(); - } - - @Test - public void unsubscriptionFromUnderlyingOnLastUnsubscription() { - @SuppressWarnings("unchecked") - ConnectableObservable connectable = mock(ConnectableObservable.class); - Observable refCounted = ConnectableObservable.refCount(connectable); - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - Subscription underlying = mock(Subscription.class); - when(connectable.subscribe(any(Observer.class))).thenReturn(underlying); - Subscription connection = mock(Subscription.class); - when(connectable.connect()).thenReturn(connection); - Subscription first = refCounted.subscribe(observer); - first.unsubscribe(); - verify(underlying, times(1)).unsubscribe(); - verify(connection, times(1)).unsubscribe(); - } - - @Test - public void noUnsubscriptionFromUnderlyingOnFirstUnsubscription() { - @SuppressWarnings("unchecked") - ConnectableObservable connectable = mock(ConnectableObservable.class); - Observable refCounted = ConnectableObservable.refCount(connectable); - @SuppressWarnings("unchecked") + public void onlyFirstShouldSubscribeAndLastUnsubscribe() { + final AtomicInteger subscriptionCount = new AtomicInteger(); + final AtomicInteger unsubscriptionCount = new AtomicInteger(); + Observable observable = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + subscriptionCount.incrementAndGet(); + return Subscriptions.create(new Action0() { + @Override + public void call() { + unsubscriptionCount.incrementAndGet(); + } + }); + } + }); + Observable refCounted = observable.publish().refCount(); Observer observer = mock(Observer.class); - Subscription underlying = mock(Subscription.class); - when(connectable.subscribe(any(Observer.class))).thenReturn(underlying); - Subscription connection = mock(Subscription.class); - when(connectable.connect()).thenReturn(connection); Subscription first = refCounted.subscribe(observer); + assertEquals(1, subscriptionCount.get()); Subscription second = refCounted.subscribe(observer); + assertEquals(1, subscriptionCount.get()); first.unsubscribe(); + assertEquals(0, unsubscriptionCount.get()); second.unsubscribe(); - verify(underlying, times(2)).unsubscribe(); - verify(connection, times(1)).unsubscribe(); + assertEquals(1, unsubscriptionCount.get()); } } From dbfc9b746c94e0112e881ab278415d7f80354536 Mon Sep 17 00:00:00 2001 From: Peter McDonnell Date: Thu, 26 Sep 2013 00:36:03 +0100 Subject: [PATCH 100/333] update counter before triggering latch --- rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java b/rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java index 4241ee3f84..7d2851abf9 100644 --- a/rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java +++ b/rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java @@ -361,8 +361,8 @@ public void testSchedulingWithDueTime() throws InterruptedException { @Override public Subscription call(Scheduler scheduler, String state) { System.out.println("doing work"); - latch.countDown(); counter.incrementAndGet(); + latch.countDown(); if (latch.getCount() == 0) { return Subscriptions.empty(); } else { From ea575d0911b6b98b1b4ab537a97c6c56e26f96d9 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Thu, 26 Sep 2013 09:40:18 +0800 Subject: [PATCH 101/333] Merge branch 'master', remote-tracking branch 'origin' From 196c119d08215c5ebd77f66ac007bc2ca4e16ce3 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Thu, 26 Sep 2013 04:09:15 +0000 Subject: [PATCH 102/333] [Gradle Release Plugin] - pre tag commit: '0.14.2'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d3291d5969..0af71a24fd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.2-SNAPSHOT +version=0.14.2 From ece8f8143f20a740820e9123a9887bcf9543e3f3 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Thu, 26 Sep 2013 04:09:19 +0000 Subject: [PATCH 103/333] [Gradle Release Plugin] - new version commit: '0.14.3-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 0af71a24fd..38ee218094 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.2 +version=0.14.3-SNAPSHOT From af2b35a8b425dee0a4e4f2d8ce8df503fec3919e Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 25 Sep 2013 21:17:15 -0700 Subject: [PATCH 104/333] Version 0.14.2 --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index e5b919e7f2..bc6f9e5a8e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # RxJava Releases # +### Version 0.14.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.2%22)) ### + +* [Pull 403](https://github.com/Netflix/RxJava/pull/403) Operators: Cast and OfType +* [Pull 401](https://github.com/Netflix/RxJava/pull/401) Operator: DefaultIfEmpty +* [Pull 409](https://github.com/Netflix/RxJava/pull/409) Operator: Synchronize with object + ### Version 0.14.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.1%22)) ### * [Pull 402](https://github.com/Netflix/RxJava/pull/402) rxjava-apache-http improvements From 14369fc7e4eaf98dac3df660f1cbabe8a0aa54fe Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Thu, 26 Sep 2013 10:07:32 +0200 Subject: [PATCH 105/333] Timestamped and its companion can now have the same name --- .../rx/lang/scala/examples/RxScalaDemo.scala | 4 +-- .../main/scala/rx/lang/scala/Observable.scala | 2 +- .../rx/lang/scala/util/Timestamped.scala | 30 +++++++++++++++++++ .../scala/rx/lang/scala/util/package.scala | 19 ------------ 4 files changed, 33 insertions(+), 22 deletions(-) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index a8d728149a..1653bd8b2b 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -22,7 +22,7 @@ import scala.concurrent.duration._ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ import rx.lang.scala.concurrency.NewThreadScheduler -import rx.lang.scala.util.{Timestamped, TimestampedObject} +import rx.lang.scala.util.Timestamped @Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @@ -378,7 +378,7 @@ class RxScalaDemo extends JUnitSuite { @Test def timestampExample() { val timestamped = Observable.interval(100 millis).take(3).timestamp.toBlockingObservable - for (TimestampedObject(millis, value) <- timestamped if value > 0) { + for (Timestamped(millis, value) <- timestamped if value > 0) { println(value + " at t = " + millis) } } diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 74da23b137..1dace17c59 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -199,7 +199,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) * @return an Observable that emits timestamped items from the source Observable */ def timestamp: Observable[Timestamped[T]] = { - Observable[rx.util.Timestamped[_ <: T]](asJava.timestamp()).map(TimestampedObject(_)) + Observable[rx.util.Timestamped[_ <: T]](asJava.timestamp()).map(Timestamped(_)) } /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala new file mode 100644 index 0000000000..93f2ef2089 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala @@ -0,0 +1,30 @@ +package rx.lang.scala.util + +/** + * Wraps a value and a timestamp. + */ +class Timestamped[+T](val asJava: rx.util.Timestamped[_ <: T]) extends AnyVal { + /** + * Returns the timestamp, in milliseconds. + */ + def millis: Long = asJava.getTimestampMillis + + /** + * Returns the value. + */ + def value: T = asJava.getValue : T +} + +object Timestamped { + def apply[T](timestampMillis: Long, value: T): Timestamped[T] = { + new Timestamped(new rx.util.Timestamped(timestampMillis, value)) + } + + def apply[T](asJava: rx.util.Timestamped[_ <: T]): Timestamped[T] = { + new Timestamped(asJava) + } + + def unapply[T](v: Timestamped[T]): Option[(Long, T)] = { + Some((v.millis, v.value)) + } +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala index aa900ed24a..1a41f43643 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala @@ -42,24 +42,5 @@ package object util { def Closing() = rx.util.Closings.create() // rx.util.Range not needed because there's a standard Scala Range - - class Timestamped[+T](val asJava: rx.util.Timestamped[_ <: T]) {} - - // TODO rename this to Timestamped without making scalac crash - object TimestampedObject { - def apply[T](timestampMillis: Long, value: T): Timestamped[T] = { - new Timestamped(new rx.util.Timestamped(timestampMillis, value)) - } - - def apply[T](asJava: rx.util.Timestamped[_ <: T]): Timestamped[T] = { - new Timestamped(asJava) - } - - def unapply[T](v: Timestamped[T]): Option[(Long, T)] = unapply(v.asJava) - - def unapply[T](v: rx.util.Timestamped[_ <: T]): Option[(Long, T)] = { - Some((v.getTimestampMillis, v.getValue)) - } - } } \ No newline at end of file From 0b764969ad585e79e846aebe9e2e3fb3c95c49a1 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Thu, 26 Sep 2013 16:12:04 +0800 Subject: [PATCH 106/333] Implemented the 'Contains' operator --- rxjava-core/src/main/java/rx/Observable.java | 16 ++++++++ .../src/test/java/rx/ObservableTests.java | 41 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 5a0f668dc5..d9af3dc375 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -3151,6 +3151,22 @@ public Observable exists(Func1 predicate) { return create(OperationAny.exists(this, predicate)); } + /** + * Determines whether an observable sequence contains a specified element. + * + * @param value + * The element to search in the sequence. + * @return an Observable that emits if the element is in the source sequence. + * @see MSDN: Observable.Contains + */ + public Observable contains(final T element) { + return exists(new Func1() { + public Boolean call(T t1) { + return element == null ? t1 == null : element.equals(t1); + } + }); + } + /** * Registers an {@link Action0} to be called when this Observable invokes {@link Observer#onCompleted onCompleted} or {@link Observer#onError onError}. *

diff --git a/rxjava-core/src/test/java/rx/ObservableTests.java b/rxjava-core/src/test/java/rx/ObservableTests.java index 297bb1ff47..1710f16d18 100644 --- a/rxjava-core/src/test/java/rx/ObservableTests.java +++ b/rxjava-core/src/test/java/rx/ObservableTests.java @@ -741,4 +741,45 @@ public void testOfTypeWithPolymorphism() { verify(aObserver, times(1)).onCompleted(); } + @Test + public void testContains() { + Observable observable = Observable.from("a", "b", null).contains("b"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onNext(false); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testContainsWithNull() { + Observable observable = Observable.from("a", "b", null).contains(null); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onNext(false); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testContainsWithEmptyObservable() { + Observable observable = Observable.empty().contains("a"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onNext(true); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } } \ No newline at end of file From bdddee691ce79a8c1d79b99c01e261611dc3025f Mon Sep 17 00:00:00 2001 From: zsxwing Date: Thu, 26 Sep 2013 16:59:54 +0800 Subject: [PATCH 107/333] Add one more unit test --- rxjava-core/src/test/java/rx/ObservableTests.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/rxjava-core/src/test/java/rx/ObservableTests.java b/rxjava-core/src/test/java/rx/ObservableTests.java index 1710f16d18..3919ab6c9d 100644 --- a/rxjava-core/src/test/java/rx/ObservableTests.java +++ b/rxjava-core/src/test/java/rx/ObservableTests.java @@ -755,6 +755,20 @@ public void testContains() { verify(aObserver, times(1)).onCompleted(); } + @Test + public void testContainsWithInexistence() { + Observable observable = Observable.from("a", "b", null).contains("c"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onNext(true); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + @Test public void testContainsWithNull() { Observable observable = Observable.from("a", "b", null).contains(null); From f7ab0b39b951df5a5bfc25560e56205a57b4ac60 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Thu, 26 Sep 2013 13:04:27 +0200 Subject: [PATCH 108/333] scaladoc for Observer, Subject, Scheduler, and new Notification class with unapply (scalac crashes) --- .../rx/lang/scala/examples/RxScalaDemo.scala | 13 +- .../scala/ImplicitFunctionConversions.scala | 4 +- .../scala/rx/lang/scala/Notification.scala | 42 ++++ .../main/scala/rx/lang/scala/Observable.scala | 15 +- .../rx/lang/scala/observables/package.scala | 12 + .../main/scala/rx/lang/scala/package.scala | 214 ++++++++++++++++-- .../rx/lang/scala/subjects/package.scala | 18 +- 7 files changed, 284 insertions(+), 34 deletions(-) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index 1653bd8b2b..1b4a8684ac 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -24,7 +24,7 @@ import org.junit.Assert._ import rx.lang.scala.concurrency.NewThreadScheduler import rx.lang.scala.util.Timestamped -@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily +//@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @Test def intervalExample() { @@ -383,6 +383,17 @@ class RxScalaDemo extends JUnitSuite { } } + @Test def materializeExample() { + def printObservable[T](o: Observable[T]): Unit = { + for (n <- o.materialize.toBlockingObservable) n match { + case Notification.OnNext[T](v) => println("Got value " + v) + case Notification.OnCompleted[T]() => println("Completed") + case Notification.OnError[T](err) => println("Error: ") + } + } + val mat = Observable.interval(100 millis).take(3).materialize + } + def output(s: String): Unit = println(s) // blocks until obs has completed diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index 1043231bbe..78b1d8d776 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -29,8 +29,8 @@ object ImplicitFunctionConversions { implicit def scalaFunction1ToOnSubscribeFunc[T](f: rx.lang.scala.Observer[T] => Subscription) = new rx.Observable.OnSubscribeFunc[T] { - def onSubscribe(obs: Observer[_ >: T]): rx.Subscription = { - f(obs).asJava + def onSubscribe(obs: rx.Observer[_ >: T]): rx.Subscription = { + f(obs) } } diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala new file mode 100644 index 0000000000..30145f27f5 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala @@ -0,0 +1,42 @@ +package rx.lang.scala + +sealed trait Notification[+T] { + def asJava: rx.Notification[_ <: T] +} + +object Notification { + + def apply[T](n: rx.Notification[_ <: T]): Notification[T] = n.getKind match { + case rx.Notification.Kind.OnNext => new OnNext(n) + case rx.Notification.Kind.OnCompleted => new OnCompleted(n) + case rx.Notification.Kind.OnError => new OnError(n) + } + + // OnNext, OnError, OnCompleted are not case classes because we don't want pattern matching + // to extract the rx.Notification + + class OnNext[+T](val asJava: rx.Notification[_ <: T]) extends Notification[T] { + def value: T = asJava.getValue + def unapply[U](n: Notification[U]): Option[U] = n match { + case n2: OnNext[U] => Some(n.asJava.getValue) + case _ => None + } + } + + class OnError[+T](val asJava: rx.Notification[_ <: T]) extends Notification[T] { + def error: Throwable = asJava.getThrowable() + def unapply[U](n: Notification[U]): Option[Throwable] = n match { + case n2: OnError[U] => Some(n2.asJava.getThrowable) + case _ => None + } + } + + class OnCompleted[T](val asJava: rx.Notification[_ <: T]) extends Notification[T] { + def unapply[U](n: Notification[U]): Option[Unit] = n match { + case n2: OnCompleted[U] => Some() + case _ => None + } + } + +} + diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 1dace17c59..afa0b59fcd 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -30,7 +30,6 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) import scala.concurrent.duration.{Duration, TimeUnit} import rx.{Observable => JObservable} import rx.util.functions._ - import rx.lang.scala.{Notification, Subscription, Scheduler, Observer} import rx.lang.scala.util._ import rx.lang.scala.subjects.Subject import rx.lang.scala.observables.BlockingObservable @@ -714,7 +713,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) * @see MSDN: Observable.materialize */ def materialize: Observable[Notification[T]] = { - Observable[Notification[T]](asJava.materialize()) + Observable[rx.Notification[_ <: T]](asJava.materialize()).map(Notification(_)) } /** @@ -755,9 +754,11 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) * @return an Observable that emits the items and notifications embedded in the {@link Notification} objects emitted by the source Observable */ // with =:= it does not work, why? - def dematerialize[U](implicit evidence: T <:< Notification[U]): Observable[U] = { - val o = asJava.dematerialize[U]() - Observable[U](o) + def dematerialize[U](implicit evidence: Observable[T] <:< Observable[Notification[U]]): Observable[U] = { + val o1: Observable[Notification[U]] = this + val o2: Observable[rx.Notification[_ <: U]] = o1.map(_.asJava) + val o3 = o2.asJava.dematerialize[U]() + Observable[U](o3) } /** @@ -1765,7 +1766,6 @@ object Observable { import scala.collection.immutable.Range import scala.concurrent.duration.Duration import rx.{Observable => JObservable} - import rx.lang.scala.{Notification, Subscription, Scheduler, Observer} import rx.lang.scala.util._ import rx.util.functions._ import rx.lang.scala.ImplicitFunctionConversions._ @@ -1923,7 +1923,8 @@ object Observable { } def apply[T](f: Future[T], scheduler: Scheduler): Observable[T] = { - Observable[T](rx.Observable.from(f, scheduler)) + val sched: rx.Scheduler = scheduler + Observable[T](rx.Observable.from(f, sched)) } def apply[T](f: Future[T], duration: Duration): Observable[T] = { diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala new file mode 100644 index 0000000000..ae1181321f --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala @@ -0,0 +1,12 @@ +package rx.lang.scala + +/** + * Contains special Observables. + * + * In Scala, this package only contains [[rx.lang.scala.observables.BlockingObservable]]. + * In the corresponding Java package {{{rx.observables}}}, there is also a + * {{{GroupedObservable}}} and a {{{ConnectableObservable}}}, but these are not needed + * in Scala, because we use a pair {{{(key, observable)}}} instead of {{{GroupedObservable}}} + * and a pair {{{(startFunction, observable)}}} instead of {{{ConnectableObservable}}}. + */ +package object observables {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala index 81af61241a..04ed0ce6af 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala @@ -16,50 +16,221 @@ package rx.lang -/* +/** * This object contains aliases to all types Scala users need to import. + * * Note that: * - Scala users cannot use Java's type with variance without always using writing * e.g. rx.Notification[_ <: T], so we create aliases fixing the variance * - For consistency, we create aliases for all types - * - Type aliases cannot be at top level, they have to be inside an object or class */ +import java.util.concurrent.TimeUnit +import java.util.Date package object scala { - type Notification[+T] = rx.Notification[_ <: T] - object Notification { - def apply[T](): Notification[T] = new rx.Notification() - def apply[T](value: T): Notification[T] = new rx.Notification(value) - def apply[T](t: Throwable): Notification[T] = new rx.Notification(t) + /* + * Here we're imitating C's preprocessor using Search & Replace. + * + * To activate the code needed to get nice Scaladoc, do the following replacements: + * /*//#ifdef SCALADOC --> //#ifdef SCALADOC + * *///#else --> /*//#else + * //#endif --> *///#endif + * + * To get back to the actual code, undo the above replacements. + * + */ + + /*//#ifdef SCALADOC + + /** + * Provides a mechanism for receiving push-based notifications. + * + * After an Observer calls an [[rx.lang.scala.Observable]]'s {{{subscribe}}} method, the Observable + * calls the Observer's {{{onNext}}} method to provide notifications. A well-behaved Observable will + * call an Observer's {{{onCompleted}}} method exactly once or the Observer's {{{onError}}} method exactly once. + */ + trait Observer[-T] { + + /** + * Notifies the Observer that the [[rx.lang.scala.Observable]] has finished sending push-based notifications. + * + * The [[rx.lang.scala.Observable]] will not call this method if it calls {{{onError}}}. + */ + def onCompleted(): Unit + + /** + * Notifies the Observer that the [[rx.lang.scala.Observable]] has experienced an error condition. + * + * If the [[rx.lang.scala.Observable]] calls this method, it will not thereafter call {{{onNext}}} or {{{onCompleted}}}. + */ + def onError(e: Throwable): Unit + + /** + * Provides the Observer with new data. + * + * The [[rx.lang.scala.Observable]] calls this closure 0 or more times. + * + * The [[rx.lang.scala.Observable]] will not call this method again after it calls either {{{onCompleted}}} or {{{onError}}}. + */ + def onNext(arg: T): Unit } - - type Observer[-T] = rx.Observer[_ >: T] - type Scheduler = rx.Scheduler + + /** + * Represents an object that schedules units of work. + */ + abstract class Scheduler { + + /** + * Schedules a cancelable action to be executed. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule[T](state: T, action: (Scheduler, T) => Subscription): Subscription + + /** + * Schedules a cancelable action to be executed in delayTime. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @param delayTime + * Time the action is to be delayed before executing. + * @param unit + * Time unit of the delay time. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule[T](state: T, action: (Scheduler, T) => Subscription, delayTime: Long, unit: TimeUnit): Subscription + + /** + * Schedules a cancelable action to be executed periodically. + * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing + * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. + * + * @param state + * State to pass into the action. + * @param action + * The action to execute periodically. + * @param initialDelay + * Time to wait before executing the action for the first time. + * @param period + * The time interval to wait each time in between executing the action. + * @param unit + * The time unit the interval above is given in. + * @return A subscription to be able to unsubscribe from action. + */ + def schedulePeriodically[T](state: T, action: (Scheduler, T) => Subscription, initialDelay: Long, period: Long, unit: TimeUnit): Subscription + + /** + * Schedules a cancelable action to be executed at dueTime. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @param dueTime + * Time the action is to be executed. If in the past it will be executed immediately. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule[T](state: T, action: (Scheduler, T) => Subscription, dueTime: Date): Subscription + + /** + * Schedules an action to be executed. + * + * @param action + * action + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(action: () => Unit): Subscription + + /** + * Schedules an action to be executed in delayTime. + * + * @param action + * action + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(action: () => Unit, delayTime: Long, unit: TimeUnit): Subscription + + /** + * Schedules an action to be executed periodically. + * + * @param action + * The action to execute periodically. + * @param initialDelay + * Time to wait before executing the action for the first time. + * @param period + * The time interval to wait each time in between executing the action. + * @param unit + * The time unit the interval above is given in. + * @return A subscription to be able to unsubscribe from action. + */ + def schedulePeriodically(action: () => Unit, initialDelay: Long, period: Long, unit: TimeUnit): Subscription + + /** + * @return the scheduler's notion of current absolute time in milliseconds. + */ + def now(): Long + + /** + * Parallelism available to a Scheduler. + *

+ * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. + * + * @return the scheduler's available degree of parallelism. + */ + def degreeOfParallelism: Int + + } + /** * Subscriptions are returned from all Observable.subscribe methods to allow unsubscribing. * - * This interface is the RxJava equivalent of IDisposable in Microsoft's Rx implementation. + * This interface is the equivalent of IDisposable in the .NET Rx implementation. */ - implicit class Subscription(val asJava: rx.Subscription) extends AnyVal { + trait Subscription { /** - * Call this to stop receiving notifications on the Observer that was registered when + * Call this method to stop receiving notifications on the Observer that was registered when * this Subscription was received. */ - def unsubscribe(): Unit = asJava.unsubscribe() + def unsubscribe(): Unit } + + private[scala] implicit def fakeSubscription2RxSubscription(s: Subscription): rx.Subscription = + new rx.Subscription { + def unsubscribe() = s.unsubscribe() + } + private[scala] implicit def rxSubscription2FakeSubscription(s: rx.Subscription): Subscription = + new Subscription { + def unsubscribe() = s.unsubscribe() + } + + private[scala] implicit def fakeObserver2RxObserver[T](o: Observer[T]): rx.Observer[_ >: T] = ??? + private[scala] implicit def rxObserver2fakeObserver[T](o: rx.Observer[_ >: T]): Observer[T] = ??? + + private[scala] implicit def fakeScheduler2RxScheduler(s: Scheduler): rx.Scheduler = ??? + private[scala] implicit def rxScheduler2fakeScheduler(s: rx.Scheduler): Scheduler = ??? + + *///#else + + type Observer[-T] = rx.Observer[_ >: T] + + type Scheduler = rx.Scheduler + + type Subscription = rx.Subscription + + //#endif } /* -TODO make aliases for these types because: -* those which are covariant or contravariant do need an alias to get variance correct -* the others for consistency - -rx.observables.BlockingObservable -rx.observables.ConnectableObservable -rx.observables.GroupedObservable +These classes are considered unnecessary for Scala users, so we don't create aliases for them: rx.plugins.RxJavaErrorHandler rx.plugins.RxJavaObservableExecutionHook @@ -70,4 +241,3 @@ rx.subscriptions.CompositeSubscription rx.subscriptions.Subscriptions */ - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala index 8f99d02bf6..22c265aefc 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala @@ -2,10 +2,24 @@ package rx.lang.scala package object subjects { - // in Java: public abstract class Subject extends Observable implements Observer + /** + * A Subject is an Observable and an Observer at the same time. + * + * The Java Subject looks like this: + * {{{ + * public abstract class Subject extends Observable implements Observer + * }}} + */ type Subject[-T, +R] = rx.subjects.Subject[_ >: T, _ <: R] - // TODO (including static methods of these classes) + // For nicer scaladoc, we would like to present something like this: + /* + trait Observable[+R] {} + trait Observer[-T] {} + trait Subject[-T, +R] extends Observable[R] with Observer[T] { } + */ + + // We don't make aliases to these types, because they are considered internal/not needed by users: // rx.subjects.AsyncSubject // rx.subjects.BehaviorSubject // rx.subjects.PublishSubject From add46e615c3f3a690c33e4fb6082255d1ed97f32 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Thu, 26 Sep 2013 14:35:01 +0200 Subject: [PATCH 109/333] put unapply of Notifications into companions no more scalac crash --- .../rx/lang/scala/examples/RxScalaDemo.scala | 17 +++++++++++------ .../main/scala/rx/lang/scala/Notification.scala | 10 +++++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index 1b4a8684ac..66be4c51c1 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -23,8 +23,9 @@ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ import rx.lang.scala.concurrency.NewThreadScheduler import rx.lang.scala.util.Timestamped +import java.io.IOException -//@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily +@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @Test def intervalExample() { @@ -385,13 +386,17 @@ class RxScalaDemo extends JUnitSuite { @Test def materializeExample() { def printObservable[T](o: Observable[T]): Unit = { + import Notification._ for (n <- o.materialize.toBlockingObservable) n match { - case Notification.OnNext[T](v) => println("Got value " + v) - case Notification.OnCompleted[T]() => println("Completed") - case Notification.OnError[T](err) => println("Error: ") - } + case OnNext(v) => println("Got value " + v) + case OnCompleted() => println("Completed") + case OnError(err) => println("Error: " + err.getMessage) + } } - val mat = Observable.interval(100 millis).take(3).materialize + val o1 = Observable.interval(100 millis).take(3) + val o2 = o1 ++ Observable(new IOException("Oops")) + printObservable(o1) + printObservable(o2) } def output(s: String): Unit = println(s) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala index 30145f27f5..e7789d13a3 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala @@ -17,6 +17,9 @@ object Notification { class OnNext[+T](val asJava: rx.Notification[_ <: T]) extends Notification[T] { def value: T = asJava.getValue + } + + object OnNext { def unapply[U](n: Notification[U]): Option[U] = n match { case n2: OnNext[U] => Some(n.asJava.getValue) case _ => None @@ -25,13 +28,18 @@ object Notification { class OnError[+T](val asJava: rx.Notification[_ <: T]) extends Notification[T] { def error: Throwable = asJava.getThrowable() + } + + object OnError { def unapply[U](n: Notification[U]): Option[Throwable] = n match { case n2: OnError[U] => Some(n2.asJava.getThrowable) case _ => None } } - class OnCompleted[T](val asJava: rx.Notification[_ <: T]) extends Notification[T] { + class OnCompleted[T](val asJava: rx.Notification[_ <: T]) extends Notification[T] {} + + object OnCompleted { def unapply[U](n: Notification[U]): Option[Unit] = n match { case n2: OnCompleted[U] => Some() case _ => None From f1a3fb27ee46fc48b4f574fefcf624e00bb8656f Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Thu, 26 Sep 2013 14:57:46 +0200 Subject: [PATCH 110/333] add rx.lang.scala.concurrency.Schedulers --- .../rx/lang/scala/examples/RxScalaDemo.scala | 6 +- .../lang/scala/concurrency/Schedulers.scala | 61 +++++++++++++++++++ .../rx/lang/scala/concurrency/package.scala | 18 ++---- 3 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index 66be4c51c1..e97e48af36 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -21,7 +21,7 @@ import rx.lang.scala._ import scala.concurrent.duration._ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ -import rx.lang.scala.concurrency.NewThreadScheduler +import rx.lang.scala.concurrency.Schedulers import rx.lang.scala.util.Timestamped import java.io.IOException @@ -169,10 +169,10 @@ class RxScalaDemo extends JUnitSuite { @Test def schedulersExample() { val o = Observable.interval(100 millis).take(8) - o.observeOn(NewThreadScheduler).subscribe( + o.observeOn(Schedulers.newThread).subscribe( i => println(s"${i}a (on thread #${Thread.currentThread().getId()})") ) - o.observeOn(NewThreadScheduler).subscribe( + o.observeOn(Schedulers.newThread).subscribe( i => println(s"${i}b (on thread #${Thread.currentThread().getId()})") ) waitFor(o) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala new file mode 100644 index 0000000000..290dddfaf9 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala @@ -0,0 +1,61 @@ +package rx.lang.scala.concurrency + +import rx.Scheduler +import java.util.concurrent.Executor +import java.util.concurrent.ScheduledExecutorService + +/** + * Factory methods for creating Schedulers. + */ +object Schedulers { + + /** + * Returns a [[rx.lang.scala.Scheduler]] that executes work immediately on the current thread. + */ + def immediate: Scheduler = rx.concurrency.Schedulers.immediate() + + /** + * Returns a [[rx.lang.scala.Scheduler]] that queues work on the current thread to be executed after the current work completes. + */ + def currentThread: Scheduler = rx.concurrency.Schedulers.currentThread() + + /** + * Returns a [[rx.lang.scala.Scheduler]] that creates a new {@link Thread} for each unit of work. + */ + def newThread: Scheduler = rx.concurrency.Schedulers.newThread + + /** + * Returns a [[rx.lang.scala.Scheduler]] that queues work on an [[java.util.concurrent.Executor]]. + * + * Note that this does not support scheduled actions with a delay. + */ + def executor(executor: Executor): Scheduler = rx.concurrency.Schedulers.executor(executor) + + /** + * Returns a [[rx.lang.scala.Scheduler]] that queues work on an [[java.util.concurrent.ScheduledExecutorService]]. + */ + def executor(executor: ScheduledExecutorService): Scheduler = rx.concurrency.Schedulers.executor(executor) + + /** + * Returns a [[rx.lang.scala.Scheduler]] intended for computational work. + * + * The implementation is backed by a [[java.util.concurrent.ScheduledExecutorService]] thread-pool sized to the number of CPU cores. + * + * This can be used for event-loops, processing callbacks and other computational work. + * + * Do not perform IO-bound work on this scheduler. Use [[rx.lang.scala.concurrency.Schedulers.threadPoolForIO]] instead. + */ + def threadPoolForComputation: Scheduler = rx.concurrency.Schedulers.threadPoolForComputation() + + /** + * [[rx.lang.scala.Scheduler]] intended for IO-bound work. + * + * The implementation is backed by an [[java.util.concurrent.Executor]] thread-pool that will grow as needed. + * + * This can be used for asynchronously performing blocking IO. + * + * Do not perform computational work on this scheduler. Use [[rx.lang.scala.concurrency.Schedulers.threadPoolForComputation]] instead. + */ + def threadPoolForIO: Scheduler = rx.concurrency.Schedulers.threadPoolForIO() + +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala index 9dabd3356b..d787986a06 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala @@ -18,17 +18,11 @@ package rx.lang.scala import rx.concurrency.CurrentThreadScheduler package object concurrency { - /* - TODO - rx.concurrency.CurrentThreadScheduler - rx.concurrency.ExecutorScheduler - rx.concurrency.ImmediateScheduler - rx.concurrency.NewThreadScheduler - rx.concurrency.Schedulers - rx.concurrency.TestScheduler - */ + // These classes are not exposed to Scala users, but are accessible through + // rx.lang.scala.concurrency.Schedulers: - lazy val CurrentThreadScheduler = rx.concurrency.CurrentThreadScheduler.getInstance() - lazy val NewThreadScheduler = rx.concurrency.NewThreadScheduler.getInstance() - + // rx.concurrency.CurrentThreadScheduler + // rx.concurrency.ExecutorScheduler + // rx.concurrency.ImmediateScheduler + // rx.concurrency.NewThreadScheduler } \ No newline at end of file From d3f933c0f040b41424c3f3295bd3fa85275ba94c Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Thu, 26 Sep 2013 17:10:15 +0200 Subject: [PATCH 111/333] improve scaladoc --- language-adaptors/rxjava-scala/README.md | 10 ++---- .../rx/lang/scala/examples/RxScalaDemo.scala | 20 ++++++++--- .../scala/ImplicitFunctionConversions.scala | 8 ++--- .../scala/rx/lang/scala/Notification.scala | 16 +++++++++ .../main/scala/rx/lang/scala/Observable.scala | 14 +++++--- .../lang/scala/concurrency/Schedulers.scala | 8 ++--- .../rx/lang/scala/concurrency/package.scala | 3 ++ .../observables/BlockingObservable.scala | 4 +-- .../rx/lang/scala/observables/package.scala | 8 ++--- .../main/scala/rx/lang/scala/package.scala | 33 +++++++++++-------- .../rx/lang/scala/subjects/package.scala | 3 ++ .../rx/lang/scala/util/Timestamped.scala | 3 ++ .../scala/rx/lang/scala/util/package.scala | 5 ++- 13 files changed, 90 insertions(+), 45 deletions(-) diff --git a/language-adaptors/rxjava-scala/README.md b/language-adaptors/rxjava-scala/README.md index 9cf23dde6e..171e37a5a2 100644 --- a/language-adaptors/rxjava-scala/README.md +++ b/language-adaptors/rxjava-scala/README.md @@ -62,18 +62,12 @@ For more examples, see [RxScalaDemo.scala](https://github.com/Netflix/RxJava/blo Scala code using Rx should only import members from `rx.lang.scala` and below. -Work on this adaptor is still in progress, and for the moment, the best source of documentation are the comments in the source code of [`rx.lang.scala.Observable`](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala). - ## Documentation -You can build the documentation as follows: In the RxJava root directory, run - - ./gradlew :language-adaptors:rxjava-scala:scaladoc - -Then navigate to +You can build the documentation by running `./gradlew scaladoc` in the RxJava root directory. - RxJava/language-adaptors/rxjava-scala/build/docs/scaladoc/index.html +Then navigate to `RxJava/language-adaptors/rxjava-scala/build/docs/scaladoc/index.html` to display it. ## Binaries diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index e97e48af36..b383681531 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -384,19 +384,31 @@ class RxScalaDemo extends JUnitSuite { } } - @Test def materializeExample() { + @Test def materializeExample1() { def printObservable[T](o: Observable[T]): Unit = { import Notification._ - for (n <- o.materialize.toBlockingObservable) n match { + o.materialize.subscribe(n => n match { case OnNext(v) => println("Got value " + v) case OnCompleted() => println("Completed") case OnError(err) => println("Error: " + err.getMessage) - } + }) } + val o1 = Observable.interval(100 millis).take(3) - val o2 = o1 ++ Observable(new IOException("Oops")) + val o2 = Observable(new IOException("Oops")) printObservable(o1) + waitFor(o1) printObservable(o2) + waitFor(o2) + } + + @Test def materializeExample2() { + import Notification._ + Observable(1, 2, 3).materialize.subscribe(n => n match { + case OnNext(v) => println("Got value " + v) + case OnCompleted() => println("Completed") + case OnError(err) => println("Error: " + err.getMessage) + }) } def output(s: String): Unit = println(s) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index 78b1d8d776..f28ee5121a 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -19,10 +19,10 @@ import java.{ lang => jlang } import rx.util.functions._ /** - * These function conversions convert between Scala functions and Rx Funcs and Actions. - * Most users RxScala won't need them, but they might be useful if one wants to use - * the rx.Observable directly instead of using rx.lang.scala.Observable or if one wants - * to use a Java library taking/returning Funcs and Actions. + * These function conversions convert between Scala functions and Rx `Func`s and `Action`s. + * Most RxScala users won't need them, but they might be useful if one wants to use + * the `rx.Observable` directly instead of using `rx.lang.scala.Observable` or if one wants + * to use a Java library taking/returning `Func`s and `Action`s. */ object ImplicitFunctionConversions { import language.implicitConversions diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala index e7789d13a3..c659ecdd6b 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala @@ -1,9 +1,25 @@ package rx.lang.scala +/** + * Emitted by Observables returned by [[rx.lang.scala.Observable.materialize]]. + */ sealed trait Notification[+T] { def asJava: rx.Notification[_ <: T] } +/** + * Provides pattern matching support for Notifications. + * + * Example: + * {{{ + * import Notification._ + * Observable(1, 2, 3).materialize.subscribe(n => n match { + * case OnNext(v) => println("Got value " + v) + * case OnCompleted() => println("Completed") + * case OnError(err) => println("Error: " + err.getMessage) + * }) + * }}} + */ object Notification { def apply[T](n: rx.Notification[_ <: T]): Notification[T] = n.getKind match { diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index afa0b59fcd..38a1f4cfd8 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -20,7 +20,7 @@ package rx.lang.scala /** * The Observable interface that implements the Reactive Pattern. */ -class Observable[+T](val asJava: rx.Observable[_ <: T]) +class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) // Uncommenting this line combined with `new Observable(...)` instead of `new Observable[T](...)` // makes the compiler crash extends AnyVal @@ -641,7 +641,8 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) } /** - *

+ * Returns an Observable which only emits those items for which a given predicate holds. + * * * * @param predicate @@ -1278,7 +1279,7 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) * @param that * an Observable to be merged * @return an Observable that emits items that are the result of flattening the items emitted by - * {$code this} and {$code that} + * {@code this} and {@code that} */ def mergeDelayError[U >: T](that: Observable[U]): Observable[U] = { Observable[U](rx.Observable.mergeDelayError[U](this.asJava, that.asJava)) @@ -1761,6 +1762,9 @@ class Observable[+T](val asJava: rx.Observable[_ <: T]) } +/** + * Provides various ways to construct new Observables. + */ object Observable { import scala.collection.JavaConverters._ import scala.collection.immutable.Range @@ -1982,7 +1986,7 @@ object Observable { // Cannot yet have inner class because of this error message: // "implementation restriction: nested class is not allowed in value class. // This restriction is planned to be removed in subsequent releases." -class WithFilter[+T] private[scala] (p: T => Boolean, asJava: rx.Observable[_ <: T]) { +private[scala] class WithFilter[+T] (p: T => Boolean, asJava: rx.Observable[_ <: T]) { import rx.lang.scala.ImplicitFunctionConversions._ def map[B](f: T => B): Observable[B] = { @@ -2000,7 +2004,7 @@ class WithFilter[+T] private[scala] (p: T => Boolean, asJava: rx.Observable[_ <: // there is no foreach here, that's only available on BlockingObservable } -class UnitTestSuite extends org.scalatest.junit.JUnitSuite { +private[scala] class UnitTestSuite extends org.scalatest.junit.JUnitSuite { import scala.concurrent.duration._ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala index 290dddfaf9..33a77ed132 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala @@ -25,21 +25,21 @@ object Schedulers { def newThread: Scheduler = rx.concurrency.Schedulers.newThread /** - * Returns a [[rx.lang.scala.Scheduler]] that queues work on an [[java.util.concurrent.Executor]]. + * Returns a [[rx.lang.scala.Scheduler]] that queues work on an `java.util.concurrent.Executor`. * * Note that this does not support scheduled actions with a delay. */ def executor(executor: Executor): Scheduler = rx.concurrency.Schedulers.executor(executor) /** - * Returns a [[rx.lang.scala.Scheduler]] that queues work on an [[java.util.concurrent.ScheduledExecutorService]]. + * Returns a [[rx.lang.scala.Scheduler]] that queues work on an `java.util.concurrent.ScheduledExecutorService`. */ def executor(executor: ScheduledExecutorService): Scheduler = rx.concurrency.Schedulers.executor(executor) /** * Returns a [[rx.lang.scala.Scheduler]] intended for computational work. * - * The implementation is backed by a [[java.util.concurrent.ScheduledExecutorService]] thread-pool sized to the number of CPU cores. + * The implementation is backed by a `java.util.concurrent.ScheduledExecutorService` thread-pool sized to the number of CPU cores. * * This can be used for event-loops, processing callbacks and other computational work. * @@ -50,7 +50,7 @@ object Schedulers { /** * [[rx.lang.scala.Scheduler]] intended for IO-bound work. * - * The implementation is backed by an [[java.util.concurrent.Executor]] thread-pool that will grow as needed. + * The implementation is backed by an `java.util.concurrent.Executor` thread-pool that will grow as needed. * * This can be used for asynchronously performing blocking IO. * diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala index d787986a06..507660c23f 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala @@ -17,6 +17,9 @@ package rx.lang.scala import rx.concurrency.CurrentThreadScheduler +/** + * Provides schedulers. + */ package object concurrency { // These classes are not exposed to Scala users, but are accessible through // rx.lang.scala.concurrency.Schedulers: diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala index 4be27d0ae0..eb2bb3bf27 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala @@ -23,7 +23,7 @@ import rx.lang.scala.ImplicitFunctionConversions._ * * You can obtain a BlockingObservable from an Observable using [[Observable.toBlockingObservable]] */ -class BlockingObservable[+T](val asJava: rx.observables.BlockingObservable[_ <: T]) +class BlockingObservable[+T] private[scala] (val asJava: rx.observables.BlockingObservable[_ <: T]) extends AnyVal { @@ -131,7 +131,7 @@ class BlockingObservable[+T](val asJava: rx.observables.BlockingObservable[_ <: // Cannot yet have inner class because of this error message: // "implementation restriction: nested class is not allowed in value class. // This restriction is planned to be removed in subsequent releases." -class WithFilter[+T] private[observables] (p: T => Boolean, asJava: rx.observables.BlockingObservable[_ <: T]) { +private[observables] class WithFilter[+T] (p: T => Boolean, asJava: rx.observables.BlockingObservable[_ <: T]) { import rx.lang.scala.ImplicitFunctionConversions._ // there's no map and flatMap here, they're only available on Observable diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala index ae1181321f..8a2241d086 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala @@ -4,9 +4,9 @@ package rx.lang.scala * Contains special Observables. * * In Scala, this package only contains [[rx.lang.scala.observables.BlockingObservable]]. - * In the corresponding Java package {{{rx.observables}}}, there is also a - * {{{GroupedObservable}}} and a {{{ConnectableObservable}}}, but these are not needed - * in Scala, because we use a pair {{{(key, observable)}}} instead of {{{GroupedObservable}}} - * and a pair {{{(startFunction, observable)}}} instead of {{{ConnectableObservable}}}. + * In the corresponding Java package `rx.observables`, there is also a + * `GroupedObservable` and a `ConnectableObservable`, but these are not needed + * in Scala, because we use a pair `(key, observable)` instead of `GroupedObservable` + * and a pair `(startFunction, observable)` instead of `ConnectableObservable`. */ package object observables {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala index 04ed0ce6af..220f307961 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala @@ -15,17 +15,22 @@ */ package rx.lang +import java.util.concurrent.TimeUnit +import java.util.Date -/** - * This object contains aliases to all types Scala users need to import. - * +/* * Note that: - * - Scala users cannot use Java's type with variance without always using writing + * - Scala users cannot use Java's types with variance without always using writing * e.g. rx.Notification[_ <: T], so we create aliases fixing the variance - * - For consistency, we create aliases for all types + * - For consistency, we create aliases for all types which Scala users need + */ + +/** + * This package contains all classes that RxScala users need. + * + * It mirrors the structure of package `rx`, but implementation classes that RxScala users + * will not need are left out. */ -import java.util.concurrent.TimeUnit -import java.util.Date package object scala { /* @@ -45,23 +50,23 @@ package object scala { /** * Provides a mechanism for receiving push-based notifications. * - * After an Observer calls an [[rx.lang.scala.Observable]]'s {{{subscribe}}} method, the Observable - * calls the Observer's {{{onNext}}} method to provide notifications. A well-behaved Observable will - * call an Observer's {{{onCompleted}}} method exactly once or the Observer's {{{onError}}} method exactly once. + * After an Observer calls an [[rx.lang.scala.Observable]]'s `subscribe` method, the Observable + * calls the Observer's `onNext` method to provide notifications. A well-behaved Observable will + * call an Observer's `onCompleted` method exactly once or the Observer's `onError` method exactly once. */ trait Observer[-T] { /** * Notifies the Observer that the [[rx.lang.scala.Observable]] has finished sending push-based notifications. * - * The [[rx.lang.scala.Observable]] will not call this method if it calls {{{onError}}}. + * The [[rx.lang.scala.Observable]] will not call this method if it calls `onError`. */ def onCompleted(): Unit /** * Notifies the Observer that the [[rx.lang.scala.Observable]] has experienced an error condition. * - * If the [[rx.lang.scala.Observable]] calls this method, it will not thereafter call {{{onNext}}} or {{{onCompleted}}}. + * If the [[rx.lang.scala.Observable]] calls this method, it will not thereafter call `onNext` or `onCompleted`. */ def onError(e: Throwable): Unit @@ -70,7 +75,7 @@ package object scala { * * The [[rx.lang.scala.Observable]] calls this closure 0 or more times. * - * The [[rx.lang.scala.Observable]] will not call this method again after it calls either {{{onCompleted}}} or {{{onError}}}. + * The [[rx.lang.scala.Observable]] will not call this method again after it calls either `onCompleted` or `onError`. */ def onNext(arg: T): Unit } @@ -201,6 +206,8 @@ package object scala { def unsubscribe(): Unit } + import language.implicitConversions + private[scala] implicit def fakeSubscription2RxSubscription(s: Subscription): rx.Subscription = new rx.Subscription { def unsubscribe() = s.unsubscribe() diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala index 22c265aefc..ec096e92eb 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala @@ -1,5 +1,8 @@ package rx.lang.scala +/** + * Provides the type `Subject`. + */ package object subjects { /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala index 93f2ef2089..e40cc0599b 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala @@ -15,6 +15,9 @@ class Timestamped[+T](val asJava: rx.util.Timestamped[_ <: T]) extends AnyVal { def value: T = asJava.getValue : T } +/** + * Provides constructor and pattern matching functionality for `Timestamped`. + */ object Timestamped { def apply[T](timestampMillis: Long, value: T): Timestamped[T] = { new Timestamped(new rx.util.Timestamped(timestampMillis, value)) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala index 1a41f43643..07820f6ea1 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala @@ -15,6 +15,9 @@ */ package rx.lang.scala +/** + * Provides [[rx.lang.scala.util.Opening]]s, [[rx.lang.scala.util.Closing]]s, and [[rx.lang.scala.util.Timestamped]]. + */ package object util { /** @@ -43,4 +46,4 @@ package object util { // rx.util.Range not needed because there's a standard Scala Range -} \ No newline at end of file +} From 45d3523058212b24204495b8f2ff6ad10e05aecf Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Thu, 26 Sep 2013 18:37:20 +0200 Subject: [PATCH 112/333] update README and TODO --- language-adaptors/rxjava-scala/README.md | 4 +++- language-adaptors/rxjava-scala/TODO.md | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/language-adaptors/rxjava-scala/README.md b/language-adaptors/rxjava-scala/README.md index 171e37a5a2..05160e8ad9 100644 --- a/language-adaptors/rxjava-scala/README.md +++ b/language-adaptors/rxjava-scala/README.md @@ -65,7 +65,9 @@ Scala code using Rx should only import members from `rx.lang.scala` and below. ## Documentation -You can build the documentation by running `./gradlew scaladoc` in the RxJava root directory. +The API documentation can be found [here](http://rxscala.github.io/scaladoc/index.html#rx.lang.scala.Observable). + +You can build the API documentation yourself by running `./gradlew scaladoc` in the RxJava root directory. Then navigate to `RxJava/language-adaptors/rxjava-scala/build/docs/scaladoc/index.html` to display it. diff --git a/language-adaptors/rxjava-scala/TODO.md b/language-adaptors/rxjava-scala/TODO.md index d4136236da..4dcf7f2c40 100644 --- a/language-adaptors/rxjava-scala/TODO.md +++ b/language-adaptors/rxjava-scala/TODO.md @@ -4,13 +4,9 @@ TODOs for Scala Adapter This is a (probably incomplete) list of what still needs to be done in the Scala adaptor: -* mirror complete Java package structure in Scala -* objects for classes with static methods or singletons (e.g. Schedulers, Subscriptions) -* Notification as a case class * integrating Scala Futures, should there be a common base interface for Futures and Observables? * Add methods present in Scala collections library, but not in RxJava, e.g. aggregate à la Scala, collect, exists, tails, ... * combineLatest with arities > 2 -* decide where the MovieLib/MovieLibUsage (use Scala code from Java code) example should live and make sure gradle builds it in the right order * Avoid text duplication in scaladoc using templates, add examples, distinction between use case signature and full signature * other small TODOs From af7d056550d3045e59a0b630154fb8ac71f9c836 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 27 Sep 2013 17:08:11 +0200 Subject: [PATCH 113/333] javadoc -> scaladoc translation with the following replacements: \{@link (\w+)#(\w+)\s+(\w+)\} [[$1.$2 $3]] \{@link (\w+)#(\w+)\((.*)\)\} [[$1.$2($3)]] \{@link #(\w+)\} [[Observable.$1]] \{@link (\w+)#(\w+)\} [[$1.$2]] ` ` \*\s*

\s*\n *\n \{@link (\w+)\} [[$1]] \{@link ((\w|\.)+)\} [[$1]] \[\[rx\. [[rx.lang.scala. \{@code\s*(([^}])+)\} `$1` @param <(\w+)> @tparam $1 <(\w+)> [$1] <(\w+)> [$1] "timespan" argument `timespan` argument "timeshift" argument `timeshift` argument \[\[Action0\]\] function plus some manual replacements --- .../main/scala/rx/lang/scala/Observable.scala | 810 +++++++++--------- 1 file changed, 403 insertions(+), 407 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 38a1f4cfd8..310fb78f76 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -36,59 +36,59 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) import rx.lang.scala.ImplicitFunctionConversions._ /** - * An {@link Observer} must call an Observable's {@code subscribe} method in order to + * An [[Observer]] must call an Observable's `subscribe` method in order to * receive items and notifications from the Observable. * - *

A typical implementation of {@code subscribe} does the following: - *

- * It stores a reference to the Observer in a collection object, such as a {@code List} object. - *

- * It returns a reference to the {@link Subscription} interface. This enables Observers to + * A typical implementation of `subscribe` does the following: + * + * It stores a reference to the Observer in a collection object, such as a `List[T]` object. + * + * It returns a reference to the [[Subscription]] interface. This enables Observers to * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's {@link Observer#onCompleted onCompleted} method. - *

- * An Observable<T> instance is responsible for accepting all subscriptions + * sending them, which also invokes the Observer's [[Observer.onCompleted onCompleted]] method. + * + * An `Observable[T]` instance is responsible for accepting all subscriptions * and notifying all Observers. Unless the documentation for a particular - * Observable<T> implementation indicates otherwise, Observers should make no + * `Observable[T]` implementation indicates otherwise, Observers should make no * assumptions about the order in which multiple Observers will receive their notifications. - *

+ * * * @param observer * the observer - * @return a {@link Subscription} reference with which the {@link Observer} can stop receiving items + * @return a [[Subscription]] reference with which the [[Observer]] can stop receiving items * before the Observable has finished sending them * @throws IllegalArgumentException - * if the {@link Observer} provided as the argument to {@code subscribe()} is {@code null} + * if the [[Observer]] provided as the argument to `subscribe()` is `null` */ def subscribe(observer: Observer[T]): Subscription = { asJava.subscribe(observer) } /** - * An {@link Observer} must call an Observable's {@code subscribe} method in order to + * An [[Observer]] must call an Observable's `subscribe` method in order to * receive items and notifications from the Observable. * - *

A typical implementation of {@code subscribe} does the following: - *

- * It stores a reference to the Observer in a collection object, such as a {@code List} object. - *

- * It returns a reference to the {@link Subscription} interface. This enables Observers to + * A typical implementation of `subscribe` does the following: + * + * It stores a reference to the Observer in a collection object, such as a `List[T]` object. + * + * It returns a reference to the [[Subscription]] interface. This enables Observers to * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's {@link Observer#onCompleted onCompleted} method. - *

- * An {@code Observable} instance is responsible for accepting all subscriptions - * and notifying all Observers. Unless the documentation for a particular {@code Observable} implementation indicates otherwise, Observers should make no + * sending them, which also invokes the Observer's [[Observer.onCompleted onCompleted]] method. + * + * An `Observable[T]` instance is responsible for accepting all subscriptions + * and notifying all Observers. Unless the documentation for a particular `Observable[T]` implementation indicates otherwise, Observers should make no * assumptions about the order in which multiple Observers will receive their notifications. - *

+ * * * @param observer * the observer * @param scheduler - * the {@link Scheduler} on which Observers subscribe to the Observable - * @return a {@link Subscription} reference with which Observers can stop receiving items and + * the [[Scheduler]] on which Observers subscribe to the Observable + * @return a [[Subscription]] reference with which Observers can stop receiving items and * notifications before the Observable has finished sending them * @throws IllegalArgumentException - * if an argument to {@code subscribe()} is {@code null} + * if an argument to `subscribe()` is `null` */ def subscribe(observer: Observer[T], scheduler: Scheduler): Subscription = { asJava.subscribe(observer, scheduler) @@ -119,16 +119,16 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Returns a {@link ConnectableObservable} that upon connection causes the source Observable to + * Returns a [[ConnectableObservable]] that upon connection causes the source Observable to * push results into the specified subject. * * @param subject - * the {@link Subject} for the {@link ConnectableObservable} to push source items + * the [[Subject]] for the [[ConnectableObservable]] to push source items * into - * @param + * @tparam R * result type - * @return a pair of a start function and an {@link Observable} such that when the start function - * is called, the Observable starts to push results into the specified {@link Subject} + * @return a pair of a start function and an [[Observable]] such that when the start function + * is called, the Observable starts to push results into the specified [[Subject]] */ def multicast[R](subject: Subject[T, R]): (() => Subscription, Observable[R]) = { val javaCO = asJava.multicast[R](subject) @@ -138,7 +138,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that first emits the items emitted by this, and then the items emitted * by that. - *

+ * * * * @param that @@ -155,7 +155,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that emits the items emitted by two or more Observables, one after the * other. - *

+ * * * * @return an Observable that emits items that are the result of combining the items emitted by @@ -172,27 +172,27 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Wraps this Observable in another Observable that ensures that the resulting * Observable is chronologically well-behaved. - *

+ * * - *

- * A well-behaved Observable does not interleave its invocations of the {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, and {@link Observer#onError onError} methods of - * its {@link Observer}s; it invokes {@code onCompleted} or {@code onError} only once; and it never invokes {@code onNext} after invoking either {@code onCompleted} or {@code onError}. - * {@code synchronize} enforces this, and the Observable it returns invokes {@code onNext} and {@code onCompleted} or {@code onError} synchronously. + * + * A well-behaved Observable does not interleave its invocations of the [[Observer.onNext onNext]], [[Observer.onCompleted onCompleted]], and [[Observer.onError onError]] methods of + * its [[Observer]]s; it invokes `onCompleted` or `onError` only once; and it never invokes `onNext` after invoking either `onCompleted` or `onError`. + * `synchronize` enforces this, and the Observable it returns invokes `onNext` and `onCompleted` or `onError` synchronously. * * @param observable * the source Observable - * @param + * @tparam T * the type of item emitted by the source Observable * @return an Observable that is a chronologically well-behaved version of the source - * Observable, and that synchronously notifies its {@link Observer}s + * Observable, and that synchronously notifies its [[Observer]]s */ def synchronize: Observable[T] = { Observable[T](asJava.synchronize) } /** - * Wraps each item emitted by a source Observable in a {@link Timestamped} object. - *

+ * Wraps each item emitted by a source Observable in a [[Timestamped]] object. + * * * * @return an Observable that emits timestamped items from the source Observable @@ -204,14 +204,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable formed from this Observable and another Observable by combining * corresponding elements in pairs. - * The number of {@code onNext} invocations of the resulting {@code Observable[(T, U)]} - * is the minumum of the number of {@code onNext} invocations of {@code this} and {@code that}. + * The number of `onNext` invocations of the resulting `Observable[(T, U)]` + * is the minumum of the number of `onNext` invocations of `this` and `that`. */ def zip[U](that: Observable[U]): Observable[(T, U)] = { Observable[(T, U)](JObservable.zip[T, U, (T, U)](this.asJava, that.asJava, (t: T, u: U) => (t, u))) } - // public static Observable zip(Observable> ws, final FuncN zipFunction) { + // public static [R] Observable[R] zip(Observable> ws, final FuncN zipFunction) { /** * Zips this Observable with its indices. @@ -228,17 +228,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. * - *

This Observable produces connected non-overlapping buffers. The current buffer is - * emitted and replaced with a new buffer when the Observable produced by the specified {@link Func0} produces a {@link rx.util.Closing} object. The * {@link Func0} will then + * This Observable produces connected non-overlapping buffers. The current buffer is + * emitted and replaced with a new buffer when the Observable produced by the specified [[Func0]] produces a [[rx.lang.scala.util.Closing]] object. The * [[Func0]] will then * be used to create a new Observable to listen for the end of the next buffer. * * @param bufferClosingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every buffer created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated buffer + * The [[Func0]] which is used to produce an [[Observable]] for every buffer created. + * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer * is emitted and replaced with a new one. * @return - * An {@link Observable} which produces connected non-overlapping buffers, which are emitted - * when the current {@link Observable} created with the {@link Func0} argument produces a {@link rx.util.Closing} object. + * An [[Observable]] which produces connected non-overlapping buffers, which are emitted + * when the current [[Observable]] created with the [[Func0]] argument produces a [[rx.lang.scala.util.Closing]] object. */ def buffer(bufferClosingSelector: () => Observable[Closing]) : Observable[Seq[T]] = { val f: Func0[_ <: rx.Observable[_ <: Closing]] = bufferClosingSelector().asJava @@ -249,20 +249,20 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. * - *

This Observable produces buffers. Buffers are created when the specified "bufferOpenings" - * Observable produces a {@link rx.util.Opening} object. Additionally the {@link Func0} argument - * is used to create an Observable which produces {@link rx.util.Closing} objects. When this + * This Observable produces buffers. Buffers are created when the specified "bufferOpenings" + * Observable produces a [[rx.lang.scala.util.Opening]] object. Additionally the [[Func0]] argument + * is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. When this * Observable produces such an object, the associated buffer is emitted. * * @param bufferOpenings - * The {@link Observable} which, when it produces a {@link rx.util.Opening} object, will cause + * The [[Observable]] which, when it produces a [[rx.lang.scala.util.Opening]] object, will cause * another buffer to be created. * @param bufferClosingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every buffer created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated buffer + * The [[Func0]] which is used to produce an [[Observable]] for every buffer created. + * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer * is emitted. * @return - * An {@link Observable} which produces buffers which are created and emitted when the specified {@link Observable}s publish certain objects. + * An [[Observable]] which produces buffers which are created and emitted when the specified [[Observable]]s publish certain objects. */ def buffer(bufferOpenings: Observable[Opening], bufferClosingSelector: Opening => Observable[Closing]): Observable[Seq[T]] = { val opening: rx.Observable[_ <: Opening] = bufferOpenings.asJava @@ -274,14 +274,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. * - *

This Observable produces connected non-overlapping buffers, each containing "count" + * This Observable produces connected non-overlapping buffers, each containing "count" * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. * * @param count * The maximum size of each buffer before it should be emitted. * @return - * An {@link Observable} which produces connected non-overlapping buffers containing at most + * An [[Observable]] which produces connected non-overlapping buffers containing at most * "count" produced values. */ def buffer(count: Int): Observable[Seq[T]] = { @@ -292,7 +292,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. * - *

This Observable produces buffers every "skip" values, each containing "count" + * This Observable produces buffers every "skip" values, each containing "count" * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. * @@ -300,9 +300,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * The maximum size of each buffer before it should be emitted. * @param skip * How many produced values need to be skipped before starting a new buffer. Note that when "skip" and - * "count" are equals that this is the same operation as {@link Observable#buffer(int)}. + * "count" are equals that this is the same operation as [[Observable.buffer(int)]]. * @return - * An {@link Observable} which produces buffers every "skipped" values containing at most + * An [[Observable]] which produces buffers every "skipped" values containing at most * "count" produced values. */ def buffer(count: Int, skip: Int): Observable[Seq[T]] = { @@ -313,15 +313,15 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. * - *

This Observable produces connected non-overlapping buffers, each of a fixed duration - * specified by the "timespan" argument. When the source Observable completes or encounters + * This Observable produces connected non-overlapping buffers, each of a fixed duration + * specified by the `timespan` argument. When the source Observable completes or encounters * an error, the current buffer is emitted and the event is propagated. * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. * @return - * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. + * An [[Observable]] which produces connected non-overlapping buffers with a fixed duration. */ def buffer(timespan: Duration): Observable[Seq[T]] = { val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(timespan.length, timespan.unit) @@ -331,17 +331,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. * - *

This Observable produces connected non-overlapping buffers, each of a fixed duration - * specified by the "timespan" argument. When the source Observable completes or encounters + * This Observable produces connected non-overlapping buffers, each of a fixed duration + * specified by the `timespan` argument. When the source Observable completes or encounters * an error, the current buffer is emitted and the event is propagated. * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. + * The [[Scheduler]] to use when determining the end and start of a buffer. * @return - * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. + * An [[Observable]] which produces connected non-overlapping buffers with a fixed duration. */ def buffer(timespan: Duration, scheduler: Scheduler): Observable[Seq[T]] = { val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(timespan.length, timespan.unit, scheduler) @@ -350,7 +350,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size + * non-overlapping buffers, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the "count" argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. * @@ -360,7 +360,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param count * The maximum size of each buffer before it should be emitted. * @return - * An {@link Observable} which produces connected non-overlapping buffers which are emitted after + * An [[Observable]] which produces connected non-overlapping buffers which are emitted after * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). */ def buffer(timespan: Duration, count: Int): Observable[Seq[T]] = { @@ -370,7 +370,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size + * non-overlapping buffers, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the "count" argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. * @@ -380,9 +380,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param count * The maximum size of each buffer before it should be emitted. * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. + * The [[Scheduler]] to use when determining the end and start of a buffer. * @return - * An {@link Observable} which produces connected non-overlapping buffers which are emitted after + * An [[Observable]] which produces connected non-overlapping buffers which are emitted after * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). */ def buffer(timespan: Duration, count: Int, scheduler: Scheduler): Observable[Seq[T]] = { @@ -392,8 +392,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. This Observable starts a new buffer - * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the + * periodically, which is determined by the `timeshift` argument. Each buffer is emitted after a fixed timespan + * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. * * @param timespan @@ -401,7 +401,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param timeshift * The period of time after which a new buffer will be created. * @return - * An {@link Observable} which produces new buffers periodically, and these are emitted after + * An [[Observable]] which produces new buffers periodically, and these are emitted after * a fixed timespan has elapsed. */ def buffer(timespan: Duration, timeshift: Duration): Observable[Seq[T]] = { @@ -414,8 +414,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. This Observable starts a new buffer - * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the + * periodically, which is determined by the `timeshift` argument. Each buffer is emitted after a fixed timespan + * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. * * @param timespan @@ -423,9 +423,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param timeshift * The period of time after which a new buffer will be created. * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. + * The [[Scheduler]] to use when determining the end and start of a buffer. * @return - * An {@link Observable} which produces new buffers periodically, and these are emitted after + * An [[Observable]] which produces new buffers periodically, and these are emitted after * a fixed timespan has elapsed. */ def buffer(timespan: Duration, timeshift: Duration, scheduler: Scheduler): Observable[Seq[T]] = { @@ -439,16 +439,16 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows. The current window is emitted and replaced with a new window when the - * Observable produced by the specified {@link Func0} produces a {@link rx.util.Closing} object. The {@link Func0} will then be used to create a new Observable to listen for the end of the next + * Observable produced by the specified [[Func0]] produces a [[rx.lang.scala.util.Closing]] object. The [[Func0]] will then be used to create a new Observable to listen for the end of the next * window. * * @param closingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every window created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated window + * The [[Func0]] which is used to produce an [[Observable]] for every window created. + * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window * is emitted and replaced with a new one. * @return - * An {@link Observable} which produces connected non-overlapping windows, which are emitted - * when the current {@link Observable} created with the {@link Func0} argument produces a {@link rx.util.Closing} object. + * An [[Observable]] which produces connected non-overlapping windows, which are emitted + * when the current [[Observable]] created with the [[Func0]] argument produces a [[rx.lang.scala.util.Closing]] object. */ def window(closingSelector: () => Observable[Closing]): Observable[Observable[T]] = { val func : Func0[_ <: rx.Observable[_ <: Closing]] = closingSelector().asJava @@ -462,19 +462,19 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces windows. - * Chunks are created when the specified "windowOpenings" Observable produces a {@link rx.util.Opening} object. - * Additionally the {@link Func0} argument is used to create an Observable which produces {@link rx.util.Closing} objects. When this Observable produces such an object, the associated window is + * Chunks are created when the specified "windowOpenings" Observable produces a [[rx.lang.scala.util.Opening]] object. + * Additionally the [[Func0]] argument is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. When this Observable produces such an object, the associated window is * emitted. * * @param windowOpenings - * The {@link Observable} which when it produces a {@link rx.util.Opening} object, will cause + * The [[Observable]] which when it produces a [[rx.lang.scala.util.Opening]] object, will cause * another window to be created. * @param closingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every window created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated window + * The [[Func0]] which is used to produce an [[Observable]] for every window created. + * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window * is emitted. * @return - * An {@link Observable} which produces windows which are created and emitted when the specified {@link Observable}s publish certain objects. + * An [[Observable]] which produces windows which are created and emitted when the specified [[Observable]]s publish certain objects. */ def window(windowOpenings: Observable[Opening], closingSelector: Opening => Observable[Closing]) = { Observable.jObsOfJObsToScObsOfScObs( @@ -490,7 +490,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param count * The maximum size of each window before it should be emitted. * @return - * An {@link Observable} which produces connected non-overlapping windows containing at most + * An [[Observable]] which produces connected non-overlapping windows containing at most * "count" produced values. */ def window(count: Int): Observable[Observable[T]] = { @@ -508,9 +508,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * The maximum size of each window before it should be emitted. * @param skip * How many produced values need to be skipped before starting a new window. Note that when "skip" and - * "count" are equals that this is the same operation as {@link Observable#window(Observable, int)}. + * "count" are equals that this is the same operation as [[Observable.window(Observable, int)]]. * @return - * An {@link Observable} which produces windows every "skipped" values containing at most + * An [[Observable]] which produces windows every "skipped" values containing at most * "count" produced values. */ def window(count: Int, skip: Int): Observable[Observable[T]] = { @@ -520,14 +520,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument. When the source + * non-overlapping windows, each of a fixed duration specified by the `timespan` argument. When the source * Observable completes or encounters an error, the current window is emitted and the event is propagated. * * @param timespan * The period of time each window is collecting values before it should be emitted, and * replaced with a new window. * @return - * An {@link Observable} which produces connected non-overlapping windows with a fixed duration. + * An [[Observable]] which produces connected non-overlapping windows with a fixed duration. */ def window(timespan: Duration): Observable[Observable[T]] = { Observable.jObsOfJObsToScObsOfScObs(asJava.window(timespan.length, timespan.unit)) @@ -536,16 +536,16 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument. When the source + * non-overlapping windows, each of a fixed duration specified by the `timespan` argument. When the source * Observable completes or encounters an error, the current window is emitted and the event is propagated. * * @param timespan * The period of time each window is collecting values before it should be emitted, and * replaced with a new window. * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a window. + * The [[Scheduler]] to use when determining the end and start of a window. * @return - * An {@link Observable} which produces connected non-overlapping windows with a fixed duration. + * An [[Observable]] which produces connected non-overlapping windows with a fixed duration. */ def window(timespan: Duration, scheduler: Scheduler): Observable[Observable[T]] = { Observable.jObsOfJObsToScObsOfScObs(asJava.window(timespan.length, timespan.unit, scheduler)) @@ -554,7 +554,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument or a maximum size + * non-overlapping windows, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the "count" argument (which ever is reached first). When the source Observable completes * or encounters an error, the current window is emitted and the event is propagated. * @@ -564,7 +564,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param count * The maximum size of each window before it should be emitted. * @return - * An {@link Observable} which produces connected non-overlapping windows which are emitted after + * An [[Observable]] which produces connected non-overlapping windows which are emitted after * a fixed duration or when the window has reached maximum capacity (which ever occurs first). */ def window(timespan: Duration, count: Int): Observable[Observable[T]] = { @@ -574,7 +574,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument or a maximum size + * non-overlapping windows, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the "count" argument (which ever is reached first). When the source Observable completes * or encounters an error, the current window is emitted and the event is propagated. * @@ -584,9 +584,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param count * The maximum size of each window before it should be emitted. * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a window. + * The [[Scheduler]] to use when determining the end and start of a window. * @return - * An {@link Observable} which produces connected non-overlapping windows which are emitted after + * An [[Observable]] which produces connected non-overlapping windows which are emitted after * a fixed duration or when the window has reached maximum capacity (which ever occurs first). */ def window(timespan: Duration, count: Int, scheduler: Scheduler): Observable[Observable[T]] = { @@ -596,8 +596,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable starts a new window - * periodically, which is determined by the "timeshift" argument. Each window is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the + * periodically, which is determined by the `timeshift` argument. Each window is emitted after a fixed timespan + * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current window is emitted and the event is propagated. * * @param timespan @@ -605,7 +605,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param timeshift * The period of time after which a new window will be created. * @return - * An {@link Observable} which produces new windows periodically, and these are emitted after + * An [[Observable]] which produces new windows periodically, and these are emitted after * a fixed timespan has elapsed. */ def window(timespan: Duration, timeshift: Duration): Observable[Observable[T]] = { @@ -618,8 +618,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable starts a new window - * periodically, which is determined by the "timeshift" argument. Each window is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the + * periodically, which is determined by the `timeshift` argument. Each window is emitted after a fixed timespan + * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current window is emitted and the event is propagated. * * @param timespan @@ -627,9 +627,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param timeshift * The period of time after which a new window will be created. * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a window. + * The [[Scheduler]] to use when determining the end and start of a window. * @return - * An {@link Observable} which produces new windows periodically, and these are emitted after + * An [[Observable]] which produces new windows periodically, and these are emitted after * a fixed timespan has elapsed. */ def window(timespan: Duration, timeshift: Duration, scheduler: Scheduler): Observable[Observable[T]] = { @@ -646,22 +646,22 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * @param predicate - * a function that evaluates the items emitted by the source Observable, returning {@code true} if they pass the filter + * a function that evaluates the items emitted by the source Observable, returning `true` if they pass the filter * @return an Observable that emits only those items in the original Observable that the filter - * evaluates as {@code true} + * evaluates as `true` */ def filter(predicate: T => Boolean): Observable[T] = { Observable[T](asJava.filter(predicate)) } /** - * Registers an {@link Action0} to be called when this Observable invokes {@link Observer#onCompleted onCompleted} or {@link Observer#onError onError}. - *

+ * Registers an function to be called when this Observable invokes [[Observer.onCompleted onCompleted]] or [[Observer.onError onError]]. + * * * * @param action - * an {@link Action0} to be invoked when the source Observable finishes - * @return an Observable that emits the same items as the source Observable, then invokes the {@link Action0} + * an function to be invoked when the source Observable finishes + * @return an Observable that emits the same items as the source Observable, then invokes the function * @see MSDN: Observable.Finally Method */ def finallyDo(action: () => Unit): Observable[T] = { @@ -672,10 +672,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Creates a new Observable by applying a function that you supply to each item emitted by * the source Observable, where that function returns an Observable, and then merging those * resulting Observables and emitting the results of this merger. - *

+ * * - *

- * Note: {@code mapMany} and {@code flatMap} are equivalent. * * @param func * a function that, when applied to an item emitted by the source Observable, returns @@ -683,7 +681,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable that emits the result of applying the transformation function to each * item emitted by the source Observable and merging the results of the Observables * obtained from this transformation. - * @see #mapMany(Func1) */ def flatMap[R](f: T => Observable[R]): Observable[R] = { Observable[R](asJava.flatMap[R]((t: T) => f(t).asJava)) @@ -692,7 +689,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that applies the given function to each item emitted by an * Observable and emits the result. - *

+ * * * * @param func @@ -705,8 +702,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Turns all of the notifications from a source Observable into {@link Observer#onNext onNext} emissions, and marks them with their original notification types within {@link Notification} objects. - *

+ * Turns all of the notifications from a source Observable into [[Observer.onNext onNext]] emissions, and marks them with their original notification types within [[Notification]] objects. + * * * * @return an Observable whose items are the result of materializing the items and @@ -718,41 +715,41 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Asynchronously subscribes and unsubscribes Observers on the specified {@link Scheduler}. - *

+ * Asynchronously subscribes and unsubscribes Observers on the specified [[Scheduler]]. + * * * * @param scheduler - * the {@link Scheduler} to perform subscription and unsubscription actions on + * the [[Scheduler]] to perform subscription and unsubscription actions on * @return the source Observable modified so that its subscriptions and unsubscriptions happen - * on the specified {@link Scheduler} + * on the specified [[Scheduler]] */ def subscribeOn(scheduler: Scheduler): Observable[T] = { Observable[T](asJava.subscribeOn(scheduler)) } /** - * Asynchronously notify {@link Observer}s on the specified {@link Scheduler}. - *

+ * Asynchronously notify [[Observer]]s on the specified [[Scheduler]]. + * * * * @param scheduler - * the {@link Scheduler} to notify {@link Observer}s on - * @return the source Observable modified so that its {@link Observer}s are notified on the - * specified {@link Scheduler} + * the [[Scheduler]] to notify [[Observer]]s on + * @return the source Observable modified so that its [[Observer]]s are notified on the + * specified [[Scheduler]] */ def observeOn(scheduler: Scheduler): Observable[T] = { Observable[T](asJava.observeOn(scheduler)) } /** - * Returns an Observable that reverses the effect of {@link #materialize materialize} by - * transforming the {@link Notification} objects emitted by the source Observable into the items + * Returns an Observable that reverses the effect of [[Observable.materialize]] by + * transforming the [[Notification]] objects emitted by the source Observable into the items * or notifications they represent. - *

+ * * * - * @return an Observable that emits the items and notifications embedded in the {@link Notification} objects emitted by the source Observable + * @return an Observable that emits the items and notifications embedded in the [[Notification]] objects emitted by the source Observable */ // with =:= it does not work, why? def dematerialize[U](implicit evidence: Observable[T] <:< Observable[Notification[U]]): Observable[U] = { @@ -763,21 +760,21 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error. - *

+ * Instruct an Observable to pass control to another Observable rather than invoking [[Observer.onError onError]] if it encounters an error. + * * - *

+ * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorResumeNext method changes this behavior. If you pass a - * function that returns an Observable (resumeFunction) to - * onErrorResumeNext, if the original Observable encounters an error, instead of - * invoking its Observer's onError method, it will instead relinquish control to - * the Observable returned from resumeFunction, which will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no - * Observable necessarily invokes onError, the Observer may never know that an + * expected item to its [[Observer]], the Observable invokes its Observer's + * `onError` method, and then quits without invoking any more of its Observer's + * methods. The `onErrorResumeNext` method changes this behavior. If you pass a + * function that returns an Observable (`resumeFunction`) to + * `onErrorResumeNext`, if the original Observable encounters an error, instead of + * invoking its Observer's `onError` method, it will instead relinquish control to + * the Observable returned from `resumeFunction`, which will invoke the Observer's [[Observer.onNext onNext]] method if it is able to do so. In such a case, because no + * Observable necessarily invokes `onError`, the Observer may never know that an * error happened. - *

+ * * You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. * @@ -793,21 +790,21 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error. - *

+ * Instruct an Observable to pass control to another Observable rather than invoking [[Observer.onError onError]] if it encounters an error. + * * - *

+ * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorResumeNext method changes this behavior. If you pass - * another Observable (resumeSequence) to an Observable's - * onErrorResumeNext method, if the original Observable encounters an error, - * instead of invoking its Observer's onError method, it will instead relinquish - * control to resumeSequence which will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no - * Observable necessarily invokes onError, the Observer may never know that an + * expected item to its [[Observer]], the Observable invokes its Observer's + * `onError` method, and then quits without invoking any more of its Observer's + * methods. The `onErrorResumeNext` method changes this behavior. If you pass + * another Observable (`resumeSequence`) to an Observable's + * `onErrorResumeNext` method, if the original Observable encounters an error, + * instead of invoking its Observer's `onError` method, it will instead relinquish + * control to `resumeSequence` which will invoke the Observer's [[Observer.onNext onNext]] method if it is able to do so. In such a case, because no + * Observable necessarily invokes `onError`, the Observer may never know that an * error happened. - *

+ * * You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. * @@ -823,23 +820,23 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error of type {@link java.lang.Exception}. - *

- * This differs from {@link #onErrorResumeNext} in that this one does not handle {@link java.lang.Throwable} or {@link java.lang.Error} but lets those continue through. - *

+ * Instruct an Observable to pass control to another Observable rather than invoking [[Observer.onError onError]] if it encounters an error of type [[java.lang.Exception]]. + * + * This differs from [[Observable.onErrorResumeNext]] in that this one does not handle [[java.lang.Throwable]] or [[java.lang.Error]] but lets those continue through. + * * - *

+ * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorResumeNext method changes this behavior. If you pass - * another Observable (resumeSequence) to an Observable's - * onErrorResumeNext method, if the original Observable encounters an error, - * instead of invoking its Observer's onError method, it will instead relinquish - * control to resumeSequence which will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no - * Observable necessarily invokes onError, the Observer may never know that an + * expected item to its [[Observer]], the Observable invokes its Observer's + * `onError` method, and then quits without invoking any more of its Observer's + * methods. The `onErrorResumeNext` method changes this behavior. If you pass + * another Observable (`resumeSequence`) to an Observable's + * `onErrorResumeNext` method, if the original Observable encounters an error, + * instead of invoking its Observer's `onError` method, it will instead relinquish + * control to `resumeSequence` which will invoke the Observer's [[Observer.onNext onNext]] method if it is able to do so. In such a case, because no + * Observable necessarily invokes `onError`, the Observer may never know that an * error happened. - *

+ * * You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. * @@ -856,19 +853,19 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Instruct an Observable to emit an item (returned by a specified function) rather than - * invoking {@link Observer#onError onError} if it encounters an error. - *

+ * invoking [[Observer.onError onError]] if it encounters an error. + * * - *

+ * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorReturn method changes this behavior. If you pass a function - * (resumeFunction) to an Observable's onErrorReturn method, if the + * expected item to its [[Observer]], the Observable invokes its Observer's + * `onError` method, and then quits without invoking any more of its Observer's + * methods. The `onErrorReturn` method changes this behavior. If you pass a function + * (`resumeFunction`) to an Observable's `onErrorReturn` method, if the * original Observable encounters an error, instead of invoking its Observer's - * onError method, it will instead pass the return value of - * resumeFunction to the Observer's {@link Observer#onNext onNext} method. - *

+ * `onError` method, it will instead pass the return value of + * `resumeFunction` to the Observer's [[Observer.onNext onNext]] method. + * * You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. * @@ -889,12 +886,12 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * by the source Observable into the same function, and so on until all items have been emitted * by the source Observable, and emits the final result from the final call to your function as * its sole item. - *

+ * * - *

+ * * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, - * has an inject method that does a similar operation on lists. + * has an `inject` method that does a similar operation on lists. * * @param accumulator * An accumulator function to be invoked on each item emitted by the source @@ -911,13 +908,13 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the underlying - * Observable that will replay all of its items and notifications to any future {@link Observer}. - *

+ * Returns a [[ConnectableObservable]] that shares a single subscription to the underlying + * Observable that will replay all of its items and notifications to any future [[Observer]]. + * * * - * @return a pair of a start function and an {@link Observable} such that when the start function - * is called, the Observable starts to emit items to its {@link Observer}s + * @return a pair of a start function and an [[Observable]] such that when the start function + * is called, the Observable starts to emit items to its [[Observer]]s */ def replay: (() => Subscription, Observable[T]) = { val javaCO = asJava.replay() @@ -925,16 +922,16 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * This method has similar behavior to {@link #replay} except that this auto-subscribes to - * the source Observable rather than returning a {@link ConnectableObservable}. - *

+ * This method has similar behavior to [[Observable.replay]] except that this auto-subscribes to + * the source Observable rather than returning a [[ConnectableObservable]]. + * * - *

+ * * This is useful when you want an Observable to cache responses and you can't control the - * subscribe/unsubscribe behavior of all the {@link Observer}s. - *

+ * subscribe/unsubscribe behavior of all the [[Observer]]s. + * * NOTE: You sacrifice the ability to unsubscribe from the origin when you use the - * cache() operator so be careful not to use this operator on Observables that + * `cache()` operator so be careful not to use this operator on Observables that * emit an infinite or very large number of items that will use up memory. * * @return an Observable that when first subscribed to, caches all of its notifications for @@ -945,13 +942,13 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Returns a {@link ConnectableObservable}, which waits until its {@link ConnectableObservable#connect connect} method is called before it begins emitting - * items to those {@link Observer}s that have subscribed to it. - *

+ * Returns a [[ConnectableObservable]], which waits until its [[ConnectableObservable.connect connect]] method is called before it begins emitting + * items to those [[Observer]]s that have subscribed to it. + * * * - * @return a pair of a start function and an {@link Observable} such that when the start function - * is called, the Observable starts to emit items to its {@link Observer}s + * @return a pair of a start function and an [[Observable]] such that when the start function + * is called, the Observable starts to emit items to its [[Observer]]s */ def publish: (() => Subscription, Observable[T]) = { val javaCO = asJava.publish() @@ -966,12 +963,12 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * by an Observable into the same function, and so on until all items have been emitted by the * source Observable, emitting the final result from the final call to your function as its sole * item. - *

+ * * - *

+ * * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, - * has an inject method that does a similar operation on lists. + * has an `inject` method that does a similar operation on lists. * * @param initialValue * the initial (seed) accumulator value @@ -990,13 +987,13 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that emits the results of sampling the items emitted by the source * Observable at a specified time interval. - *

+ * * * * @param period * the sampling rate * @param unit - * the {@link TimeUnit} in which period is defined + * the [[TimeUnit]] in which `period` is defined * @return an Observable that emits the results of sampling the items emitted by the source * Observable at the specified time interval */ @@ -1007,15 +1004,15 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that emits the results of sampling the items emitted by the source * Observable at a specified time interval. - *

+ * * * * @param period * the sampling rate * @param unit - * the {@link TimeUnit} in which period is defined + * the [[TimeUnit]] in which `period` is defined * @param scheduler - * the {@link Scheduler} to use when sampling + * the [[Scheduler]] to use when sampling * @return an Observable that emits the results of sampling the items emitted by the source * Observable at the specified time interval */ @@ -1028,19 +1025,19 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * source Observable, then feeds the result of that function along with the second item emitted * by an Observable into the same function, and so on until all items have been emitted by the * source Observable, emitting the result of each of these iterations. - *

+ * * - *

+ * * This sort of function is sometimes called an accumulator. - *

- * Note that when you pass a seed to scan() the resulting Observable will emit + * + * Note that when you pass a seed to `scan()` the resulting Observable will emit * that seed as its first emitted item. * * @param initialValue * the initial (seed) accumulator value * @param accumulator * an accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be emitted to {@link Observer}s via {@link Observer#onNext onNext} and used in the next accumulator call. + * Observable, whose result will be emitted to [[Observer]]s via [[Observer.onNext onNext]] and used in the next accumulator call. * @return an Observable that emits the results of each call to the accumulator function * @see MSDN: Observable.Scan */ @@ -1051,13 +1048,13 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that emits a Boolean that indicates whether all of the items emitted by * the source Observable satisfy a condition. - *

+ * * * * @param predicate * a function that evaluates an item and returns a Boolean - * @return an Observable that emits true if all items emitted by the source - * Observable satisfy the predicate; otherwise, false + * @return an Observable that emits `true` if all items emitted by the source + * Observable satisfy the predicate; otherwise, `false` */ def forall(predicate: T => Boolean): Observable[Boolean] = { // type mismatch; found : rx.Observable[java.lang.Boolean] required: rx.Observable[_ <: scala.Boolean] @@ -1067,18 +1064,18 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Returns an Observable that skips the first num items emitted by the source + * Returns an Observable that skips the first `num` items emitted by the source * Observable and emits the remainder. - *

+ * * - *

- * You can ignore the first num items emitted by an Observable and attend only to - * those items that come after, by modifying the Observable with the skip method. + * + * You can ignore the first `num` items emitted by an Observable and attend only to + * those items that come after, by modifying the Observable with the `skip` method. * * @param num * the number of items to skip * @return an Observable that is identical to the source Observable except that it does not - * emit the first num items that the source emits + * emit the first `num` items that the source emits */ def drop(n: Int): Observable[T] = { Observable[T](asJava.skip(n)) @@ -1087,7 +1084,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that bypasses all items from the source Observable as long as the specified * condition holds true. Emits all further source items as soon as the condition becomes false. - *

+ * * * * @param predicate @@ -1100,19 +1097,19 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Returns an Observable that emits only the first num items emitted by the source + * Returns an Observable that emits only the first `num` items emitted by the source * Observable. - *

+ * * - *

- * This method returns an Observable that will invoke a subscribing {@link Observer}'s {@link Observer#onNext onNext} function a maximum of num times before invoking - * {@link Observer#onCompleted onCompleted}. + * + * This method returns an Observable that will invoke a subscribing [[Observer]]'s [[Observer.onNext onNext]] function a maximum of `num` times before invoking + * [[Observer.onCompleted onCompleted]]. * * @param num * the number of items to take - * @return an Observable that emits only the first num items from the source + * @return an Observable that emits only the first `num` items from the source * Observable, or all of the items from the source Observable if that Observable emits - * fewer than num items + * fewer than `num` items */ def take(n: Int): Observable[T] = { Observable[T](asJava.take(n)) @@ -1121,14 +1118,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that emits items emitted by the source Observable so long as a * specified condition is true. - *

+ * * * * @param predicate * a function that evaluates an item emitted by the source Observable and returns a * Boolean * @return an Observable that emits the items from the source Observable so long as each item - * satisfies the condition defined by predicate + * satisfies the condition defined by `predicate` */ def takeWhile(predicate: T => Boolean): Observable[T] = { Observable[T](asJava.takeWhile(predicate)) @@ -1138,29 +1135,29 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Returns an Observable that emits the items emitted by a source Observable so long as a given * predicate remains true, where the predicate can operate on both the item and its index * relative to the complete sequence. - *

+ * * * * @param predicate * a function to test each item emitted by the source Observable for a condition; * the second parameter of the function represents the index of the source item * @return an Observable that emits items from the source Observable so long as the predicate - * continues to return true for each item, then completes + * continues to return `true` for each item, then completes */ def takeWhileWithIndex(predicate: (T, Integer) => Boolean): Observable[T] = { Observable[T](asJava.takeWhileWithIndex(predicate)) } /** - * Returns an Observable that emits only the last count items emitted by the source + * Returns an Observable that emits only the last `count` items emitted by the source * Observable. - *

+ * * * * @param count * the number of items to emit from the end of the sequence emitted by the source * Observable - * @return an Observable that emits only the last count items emitted by the source + * @return an Observable that emits only the last `count` items emitted by the source * Observable */ def takeRight(n: Int): Observable[T] = { @@ -1169,17 +1166,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that emits the items from the source Observable only until the - * other Observable emits an item. - *

+ * `other` Observable emits an item. + * * * * @param that - * the Observable whose first emitted item will cause takeUntil to stop + * the Observable whose first emitted item will cause `takeUntil` to stop * emitting items from the source Observable - * @param - * the type of items emitted by other + * @tparam E + * the type of items emitted by `other` * @return an Observable that emits the items of the source Observable until such time as - * other emits its first item + * `other` emits its first item */ def takeUntil[E](that: Observable[E]): Observable[T] = { Observable[T](asJava.takeUntil(that.asJava)) @@ -1188,14 +1185,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that emits a single item, a list composed of all the items emitted by * the source Observable. - *

+ * * - *

- * Normally, an Observable that returns multiple items will do so by invoking its {@link Observer}'s {@link Observer#onNext onNext} method for each such item. You can change + * + * Normally, an Observable that returns multiple items will do so by invoking its [[Observer]]'s [[Observer.onNext onNext]] method for each such item. You can change * this behavior, instructing the Observable to compose a list of all of these items and then to - * invoke the Observer's onNext function once, passing it the entire list, by - * calling the Observable's toList method prior to calling its {@link #subscribe} method. - *

+ * invoke the Observer's `onNext` function once, passing it the entire list, by + * calling the Observable's `toList` method prior to calling its [[Observable.subscribe]] method. + * * Be careful not to use this operator on Observables that emit infinite or very large numbers * of items, as you do not have the option to unsubscribe. * @@ -1212,10 +1209,10 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * @param f * a function that extracts the key from an item - * @param + * @tparam K * the type of keys returned by the discriminator function. - * @return an Observable that emits {@code (key, observable)} pairs, where {@code observable} - * contains all items for which {@code f} returned {@code key}. + * @return an Observable that emits `(key, observable)` pairs, where `observable` + * contains all items for which `f` returned `key`. */ def groupBy[K](f: T => K): Observable[(K, Observable[T])] = { val o1 = asJava.groupBy[K](f) : rx.Observable[_ <: rx.observables.GroupedObservable[K, _ <: T]] @@ -1226,7 +1223,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Given an Observable that emits Observables, creates a single Observable that * emits the items emitted by the most recently published of those Observables. - *

+ * * * * @param sequenceOfSequences @@ -1245,16 +1242,16 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Flattens two Observables into one Observable, without any transformation. - *

+ * * - *

+ * * You can combine items emitted by two Observables so that they act like a single - * Observable by using the {@code merge} method. + * Observable by using the `merge` method. * * @param that * an Observable to be merged - * @return an Observable that emits items from {@code this} and {@code that} until - * {@code this} or {@code that} emits {@code onError} or {@code onComplete}. + * @return an Observable that emits items from `this` and `that` until + * `this` or `that` emits `onError` or `onComplete`. */ def merge[U >: T](that: Observable[U]): Observable[U] = { val thisJava: rx.Observable[_ <: U] = this.asJava @@ -1263,39 +1260,39 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * This behaves like {@link #merge(Observable)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will + * This behaves like [[Observable.merge]] except that if any of the merged Observables + * notify of an error via [[Observer.onError onError]], `mergeDelayError` will * refrain from propagating that error notification until all of the merged Observables have * finished emitting items. - *

+ * * - *

- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its + * + * Even if multiple merged Observables send `onError` notifications, `mergeDelayError` will only invoke the `onError` method of its * Observers once. - *

+ * * This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. * * @param that * an Observable to be merged * @return an Observable that emits items that are the result of flattening the items emitted by - * {@code this} and {@code that} + * `this` and `that` */ def mergeDelayError[U >: T](that: Observable[U]): Observable[U] = { Observable[U](rx.Observable.mergeDelayError[U](this.asJava, that.asJava)) } /** - * Flattens the sequence of Observables emitted by {@code this} into one Observable, without any + * Flattens the sequence of Observables emitted by `this` into one Observable, without any * transformation. - *

+ * * - *

+ * * You can combine the items emitted by multiple Observables so that they act like a single * Observable by using this method. * * @return an Observable that emits items that are the result of flattening the items emitted - * by the Observables emitted by {@code this} + * by the Observables emitted by `this` */ def flatten[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this @@ -1306,16 +1303,16 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * This behaves like {@link #flatten(<:<)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, this method will + * This behaves like [[Observable.flatten]] except that if any of the merged Observables + * notify of an error via [[Observer.onError onError]], this method will * refrain from propagating that error notification until all of the merged Observables have * finished emitting items. - *

+ * * - *

- * Even if multiple merged Observables send {@code onError} notifications, this method will only invoke the {@code onError} method of its + * + * Even if multiple merged Observables send `onError` notifications, this method will only invoke the `onError` method of its * Observers once. - *

+ * * This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. * @@ -1346,24 +1343,24 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. - *

+ * * NOTE: If events keep firing faster than the timeout then no data will be emitted. - *

+ * * - *

+ * * Information on debounce vs throttle: - *

- *

    - *
  • http://drupalmotion.com/article/debounce-and-throttle-visual-explanation
  • - *
  • http://unscriptable.com/2009/03/20/debouncing-javascript-methods/
  • - *
  • http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/
  • + * + * [ul] + * [li]http://drupalmotion.com/article/debounce-and-throttle-visual-explanation + * [li]http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ + * [li]http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ *
* * @param timeout - * The time each value has to be 'the most recent' of the {@link Observable} to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. * - * @return An {@link Observable} which filters out values which are too quickly followed up with newer values. - * @see {@link #debounce} + * @return An [[Observable]] which filters out values which are too quickly followed up with newer values. + * @see [[Observable.debounce]] */ def throttleWithTimeout(timeout: Duration): Observable[T] = { Observable[T](asJava.throttleWithTimeout(timeout.length, timeout.unit)) @@ -1371,24 +1368,24 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. - *

+ * * NOTE: If events keep firing faster than the timeout then no data will be emitted. - *

+ * * - *

+ * * Information on debounce vs throttle: - *

- *

    - *
  • http://drupalmotion.com/article/debounce-and-throttle-visual-explanation
  • - *
  • http://unscriptable.com/2009/03/20/debouncing-javascript-methods/
  • - *
  • http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/
  • + * + * [ul] + * [li]http://drupalmotion.com/article/debounce-and-throttle-visual-explanation + * [li]http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ + * [li]http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ *
* * @param timeout - * The time each value has to be 'the most recent' of the {@link Observable} to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. * - * @return An {@link Observable} which filters out values which are too quickly followed up with newer values. - * @see {@link #throttleWithTimeout}; + * @return An [[Observable]] which filters out values which are too quickly followed up with newer values. + * @see [[Observable.throttleWithTimeout]]; */ def debounce(timeout: Duration): Observable[T] = { Observable[T](asJava.debounce(timeout.length, timeout.unit)) @@ -1396,25 +1393,25 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. - *

+ * * NOTE: If events keep firing faster than the timeout then no data will be emitted. - *

+ * * - *

+ * * Information on debounce vs throttle: - *

- *

    - *
  • http://drupalmotion.com/article/debounce-and-throttle-visual-explanation
  • - *
  • http://unscriptable.com/2009/03/20/debouncing-javascript-methods/
  • - *
  • http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/
  • + * + * [ul] + * [li]http://drupalmotion.com/article/debounce-and-throttle-visual-explanation + * [li]http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ + * [li]http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ *
* * @param timeout - * The time each value has to be 'the most recent' of the {@link Observable} to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. * @param scheduler - * The {@link Scheduler} to use internally to manage the timers which handle timeout for each event. + * The [[Scheduler]] to use internally to manage the timers which handle timeout for each event. * @return Observable which performs the throttle operation. - * @see {@link #throttleWithTimeout}; + * @see [[Observable.throttleWithTimeout]]; */ def debounce(timeout: Duration, scheduler: Scheduler): Observable[T] = { Observable[T](asJava.debounce(timeout.length, timeout.unit, scheduler)) @@ -1422,17 +1419,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. - *

+ * * NOTE: If events keep firing faster than the timeout then no data will be emitted. - *

+ * * * * @param timeout - * The time each value has to be 'the most recent' of the {@link Observable} to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. * @param scheduler - * The {@link Scheduler} to use internally to manage the timers which handle timeout for each event. + * The [[Scheduler]] to use internally to manage the timers which handle timeout for each event. * @return Observable which performs the throttle operation. - * @see {@link #debounce} + * @see [[Observable.debounce]] */ def throttleWithTimeout(timeout: Duration, scheduler: Scheduler): Observable[T] = { Observable[T](asJava.throttleWithTimeout(timeout.length, timeout.unit, scheduler)) @@ -1440,15 +1437,15 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Throttles by skipping value until `skipDuration` passes and then emits the next received value. - *

- * This differs from {@link #throttleLast} in that this only tracks passage of time whereas {@link #throttleLast} ticks at scheduled intervals. - *

+ * + * This differs from [[Observable.throttleLast]] in that this only tracks passage of time whereas [[Observable.throttleLast]] ticks at scheduled intervals. + * * * * @param skipDuration * Time to wait before sending another value after emitting last value. * @param scheduler - * The {@link Scheduler} to use internally to manage the timers which handle timeout for each event. + * The [[Scheduler]] to use internally to manage the timers which handle timeout for each event. * @return Observable which performs the throttle operation. */ def throttleFirst(skipDuration: Duration, scheduler: Scheduler): Observable[T] = { @@ -1457,9 +1454,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Throttles by skipping value until `skipDuration` passes and then emits the next received value. - *

- * This differs from {@link #throttleLast} in that this only tracks passage of time whereas {@link #throttleLast} ticks at scheduled intervals. - *

+ * + * This differs from [[Observable.throttleLast]] in that this only tracks passage of time whereas [[Observable.throttleLast]] ticks at scheduled intervals. + * * * * @param skipDuration @@ -1472,15 +1469,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Throttles by returning the last value of each interval defined by 'intervalDuration'. - *

- * This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas {@link #throttleFirst} does not tick, it just tracks passage of time. - *

+ * + * This differs from [[Observable.throttleFirst]] in that this ticks along at a scheduled interval whereas [[Observable.throttleFirst]] does not tick, it just tracks passage of time. + * * * * @param intervalDuration * Duration of windows within with the last value will be chosen. * @return Observable which performs the throttle operation. - * @see {@link #sample(long, TimeUnit)} */ def throttleLast(intervalDuration: Duration): Observable[T] = { Observable[T](asJava.throttleLast(intervalDuration.length, intervalDuration.unit)) @@ -1488,15 +1484,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Throttles by returning the last value of each interval defined by 'intervalDuration'. - *

- * This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas {@link #throttleFirst} does not tick, it just tracks passage of time. - *

+ * + * This differs from [[Observable.throttleFirst]] in that this ticks along at a scheduled interval whereas [[Observable.throttleFirst]] does not tick, it just tracks passage of time. + * * * * @param intervalDuration * Duration of windows within with the last value will be chosen. * @return Observable which performs the throttle operation. - * @see {@link #sample(long, TimeUnit, Scheduler)} */ def throttleLast(intervalDuration: Duration, scheduler: Scheduler): Observable[T] = { Observable[T](asJava.throttleLast(intervalDuration.length, intervalDuration.unit, scheduler)) @@ -1525,7 +1520,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that emits only the very first item emitted by the source Observable, or * a default value if the source Observable is empty. - *

+ * * * * @param defaultValue @@ -1543,8 +1538,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that emits only the very first item emitted by the source Observable. - * This is just a shorthand for {@code take(1)}. - *

+ * This is just a shorthand for `take(1)`. + * * * * @return an Observable that emits only the very first item from the source, or none if the @@ -1556,7 +1551,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that forwards all sequentially distinct items emitted from the source Observable. - *

+ * * * * @return an Observable of sequentially distinct items @@ -1568,7 +1563,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that forwards all items emitted from the source Observable that are sequentially * distinct according to a key selector function. - *

+ * * * * @param keySelector @@ -1583,7 +1578,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that forwards all items emitted from the source Observable that are sequentially * distinct according to an equality function. - *

+ * * * * @param equality @@ -1597,7 +1592,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that forwards all items emitted from the source Observable that are sequentially * distinct according to a key selector function and a comparator. - *

+ * * * * @param keySelector @@ -1613,7 +1608,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that forwards all distinct items emitted from the source Observable. - *

+ * * * * @return an Observable of distinct items @@ -1625,7 +1620,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that forwards all items emitted from the source Observable that are distinct according * to a comparator. - *

+ * * * * @param equality @@ -1639,7 +1634,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that forwards all items emitted from the source Observable that are distinct according * to a key selector function. - *

+ * * * * @param keySelector @@ -1654,7 +1649,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that forwards all items emitted from the source Observable that are distinct according * to a key selector function and a comparator. - *

+ * * * * @param keySelector @@ -1671,7 +1666,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that counts the total number of elements in the source Observable. - *

+ * * * * @return an Observable emitting the number of counted elements of the source Observable @@ -1683,13 +1678,13 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Retry subscription to origin Observable upto given retry count. - *

+ * * - *

- * If {@link Observer#onError} is invoked the source Observable will be re-subscribed to as many times as defined by retryCount. - *

- * Any {@link Observer#onNext} calls received on each attempt will be emitted and concatenated together. - *

+ * + * If [[Observer.onError]] is invoked the source Observable will be re-subscribed to as many times as defined by retryCount. + * + * Any [[Observer.onNext]] calls received on each attempt will be emitted and concatenated together. + * * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. * @@ -1703,13 +1698,13 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Retry subscription to origin Observable whenever onError is called (infinite retry count). - *

+ * * - *

- * If {@link Observer#onError} is invoked the source Observable will be re-subscribed to. - *

- * Any {@link Observer#onNext} calls received on each attempt will be emitted and concatenated together. - *

+ * + * If [[Observer.onError]] is invoked the source Observable will be re-subscribed to. + * + * Any [[Observer.onNext]] calls received on each attempt will be emitted and concatenated together. + * * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. * @return Observable with retry logic. @@ -1719,7 +1714,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking + * Converts an Observable into a [[BlockingObservable]] (an Observable with blocking * operators). * * @see Blocking Observable Operators @@ -1729,11 +1724,11 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Perform work in parallel by sharding an {@code Observable} on a {@link Schedulers#threadPoolForComputation()} {@link Scheduler} and return an {@code Observable} with the output. + * Perform work in parallel by sharding an `Observable[T]` on a [[Schedulers.threadPoolForComputation()]] [[Scheduler]] and return an `Observable[R]` with the output. * * @param f - * a {@link Func1} that applies Observable operators to {@code Observable} in parallel and returns an {@code Observable} - * @return an Observable with the output of the function executed on a {@link Scheduler} + * a [[Func1]] that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` + * @return an Observable with the output of the function executed on a [[Scheduler]] */ def parallel[R](f: Observable[T] => Observable[R]): Observable[R] = { val fJava: Func1[rx.Observable[T], rx.Observable[R]] = @@ -1742,13 +1737,13 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Perform work in parallel by sharding an {@code Observable} on a {@link Scheduler} and return an {@code Observable} with the output. + * Perform work in parallel by sharding an `Observable[T]` on a [[Scheduler]] and return an `Observable[R]` with the output. * * @param f - * a {@link Func1} that applies Observable operators to {@code Observable} in parallel and returns an {@code Observable} + * a [[Func1]] that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` * @param s - * a {@link Scheduler} to perform the work on. - * @return an Observable with the output of the {@link Func1} executed on a {@link Scheduler} + * a [[Scheduler]] to perform the work on. + * @return an Observable with the output of the [[Func1]] executed on a [[Scheduler]] */ def parallel[R](f: Observable[T] => Observable[R], scheduler: Scheduler): Observable[R] = { val fJava: Func1[rx.Observable[T], rx.Observable[R]] = @@ -1791,27 +1786,28 @@ object Observable { } /** - * Creates an Observable that will execute the given function when an {@link Observer} subscribes to it. - *

+ * Creates an Observable that will execute the given function when an [[Observer]] subscribes to it. + * * - *

- * Write the function you pass to create so that it behaves as an Observable: It - * should invoke the Observer's {@link Observer#onNext onNext}, {@link Observer#onError onError}, and {@link Observer#onCompleted onCompleted} methods + * + * Write the function you pass to `create` so that it behaves as an Observable: It + * should invoke the Observer's [[Observer.onNext onNext]], [[Observer.onError onError]], and [[Observer.onCompleted onCompleted]] methods * appropriately. - *

- * A well-formed Observable must invoke either the Observer's onCompleted method - * exactly once or its onError method exactly once. - *

+ * + * A well-formed Observable must invoke either the Observer's `onCompleted` method + * exactly once or its `onError` method exactly once. + * * See Rx Design Guidelines (PDF) * for detailed information. * - * @param + * + * @tparam T * the type of the items that this Observable emits * @param func - * a function that accepts an {@code Observer}, invokes its {@code onNext}, {@code onError}, and {@code onCompleted} methods - * as appropriate, and returns a {@link Subscription} to allow the Observer to + * a function that accepts an `Observer[T]`, invokes its `onNext`, `onError`, and `onCompleted` methods + * as appropriate, and returns a [[Subscription]] to allow the Observer to * canceling the subscription - * @return an Observable that, when an {@link Observer} subscribes to it, will execute the given + * @return an Observable that, when an [[Observer]] subscribes to it, will execute the given * function */ def apply[T](func: Observer[T] => Subscription): Observable[T] = { @@ -1819,15 +1815,15 @@ object Observable { } /** - * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it - *

+ * Returns an Observable that invokes an [[Observer]]'s [[Observer.onError onError]] method when the Observer subscribes to it + * * * * @param exception * the particular error to report - * @param + * @tparam T * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it + * @return an Observable that invokes the [[Observer]]'s [[Observer.onError onError]] method when the Observer subscribes to it */ def apply[T](exception: Throwable): Observable[T] = { Observable[T](JObservable.error(exception)) @@ -1835,15 +1831,15 @@ object Observable { /** * Converts a sequence of values into an Observable. - *

+ * * * - *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, + * Implementation note: the entire array will be immediately emitted each time an [[Observer]] subscribes. Since this occurs before the [[Subscription]] is returned, * it in not possible to unsubscribe from the sequence before it completes. * * @param items * the source Array - * @param + * @tparam T * the type of items in the Array, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item in the source Array @@ -1861,19 +1857,19 @@ object Observable { * new Observer that subscribes. That is, for each subscriber, the actuall Observable is determined * by the factory function. * - *

+ * * - *

+ * * The defer operator allows you to defer or delay emitting items from an Observable until such - * time as an Observer subscribes to the Observable. This allows an {@link Observer} to easily + * time as an Observer subscribes to the Observable. This allows an [[Observer]] to easily * obtain updates or a refreshed version of the sequence. * * @param observableFactory - * the Observable factory function to invoke for each {@link Observer} that + * the Observable factory function to invoke for each [[Observer]] that * subscribes to the resulting Observable - * @param + * @tparam T * the type of the items emitted by the Observable - * @return an Observable whose {@link Observer}s trigger an invocation of the given Observable + * @return an Observable whose [[Observer]]s trigger an invocation of the given Observable * factory function */ def defer[T](observable: => Observable[T]): Observable[T] = { @@ -1882,20 +1878,20 @@ object Observable { /** * Returns an Observable that emits a single item and then completes. - *

+ * * - *

+ * * To convert any object into an Observable that emits that object, pass that object into the - * just method. - *

- * This is similar to the {@link #apply(Iterable[T])} method, except that - * {@link #apply(Iterable[T])} will convert an {@link Iterable} object into an Observable that emits - * each of the items in the Iterable, one at a time, while the just() method + * `just` method. + * + * This is similar to the [[Observable.apply[T](T*)]] method, except that + * [[Observable.apply[T](T*)]] will convert an `Iterable` object into an Observable that emits + * each of the items in the Iterable, one at a time, while the `just()` method * converts an Iterable into an Observable that emits the entire Iterable as a single item. * * @param value - * the item to pass to the {@link Observer}'s {@link Observer#onNext onNext} method - * @param + * the item to pass to the [[Observer]]'s [[Observer.onNext onNext]] method + * @tparam T * the type of that item * @return an Observable that emits a single item and then completes */ @@ -1904,13 +1900,13 @@ object Observable { } /** - * Returns an Observable that never sends any items or notifications to an {@link Observer}. - *

+ * Returns an Observable that never sends any items or notifications to an [[Observer]]. + * * - *

+ * * This Observable is useful primarily for testing purposes. * - * @return an Observable that never sends any items or notifications to an {@link Observer} + * @return an Observable that never sends any items or notifications to an [[Observer]] */ def never: Observable[Nothing] = { Observable[Nothing](JObservable.never()) From 79ce193e717b76d6b4d99e11554657ad74f2c428 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 27 Sep 2013 18:26:35 +0200 Subject: [PATCH 114/333] start TestScheduler --- .../scala/concurrency/TestScheduler.scala | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala new file mode 100644 index 0000000000..a022d81838 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala @@ -0,0 +1,46 @@ +package rx.lang.scala.concurrency + +import scala.concurrent.duration.Duration +import rx.lang.scala.Subscription +import rx.lang.scala.Scheduler +import rx.lang.scala.ImplicitFunctionConversions._ +import rx.util.functions.Func2 +import java.util.concurrent.TimeUnit + +// TODO make a Scheduler interface in Java, and a DefaultScheduler Java and one for Scala + +class TestScheduler extends Scheduler { + + private val asJava = new rx.concurrency.TestScheduler + + override def now: Long = asJava.now + + def advanceTimeBy(time: Duration) { + asJava.advanceTimeBy(time.length, time.unit) + } + + def advanceTimeTo(time: Duration) { + asJava.advanceTimeTo(time.length, time.unit) + } + + def triggerActions() { + asJava.triggerActions() + } + + def schedule[T](state: T, action: (Scheduler, T) => Subscription): Subscription = { + asJava.schedule(state, action) + } + + def schedule[T](state: T, action: (Scheduler, T) => Subscription, delay: Duration): Subscription = { + asJava.schedule(state, action, delay.length, delay.unit) + } + + override def schedule[T](state: T, action: Func2[_ >: Scheduler, _ >: T, _ <: Subscription]): Subscription = { + asJava.schedule(state, action) + } + + override def schedule[T](state: T, action: Func2[_ >: Scheduler, _ >: T, _ <: Subscription], delayTime: Long, unit: TimeUnit): Subscription = { + asJava.schedule(state, action, delayTime, unit) + } + +} From 7c4d23a175385fa9e1ec08676da64f37deff54a8 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 27 Sep 2013 19:48:47 +0200 Subject: [PATCH 115/333] work on Schedulers rx.lang.scala.concurrency.UnitTest.testInterval fails --- .../scala/ImplicitFunctionConversions.scala | 12 ++ .../scala/concurrency/GenericScheduler.scala | 179 ++++++++++++++++++ .../lang/scala/concurrency/Schedulers.scala | 3 +- .../scala/concurrency/TestScheduler.scala | 31 +-- .../rx/lang/scala/concurrency/package.scala | 8 +- .../main/scala/rx/lang/scala/package.scala | 3 +- 6 files changed, 209 insertions(+), 27 deletions(-) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/GenericScheduler.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index f28ee5121a..9410ce818a 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -17,6 +17,7 @@ package rx.lang.scala import java.{ lang => jlang } import rx.util.functions._ +import rx.lang.scala.concurrency.GenericScheduler /** * These function conversions convert between Scala functions and Rx `Func`s and `Action`s. @@ -27,6 +28,17 @@ import rx.util.functions._ object ImplicitFunctionConversions { import language.implicitConversions + implicit def schedulerActionToFunc2[T](action: (Scheduler, T) => Subscription) = + new Func2[rx.Scheduler, T, Subscription] { + def call(s: rx.Scheduler, t: T): Subscription = { + action(s, t) + } + } + + implicit def scalaSchedulerToJavaScheduler(s: Scheduler): rx.Scheduler = s.asJava + + implicit def javaSchedulerToScalaScheduler[S <: rx.Scheduler](s: S): GenericScheduler[S] = new GenericScheduler(s) + implicit def scalaFunction1ToOnSubscribeFunc[T](f: rx.lang.scala.Observer[T] => Subscription) = new rx.Observable.OnSubscribeFunc[T] { def onSubscribe(obs: rx.Observer[_ >: T]): rx.Subscription = { diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/GenericScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/GenericScheduler.scala new file mode 100644 index 0000000000..c8230b4103 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/GenericScheduler.scala @@ -0,0 +1,179 @@ +package rx.lang.scala.concurrency + +import rx.Subscription +import java.util.Date +import scala.concurrent.duration.Duration +import rx.lang.scala.ImplicitFunctionConversions._ +import rx.util.functions.Func2 +import rx.lang.scala.Scheduler +import rx.lang.scala.Observer +import org.scalatest.junit.JUnitSuite +import org.junit.Before +import rx.lang.scala.Observable + + +class GenericScheduler[+S <: rx.Scheduler](val asJava: S) extends AnyVal { + /** + * Schedules a cancelable action to be executed. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule[T](state: T, action: (Scheduler, T) => Subscription): Subscription = { + asJava.schedule(state, action) + } + + /** + * Schedules a cancelable action to be executed in delayTime. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @param delayTime + * Time the action is to be delayed before executing. + * @param unit + * Time unit of the delay time. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule[T](state: T, action: (Scheduler, T) => Subscription, delayTime: Duration): Subscription = { + asJava.schedule(state, action, delayTime.length, delayTime.unit) + } + + /** + * Schedules a cancelable action to be executed periodically. + * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing + * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. + * + * @param state + * State to pass into the action. + * @param action + * The action to execute periodically. + * @param initialDelay + * Time to wait before executing the action for the first time. + * @param period + * The time interval to wait each time in between executing the action. + * @return A subscription to be able to unsubscribe from action. + */ + def schedulePeriodically[T](state: T, action: (Scheduler, T) => Subscription, initialDelay: Duration, period: Duration): Subscription = { + asJava.schedulePeriodically(state, action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit) + } + + /** + * Schedules a cancelable action to be executed at dueTime. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @param dueTime + * Time the action is to be executed. If in the past it will be executed immediately. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule[T](state: T, action: (Scheduler, T) => Subscription, dueTime: Date): Subscription = { + asJava.schedule(state, action, dueTime) + } + + /** + * Schedules an action to be executed. + * + * @param action + * action + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(action: () => Unit): Subscription = { + asJava.schedule(action) + } + + /** + * Schedules an action to be executed in delayTime. + * + * @param action + * action + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(action: () => Unit, delayTime: Duration): Subscription = { + asJava.schedule(action, delayTime.length, delayTime.unit) + } + + /** + * Schedules an action to be executed periodically. + * + * @param action + * The action to execute periodically. + * @param initialDelay + * Time to wait before executing the action for the first time. + * @param period + * The time interval to wait each time in between executing the action. + * @return A subscription to be able to unsubscribe from action. + */ + def schedulePeriodically(action: () => Unit, initialDelay: Duration, period: Duration): Subscription = { + asJava.schedulePeriodically(action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit) + } + + /** + * @return the scheduler's notion of current absolute time in milliseconds. + */ + def now: Long = { + asJava.now + } + + /** + * Parallelism available to a Scheduler. + * + * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. + * + * @return the scheduler's available degree of parallelism. + */ + def degreeOfParallelism: Int = { + asJava.degreeOfParallelism + } + +} + +class UnitTest extends JUnitSuite { + import org.mockito.Matchers._ + import org.mockito.Mockito._ + import org.junit.Test + import org.junit.Before + import scala.concurrent.duration._ + import scala.language.postfixOps + + var scheduler: TestScheduler = null + var observer: Observer[Long] = null + var observer2: Observer[Long] = null + + @Before def before() { + scheduler = TestScheduler() + observer = mock(classOf[rx.Observer[Long]]) + observer2 = mock(classOf[rx.Observer[Long]]) + } + + @Test def testInterval() { + val w = Observable.interval(1 seconds) + val sub = w.subscribe(observer) + + verify(observer, never()).onNext(0L) + verify(observer, never()).onCompleted() + verify(observer, never()).onError(any(classOf[Throwable])) + + scheduler.advanceTimeTo(2 seconds) + + val inOrdr = inOrder(observer); + inOrdr.verify(observer, times(1)).onNext(0L) + inOrdr.verify(observer, times(1)).onNext(1L) + inOrdr.verify(observer, never()).onNext(2L) + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(classOf[Throwable])) + + sub.unsubscribe(); + scheduler.advanceTimeTo(4 seconds) + verify(observer, never()).onNext(2L) + verify(observer, times(1)).onCompleted() + verify(observer, never()).onError(any(classOf[Throwable])) + } +} + diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala index 33a77ed132..8ba88ba2d0 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala @@ -1,8 +1,9 @@ package rx.lang.scala.concurrency -import rx.Scheduler import java.util.concurrent.Executor import java.util.concurrent.ScheduledExecutorService +import rx.lang.scala.Scheduler +import rx.lang.scala.ImplicitFunctionConversions._ /** * Factory methods for creating Schedulers. diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala index a022d81838..41c5ff70ae 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala @@ -1,19 +1,14 @@ package rx.lang.scala.concurrency import scala.concurrent.duration.Duration -import rx.lang.scala.Subscription -import rx.lang.scala.Scheduler -import rx.lang.scala.ImplicitFunctionConversions._ -import rx.util.functions.Func2 -import java.util.concurrent.TimeUnit -// TODO make a Scheduler interface in Java, and a DefaultScheduler Java and one for Scala -class TestScheduler extends Scheduler { + +class TestScheduler { private val asJava = new rx.concurrency.TestScheduler - override def now: Long = asJava.now + /*override*/ def now: Long = asJava.now def advanceTimeBy(time: Duration) { asJava.advanceTimeBy(time.length, time.unit) @@ -26,21 +21,13 @@ class TestScheduler extends Scheduler { def triggerActions() { asJava.triggerActions() } +} - def schedule[T](state: T, action: (Scheduler, T) => Subscription): Subscription = { - asJava.schedule(state, action) - } - - def schedule[T](state: T, action: (Scheduler, T) => Subscription, delay: Duration): Subscription = { - asJava.schedule(state, action, delay.length, delay.unit) - } - - override def schedule[T](state: T, action: Func2[_ >: Scheduler, _ >: T, _ <: Subscription]): Subscription = { - asJava.schedule(state, action) - } - override def schedule[T](state: T, action: Func2[_ >: Scheduler, _ >: T, _ <: Subscription], delayTime: Long, unit: TimeUnit): Subscription = { - asJava.schedule(state, action, delayTime, unit) +object TestScheduler { + def apply(): TestScheduler = { + //rx.lang.scala.ImplicitFunctionConversions.javaSchedulerToScalaScheduler(new rx.concurrency.TestScheduler()) + new TestScheduler } - } + diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala index 507660c23f..c32ef31f1a 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala @@ -21,11 +21,13 @@ import rx.concurrency.CurrentThreadScheduler * Provides schedulers. */ package object concurrency { - // These classes are not exposed to Scala users, but are accessible through - // rx.lang.scala.concurrency.Schedulers: + + //type TestScheduler = GenericScheduler[rx.concurrency.TestScheduler] + + // These classes are not exposed to Scala users, but are accessible through rx.lang.scala.concurrency.Schedulers: // rx.concurrency.CurrentThreadScheduler // rx.concurrency.ExecutorScheduler // rx.concurrency.ImmediateScheduler // rx.concurrency.NewThreadScheduler -} \ No newline at end of file +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala index 220f307961..3dec7a43c9 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala @@ -17,6 +17,7 @@ package rx.lang import java.util.concurrent.TimeUnit import java.util.Date +import rx.lang.scala.concurrency.GenericScheduler /* * Note that: @@ -227,7 +228,7 @@ package object scala { type Observer[-T] = rx.Observer[_ >: T] - type Scheduler = rx.Scheduler + type Scheduler = GenericScheduler[rx.Scheduler] type Subscription = rx.Subscription From dcbae87e21e016c91d899e6baf9a2743bc0a2a01 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 27 Sep 2013 22:50:22 +0200 Subject: [PATCH 116/333] Scheduler and TestScheduler --- .../scala/ImplicitFunctionConversions.scala | 3 +- .../main/scala/rx/lang/scala/Scheduler.scala | 161 ++++++++++++++++ .../scala/concurrency/GenericScheduler.scala | 179 ------------------ .../scala/concurrency/TestScheduler.scala | 61 +++++- .../rx/lang/scala/concurrency/package.scala | 4 +- .../main/scala/rx/lang/scala/package.scala | 119 ------------ 6 files changed, 215 insertions(+), 312 deletions(-) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/GenericScheduler.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index 9410ce818a..7bdcdf6f36 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -17,7 +17,6 @@ package rx.lang.scala import java.{ lang => jlang } import rx.util.functions._ -import rx.lang.scala.concurrency.GenericScheduler /** * These function conversions convert between Scala functions and Rx `Func`s and `Action`s. @@ -37,7 +36,7 @@ object ImplicitFunctionConversions { implicit def scalaSchedulerToJavaScheduler(s: Scheduler): rx.Scheduler = s.asJava - implicit def javaSchedulerToScalaScheduler[S <: rx.Scheduler](s: S): GenericScheduler[S] = new GenericScheduler(s) + implicit def javaSchedulerToScalaScheduler(s: rx.Scheduler): Scheduler = Scheduler(s) implicit def scalaFunction1ToOnSubscribeFunc[T](f: rx.lang.scala.Observer[T] => Subscription) = new rx.Observable.OnSubscribeFunc[T] { diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala new file mode 100644 index 0000000000..12f452db59 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala @@ -0,0 +1,161 @@ +package rx.lang.scala + +import java.util.Date + +import scala.concurrent.duration.Duration +import scala.concurrent.duration.DurationInt +import scala.language.postfixOps + +import org.junit.Before +import org.junit.Test +import org.mockito.Matchers.any +import org.mockito.Mockito.inOrder +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.scalatest.junit.JUnitSuite + +import rx.lang.scala.ImplicitFunctionConversions.scalaFunction0ProducingUnitToAction0 +import rx.lang.scala.ImplicitFunctionConversions.schedulerActionToFunc2 +import rx.lang.scala.concurrency.TestScheduler + + +/** + * Represents an object that schedules units of work. + */ +trait Scheduler { + def asJava: rx.Scheduler + + /** + * Schedules a cancelable action to be executed. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule[T](state: T, action: (Scheduler, T) => Subscription): Subscription = { + asJava.schedule(state, action) + } + + /** + * Schedules a cancelable action to be executed in delayTime. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @param delayTime + * Time the action is to be delayed before executing. + * @param unit + * Time unit of the delay time. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule[T](state: T, action: (Scheduler, T) => Subscription, delayTime: Duration): Subscription = { + asJava.schedule(state, action, delayTime.length, delayTime.unit) + } + + /** + * Schedules a cancelable action to be executed periodically. + * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing + * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. + * + * @param state + * State to pass into the action. + * @param action + * The action to execute periodically. + * @param initialDelay + * Time to wait before executing the action for the first time. + * @param period + * The time interval to wait each time in between executing the action. + * @return A subscription to be able to unsubscribe from action. + */ + def schedulePeriodically[T](state: T, action: (Scheduler, T) => Subscription, initialDelay: Duration, period: Duration): Subscription = { + asJava.schedulePeriodically(state, action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit) + } + + /** + * Schedules a cancelable action to be executed at dueTime. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @param dueTime + * Time the action is to be executed. If in the past it will be executed immediately. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule[T](state: T, action: (Scheduler, T) => Subscription, dueTime: Date): Subscription = { + asJava.schedule(state, action, dueTime) + } + + /** + * Schedules an action to be executed. + * + * @param action + * action + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(action: () => Unit): Subscription = { + asJava.schedule(action) + } + + /** + * Schedules an action to be executed in delayTime. + * + * @param action + * action + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(action: () => Unit, delayTime: Duration): Subscription = { + asJava.schedule(action, delayTime.length, delayTime.unit) + } + + /** + * Schedules an action to be executed periodically. + * + * @param action + * The action to execute periodically. + * @param initialDelay + * Time to wait before executing the action for the first time. + * @param period + * The time interval to wait each time in between executing the action. + * @return A subscription to be able to unsubscribe from action. + */ + def schedulePeriodically(action: () => Unit, initialDelay: Duration, period: Duration): Subscription = { + asJava.schedulePeriodically(action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit) + } + + /** + * @return the scheduler's notion of current absolute time in milliseconds. + */ + def now: Long = { + asJava.now + } + + /** + * Parallelism available to a Scheduler. + * + * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. + * + * @return the scheduler's available degree of parallelism. + */ + def degreeOfParallelism: Int = { + asJava.degreeOfParallelism + } + +} + +/** + * Provides constructors for Schedulers. + */ +object Scheduler { + private class WrapJavaScheduler(val asJava: rx.Scheduler) extends Scheduler + + /** + * Constructs a Scala Scheduler from a Java Scheduler. + */ + def apply(s: rx.Scheduler): Scheduler = new WrapJavaScheduler(s) +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/GenericScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/GenericScheduler.scala deleted file mode 100644 index c8230b4103..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/GenericScheduler.scala +++ /dev/null @@ -1,179 +0,0 @@ -package rx.lang.scala.concurrency - -import rx.Subscription -import java.util.Date -import scala.concurrent.duration.Duration -import rx.lang.scala.ImplicitFunctionConversions._ -import rx.util.functions.Func2 -import rx.lang.scala.Scheduler -import rx.lang.scala.Observer -import org.scalatest.junit.JUnitSuite -import org.junit.Before -import rx.lang.scala.Observable - - -class GenericScheduler[+S <: rx.Scheduler](val asJava: S) extends AnyVal { - /** - * Schedules a cancelable action to be executed. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription): Subscription = { - asJava.schedule(state, action) - } - - /** - * Schedules a cancelable action to be executed in delayTime. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @param delayTime - * Time the action is to be delayed before executing. - * @param unit - * Time unit of the delay time. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription, delayTime: Duration): Subscription = { - asJava.schedule(state, action, delayTime.length, delayTime.unit) - } - - /** - * Schedules a cancelable action to be executed periodically. - * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing - * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. - * - * @param state - * State to pass into the action. - * @param action - * The action to execute periodically. - * @param initialDelay - * Time to wait before executing the action for the first time. - * @param period - * The time interval to wait each time in between executing the action. - * @return A subscription to be able to unsubscribe from action. - */ - def schedulePeriodically[T](state: T, action: (Scheduler, T) => Subscription, initialDelay: Duration, period: Duration): Subscription = { - asJava.schedulePeriodically(state, action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit) - } - - /** - * Schedules a cancelable action to be executed at dueTime. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @param dueTime - * Time the action is to be executed. If in the past it will be executed immediately. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription, dueTime: Date): Subscription = { - asJava.schedule(state, action, dueTime) - } - - /** - * Schedules an action to be executed. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - def schedule(action: () => Unit): Subscription = { - asJava.schedule(action) - } - - /** - * Schedules an action to be executed in delayTime. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - def schedule(action: () => Unit, delayTime: Duration): Subscription = { - asJava.schedule(action, delayTime.length, delayTime.unit) - } - - /** - * Schedules an action to be executed periodically. - * - * @param action - * The action to execute periodically. - * @param initialDelay - * Time to wait before executing the action for the first time. - * @param period - * The time interval to wait each time in between executing the action. - * @return A subscription to be able to unsubscribe from action. - */ - def schedulePeriodically(action: () => Unit, initialDelay: Duration, period: Duration): Subscription = { - asJava.schedulePeriodically(action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit) - } - - /** - * @return the scheduler's notion of current absolute time in milliseconds. - */ - def now: Long = { - asJava.now - } - - /** - * Parallelism available to a Scheduler. - * - * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. - * - * @return the scheduler's available degree of parallelism. - */ - def degreeOfParallelism: Int = { - asJava.degreeOfParallelism - } - -} - -class UnitTest extends JUnitSuite { - import org.mockito.Matchers._ - import org.mockito.Mockito._ - import org.junit.Test - import org.junit.Before - import scala.concurrent.duration._ - import scala.language.postfixOps - - var scheduler: TestScheduler = null - var observer: Observer[Long] = null - var observer2: Observer[Long] = null - - @Before def before() { - scheduler = TestScheduler() - observer = mock(classOf[rx.Observer[Long]]) - observer2 = mock(classOf[rx.Observer[Long]]) - } - - @Test def testInterval() { - val w = Observable.interval(1 seconds) - val sub = w.subscribe(observer) - - verify(observer, never()).onNext(0L) - verify(observer, never()).onCompleted() - verify(observer, never()).onError(any(classOf[Throwable])) - - scheduler.advanceTimeTo(2 seconds) - - val inOrdr = inOrder(observer); - inOrdr.verify(observer, times(1)).onNext(0L) - inOrdr.verify(observer, times(1)).onNext(1L) - inOrdr.verify(observer, never()).onNext(2L) - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(classOf[Throwable])) - - sub.unsubscribe(); - scheduler.advanceTimeTo(4 seconds) - verify(observer, never()).onNext(2L) - verify(observer, times(1)).onCompleted() - verify(observer, never()).onError(any(classOf[Throwable])) - } -} - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala index 41c5ff70ae..0165a2d285 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala @@ -1,14 +1,14 @@ package rx.lang.scala.concurrency import scala.concurrent.duration.Duration +import rx.lang.scala.Scheduler +import org.scalatest.junit.JUnitSuite - - -class TestScheduler { - - private val asJava = new rx.concurrency.TestScheduler - - /*override*/ def now: Long = asJava.now +/** + * Scheduler with artificial time, useful for testing. + */ +class TestScheduler extends Scheduler { + val asJava = new rx.concurrency.TestScheduler def advanceTimeBy(time: Duration) { asJava.advanceTimeBy(time.length, time.unit) @@ -23,11 +23,54 @@ class TestScheduler { } } - +/** + * Provides constructors for `TestScheduler`. + */ object TestScheduler { def apply(): TestScheduler = { - //rx.lang.scala.ImplicitFunctionConversions.javaSchedulerToScalaScheduler(new rx.concurrency.TestScheduler()) new TestScheduler } } +private class UnitTest extends JUnitSuite { + import org.mockito.Matchers._ + import org.mockito.Mockito._ + import org.junit.{Test, Before} + import scala.concurrent.duration._ + import scala.language.postfixOps + import rx.lang.scala.{Observable, Observer} + + var scheduler: TestScheduler = null + var observer: Observer[Long] = null + var observer2: Observer[Long] = null + + @Before def before() { + scheduler = TestScheduler() + observer = mock(classOf[rx.Observer[Long]]) + } + + @Test def testInterval() { + val w = Observable.interval(1 second, scheduler) + val sub = w.subscribe(observer) + + verify(observer, never()).onNext(0L) + verify(observer, never()).onCompleted() + verify(observer, never()).onError(any(classOf[Throwable])) + + scheduler.advanceTimeTo(2 seconds) + + val inOrdr = inOrder(observer); + inOrdr.verify(observer, times(1)).onNext(0L) + inOrdr.verify(observer, times(1)).onNext(1L) + inOrdr.verify(observer, never()).onNext(2L) + verify(observer, never()).onCompleted() + verify(observer, never()).onError(any(classOf[Throwable])) + + sub.unsubscribe(); + scheduler.advanceTimeTo(4 seconds) + verify(observer, never()).onNext(2L) + verify(observer, times(1)).onCompleted() + verify(observer, never()).onError(any(classOf[Throwable])) + } +} + diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala index c32ef31f1a..a3e61c0021 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala @@ -21,9 +21,7 @@ import rx.concurrency.CurrentThreadScheduler * Provides schedulers. */ package object concurrency { - - //type TestScheduler = GenericScheduler[rx.concurrency.TestScheduler] - + // These classes are not exposed to Scala users, but are accessible through rx.lang.scala.concurrency.Schedulers: // rx.concurrency.CurrentThreadScheduler diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala index 3dec7a43c9..63e686250f 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala @@ -17,7 +17,6 @@ package rx.lang import java.util.concurrent.TimeUnit import java.util.Date -import rx.lang.scala.concurrency.GenericScheduler /* * Note that: @@ -81,119 +80,6 @@ package object scala { def onNext(arg: T): Unit } - - /** - * Represents an object that schedules units of work. - */ - abstract class Scheduler { - - /** - * Schedules a cancelable action to be executed. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription): Subscription - - /** - * Schedules a cancelable action to be executed in delayTime. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @param delayTime - * Time the action is to be delayed before executing. - * @param unit - * Time unit of the delay time. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription, delayTime: Long, unit: TimeUnit): Subscription - - /** - * Schedules a cancelable action to be executed periodically. - * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing - * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. - * - * @param state - * State to pass into the action. - * @param action - * The action to execute periodically. - * @param initialDelay - * Time to wait before executing the action for the first time. - * @param period - * The time interval to wait each time in between executing the action. - * @param unit - * The time unit the interval above is given in. - * @return A subscription to be able to unsubscribe from action. - */ - def schedulePeriodically[T](state: T, action: (Scheduler, T) => Subscription, initialDelay: Long, period: Long, unit: TimeUnit): Subscription - - /** - * Schedules a cancelable action to be executed at dueTime. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @param dueTime - * Time the action is to be executed. If in the past it will be executed immediately. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription, dueTime: Date): Subscription - - /** - * Schedules an action to be executed. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - def schedule(action: () => Unit): Subscription - - /** - * Schedules an action to be executed in delayTime. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - def schedule(action: () => Unit, delayTime: Long, unit: TimeUnit): Subscription - - /** - * Schedules an action to be executed periodically. - * - * @param action - * The action to execute periodically. - * @param initialDelay - * Time to wait before executing the action for the first time. - * @param period - * The time interval to wait each time in between executing the action. - * @param unit - * The time unit the interval above is given in. - * @return A subscription to be able to unsubscribe from action. - */ - def schedulePeriodically(action: () => Unit, initialDelay: Long, period: Long, unit: TimeUnit): Subscription - - /** - * @return the scheduler's notion of current absolute time in milliseconds. - */ - def now(): Long - - /** - * Parallelism available to a Scheduler. - *

- * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. - * - * @return the scheduler's available degree of parallelism. - */ - def degreeOfParallelism: Int - - } - /** * Subscriptions are returned from all Observable.subscribe methods to allow unsubscribing. * @@ -221,15 +107,10 @@ package object scala { private[scala] implicit def fakeObserver2RxObserver[T](o: Observer[T]): rx.Observer[_ >: T] = ??? private[scala] implicit def rxObserver2fakeObserver[T](o: rx.Observer[_ >: T]): Observer[T] = ??? - private[scala] implicit def fakeScheduler2RxScheduler(s: Scheduler): rx.Scheduler = ??? - private[scala] implicit def rxScheduler2fakeScheduler(s: rx.Scheduler): Scheduler = ??? - *///#else type Observer[-T] = rx.Observer[_ >: T] - type Scheduler = GenericScheduler[rx.Scheduler] - type Subscription = rx.Subscription //#endif From eba034e2f98ff160103dda26247509bb9cc95026 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Sun, 29 Sep 2013 10:24:56 +0800 Subject: [PATCH 117/333] Fixed the issue of takeLast(items, 0) --- .../java/rx/operators/OperationTakeLast.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java b/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java index 46ff21e9e2..3ae2e7fc41 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java @@ -26,6 +26,7 @@ import rx.Observable; import rx.Observable.OnSubscribeFunc; +import rx.subscriptions.Subscriptions; import rx.Observer; import rx.Subscription; @@ -59,6 +60,26 @@ private static class TakeLast implements OnSubscribeFunc { } public Subscription onSubscribe(Observer observer) { + if(count == 0) { + items.subscribe(new Observer() { + + @Override + public void onCompleted() { + } + + @Override + public void onError(Throwable e) { + } + + @Override + public void onNext(T args) { + } + + }).unsubscribe(); + observer.onCompleted(); + return Subscriptions.empty(); + } + return subscription.wrap(items.subscribe(new ItemObserver(observer))); } @@ -140,6 +161,19 @@ public void testTakeLast2() { verify(aObserver, times(1)).onCompleted(); } + @Test + public void testTakeLastWithZeroCount() { + Observable w = Observable.from("one"); + Observable take = Observable.create(takeLast(w, 0)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + } } From 4323b704fee1c4339a5e77b2f9724f4c074d7086 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Sun, 29 Sep 2013 15:20:58 +0800 Subject: [PATCH 118/333] Implemented the 'SkipLast' operator --- rxjava-core/src/main/java/rx/Observable.java | 27 +++ .../java/rx/operators/OperationSkipLast.java | 197 ++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationSkipLast.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 5a0f668dc5..bafedc29d8 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -63,6 +63,7 @@ import rx.operators.OperationSample; import rx.operators.OperationScan; import rx.operators.OperationSkip; +import rx.operators.OperationSkipLast; import rx.operators.OperationSkipWhile; import rx.operators.OperationSubscribeOn; import rx.operators.OperationSum; @@ -4054,6 +4055,32 @@ public Observable skipWhile(Func1 predicate) { return create(OperationSkipWhile.skipWhile(this, predicate)); } + /** + * Bypasses a specified number of elements at the end of an observable + * sequence. + *

+ * This operator accumulates a queue with a length enough to store the first + * count elements. As more elements are received, elements are taken from + * the front of the queue and produced on the result sequence. This causes + * elements to be delayed. + * + * @param source + * the source sequence. + * @param count + * number of elements to bypass at the end of the source + * sequence. + * @return An observable sequence containing the source sequence elements + * except for the bypassed ones at the end. + * + * @throws IndexOutOfBoundsException + * count is less than zero. + * + * @see MSDN: Observable.SkipLast + */ + public Observable skipLast(int count) { + return create(OperationSkipLast.skipLast(this, count)); + } + /** * Returns an Observable that emits a single item, a list composed of all the items emitted by * the source Observable. diff --git a/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java b/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java new file mode 100644 index 0000000000..73e0014b22 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java @@ -0,0 +1,197 @@ +package rx.operators; + +import static org.mockito.Matchers.any; +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.Deque; +import java.util.LinkedList; +import java.util.concurrent.locks.ReentrantLock; + +import org.junit.Test; +import org.mockito.InOrder; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; + +/** + * Bypasses a specified number of elements at the end of an observable sequence. + */ +public class OperationSkipLast { + + /** + * Bypasses a specified number of elements at the end of an observable + * sequence. + *

+ * This operator accumulates a queue with a length enough to store the first + * count elements. As more elements are received, elements are taken from + * the front of the queue and produced on the result sequence. This causes + * elements to be delayed. + * + * @param source + * the source sequence. + * @param count + * number of elements to bypass at the end of the source + * sequence. + * @return An observable sequence containing the source sequence elements + * except for the bypassed ones at the end. + * + * @throws IndexOutOfBoundsException + * count is less than zero. + */ + public static OnSubscribeFunc skipLast( + Observable source, int count) { + return new SkipLast(source, count); + } + + private static class SkipLast implements OnSubscribeFunc { + private final int count; + private final Observable source; + + private SkipLast(Observable source, int count) { + this.count = count; + this.source = source; + } + + public Subscription onSubscribe(final Observer observer) { + if (count < 0) { + throw new IndexOutOfBoundsException( + "count could not be negative"); + } + final SafeObservableSubscription subscription = new SafeObservableSubscription(); + return subscription.wrap(source.subscribe(new Observer() { + + private final ReentrantLock lock = new ReentrantLock(); + + /** + * Store the last count elements until now. + */ + private final Deque deque = new LinkedList(); + + @Override + public void onCompleted() { + observer.onCompleted(); + } + + @Override + public void onError(Throwable e) { + observer.onError(e); + } + + @Override + public void onNext(T value) { + lock.lock(); + try { + deque.offerLast(value); + if (deque.size() > count) { + // Now deque has count + 1 elements, so the first + // element in the deque definitely does not belong + // to the last count elements of the source + // sequence. We can emit it now. + observer.onNext(deque.removeFirst()); + } + } catch (Throwable ex) { + observer.onError(ex); + subscription.unsubscribe(); + } finally { + lock.unlock(); + } + } + + })); + } + } + + public static class UnitTest { + + @Test + public void testSkipLastEmpty() { + Observable w = Observable.empty(); + Observable observable = Observable.create(skipLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLast1() { + Observable w = Observable.from("one", "two", "three"); + Observable observable = Observable.create(skipLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + InOrder inOrder = inOrder(aObserver); + observable.subscribe(aObserver); + inOrder.verify(aObserver, never()).onNext("two"); + inOrder.verify(aObserver, never()).onNext("three"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLast2() { + Observable w = Observable.from("one", "two"); + Observable observable = Observable.create(skipLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLastWithZeroCount() { + Observable w = Observable.from("one", "two"); + Observable observable = Observable.create(skipLast(w, 0)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLastWithNull() { + Observable w = Observable.from("one", null, "two"); + Observable observable = Observable.create(skipLast(w, 1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext(null); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLastWithNegativeCount() { + Observable w = Observable.from("one"); + Observable observable = Observable.create(skipLast(w, -1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, times(1)).onError( + any(IndexOutOfBoundsException.class)); + verify(aObserver, never()).onCompleted(); + } + } +} From 9abfb5a1d6abee7c3f54416bac595ff1d22adcb5 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Sun, 29 Sep 2013 15:23:16 +0800 Subject: [PATCH 119/333] Updated the comments --- rxjava-core/src/main/java/rx/Observable.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index bafedc29d8..c19ec3c824 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4064,8 +4064,6 @@ public Observable skipWhile(Func1 predicate) { * the front of the queue and produced on the result sequence. This causes * elements to be delayed. * - * @param source - * the source sequence. * @param count * number of elements to bypass at the end of the source * sequence. From f7ae906f19c6b1c608feb7fe3b0d40b6100fcf6d Mon Sep 17 00:00:00 2001 From: zsxwing Date: Sun, 29 Sep 2013 16:26:56 +0800 Subject: [PATCH 120/333] Fixed the issue about null values --- .../java/rx/operators/OperationTakeLast.java | 106 ++++++++++++------ 1 file changed, 74 insertions(+), 32 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java b/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java index 3ae2e7fc41..e83f5dc432 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java @@ -15,18 +15,22 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; +import static org.mockito.Matchers.any; +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.Iterator; -import java.util.concurrent.LinkedBlockingDeque; +import java.util.Deque; +import java.util.LinkedList; +import java.util.concurrent.locks.ReentrantLock; import org.junit.Test; import org.mockito.InOrder; import rx.Observable; import rx.Observable.OnSubscribeFunc; -import rx.subscriptions.Subscriptions; import rx.Observer; import rx.Subscription; @@ -60,33 +64,21 @@ private static class TakeLast implements OnSubscribeFunc { } public Subscription onSubscribe(Observer observer) { - if(count == 0) { - items.subscribe(new Observer() { - - @Override - public void onCompleted() { - } - - @Override - public void onError(Throwable e) { - } - - @Override - public void onNext(T args) { - } - - }).unsubscribe(); - observer.onCompleted(); - return Subscriptions.empty(); + if (count < 0) { + throw new IndexOutOfBoundsException( + "count could not be negative"); } - return subscription.wrap(items.subscribe(new ItemObserver(observer))); } private class ItemObserver implements Observer { - private LinkedBlockingDeque deque = new LinkedBlockingDeque(count); + /** + * Store the last count elements until now. + */ + private Deque deque = new LinkedList(); private final Observer observer; + private final ReentrantLock lock = new ReentrantLock(); public ItemObserver(Observer observer) { this.observer = observer; @@ -94,11 +86,14 @@ public ItemObserver(Observer observer) { @Override public void onCompleted() { - Iterator reverse = deque.descendingIterator(); - while (reverse.hasNext()) { - observer.onNext(reverse.next()); + try { + for (T value : deque) { + observer.onNext(value); + } + observer.onCompleted(); + } catch (Throwable e) { + observer.onError(e); } - observer.onCompleted(); } @Override @@ -107,9 +102,27 @@ public void onError(Throwable e) { } @Override - public void onNext(T args) { - while (!deque.offerFirst(args)) { - deque.removeLast(); + public void onNext(T value) { + if (count == 0) { + // If count == 0, we do not need to put value into deque and + // remove it at once. We can ignore the value directly. + return; + } + lock.lock(); + try { + deque.offerLast(value); + if (deque.size() > count) { + // Now deque has count + 1 elements, so the first + // element in the deque definitely does not belong + // to the last count elements of the source + // sequence. We can drop it now. + deque.removeFirst(); + } + } catch (Throwable e) { + observer.onError(e); + subscription.unsubscribe(); + } finally { + lock.unlock(); } } @@ -174,6 +187,35 @@ public void testTakeLastWithZeroCount() { verify(aObserver, times(1)).onCompleted(); } + @Test + public void testTakeLastWithNull() { + Observable w = Observable.from("one", null, "three"); + Observable take = Observable.create(takeLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, times(1)).onNext(null); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeLastWithNegativeCount() { + Observable w = Observable.from("one"); + Observable take = Observable.create(takeLast(w, -1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, times(1)).onError( + any(IndexOutOfBoundsException.class)); + verify(aObserver, never()).onCompleted(); + } + } } From b46b5b35ff36064d77e80a66850677e357d183d0 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Sun, 29 Sep 2013 16:40:26 +0800 Subject: [PATCH 121/333] Optimize for 'count == 0' --- .../main/java/rx/operators/OperationSkipLast.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java b/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java index 73e0014b22..f8518b21df 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java @@ -85,6 +85,18 @@ public void onError(Throwable e) { @Override public void onNext(T value) { + if (count == 0) { + // If count == 0, we do not need to put value into deque + // and remove it at once. We can emit the value + // directly. + try { + observer.onNext(value); + } catch (Throwable ex) { + observer.onError(ex); + subscription.unsubscribe(); + } + return; + } lock.lock(); try { deque.offerLast(value); From aae04c175938b7d5587ad279e265bc2f9120cfd8 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Sun, 29 Sep 2013 20:40:06 +0200 Subject: [PATCH 122/333] add exists and isEmpty --- .../rx/lang/scala/examples/RxScalaDemo.scala | 9 ++++++++ .../main/scala/rx/lang/scala/Observable.scala | 21 ++++++++++++++++++- .../rx/lang/scala/CompletenessTest.scala | 4 +++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index b383681531..c7856b4ea4 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -411,6 +411,15 @@ class RxScalaDemo extends JUnitSuite { }) } + @Test def elementAtReplacement() { + assertEquals("b", Observable("a", "b", "c").drop(1).first.toBlockingObservable.single) + } + + @Test def elementAtOrDefaultReplacement() { + assertEquals("b", Observable("a", "b", "c").drop(1).firstOrElse("!").toBlockingObservable.single) + assertEquals("!!", Observable("a", "b", "c").drop(10).firstOrElse("!!").toBlockingObservable.single) + } + def output(s: String): Unit = println(s) // blocks until obs has completed diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 310fb78f76..0937c4e6a6 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1749,7 +1749,26 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) val fJava: Func1[rx.Observable[T], rx.Observable[R]] = (jo: rx.Observable[T]) => f(Observable[T](jo)).asJava.asInstanceOf[rx.Observable[R]] Observable[R](asJava.asInstanceOf[rx.Observable[T]].parallel[R](fJava, scheduler)) - } + } + + /** Tests whether a predicate holds for some of the elements of this `Observable`. + * + * @param p the predicate used to test elements. + * @return an Observable emitting one single Boolean, which is `true` if the given predicate `p` + * holds for some of the elements of this Observable, and `false` otherwise. + */ + def exists(p: T => Boolean): Observable[Boolean] = { + Observable[java.lang.Boolean](asJava.exists(p)).map(_.booleanValue()) + } + + /** Tests whether this `Observable` emits no elements. + * + * @return an Observable emitting one single Boolean, which is `true` if this `Observable` + * emits no elements, and `false` otherwise. + */ + def isEmpty: Observable[Boolean] = { + Observable[java.lang.Boolean](asJava.isEmpty).map(_.booleanValue()) + } def withFilter(p: T => Boolean): WithFilter[T] = { new WithFilter[T](p, asJava) diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index abf0439b75..cf22630a3a 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -31,7 +31,9 @@ class CompletenessTest extends JUnitSuite { "buffer(Long, Long, TimeUnit)" -> "buffer(Duration, Duration)", "buffer(Long, Long, TimeUnit, Scheduler)" -> "buffer(Duration, Duration, Scheduler)", "count()" -> "length", - "dematerialize()" -> "dematerialize(<:<[T, Notification[U]])", + "dematerialize()" -> "dematerialize(<:<[Observable[T], Observable[Notification[U]]])", + "elementAt(Int)" -> "[use `.drop(index).first`]", + "elementAtOrDefault(Int, T)" -> "[use `.drop(index).firstOrElse(default)`]", "first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, "firstOrDefault(T)" -> "firstOrElse(=> U)", "firstOrDefault(Func1[_ >: T, Boolean], T)" -> "[use `.filter(condition).firstOrElse(default)`]", From 70c14a3424512c67db201f8ccca30d6a93307f8f Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Sun, 29 Sep 2013 20:49:46 +0200 Subject: [PATCH 123/333] TestScheduler example --- .../scala/concurrency/TestScheduler.scala | 73 +++++++++++++------ 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala index 0165a2d285..7023ca2f4e 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala @@ -6,6 +6,40 @@ import org.scalatest.junit.JUnitSuite /** * Scheduler with artificial time, useful for testing. + * + * For example, you could test the `Observable.interval` operation using a `TestScheduler` as follows: + * + * {{{ + * @Test def testInterval() { + * import org.mockito.Matchers._ + * import org.mockito.Mockito._ + * + * val scheduler = TestScheduler() + * val observer = mock(classOf[rx.Observer[Long]]) + * + * val o = Observable.interval(1 second, scheduler) + * val sub = o.subscribe(observer) + * + * verify(observer, never).onNext(0L) + * verify(observer, never).onCompleted() + * verify(observer, never).onError(any(classOf[Throwable])) + * + * scheduler.advanceTimeTo(2 seconds) + * + * val inOrdr = inOrder(observer); + * inOrdr.verify(observer, times(1)).onNext(0L) + * inOrdr.verify(observer, times(1)).onNext(1L) + * inOrdr.verify(observer, never).onNext(2L) + * verify(observer, never).onCompleted() + * verify(observer, never).onError(any(classOf[Throwable])) + * + * sub.unsubscribe(); + * scheduler.advanceTimeTo(4 seconds) + * verify(observer, never).onNext(2L) + * verify(observer, times(1)).onCompleted() + * verify(observer, never).onError(any(classOf[Throwable])) + * } + * }}} */ class TestScheduler extends Scheduler { val asJava = new rx.concurrency.TestScheduler @@ -33,44 +67,39 @@ object TestScheduler { } private class UnitTest extends JUnitSuite { - import org.mockito.Matchers._ - import org.mockito.Mockito._ - import org.junit.{Test, Before} + import org.junit.Test import scala.concurrent.duration._ import scala.language.postfixOps import rx.lang.scala.{Observable, Observer} - var scheduler: TestScheduler = null - var observer: Observer[Long] = null - var observer2: Observer[Long] = null - - @Before def before() { - scheduler = TestScheduler() - observer = mock(classOf[rx.Observer[Long]]) - } - @Test def testInterval() { - val w = Observable.interval(1 second, scheduler) - val sub = w.subscribe(observer) + import org.mockito.Matchers._ + import org.mockito.Mockito._ + + val scheduler = TestScheduler() + val observer = mock(classOf[rx.Observer[Long]]) + + val o = Observable.interval(1 second, scheduler) + val sub = o.subscribe(observer) - verify(observer, never()).onNext(0L) - verify(observer, never()).onCompleted() - verify(observer, never()).onError(any(classOf[Throwable])) + verify(observer, never).onNext(0L) + verify(observer, never).onCompleted() + verify(observer, never).onError(any(classOf[Throwable])) scheduler.advanceTimeTo(2 seconds) val inOrdr = inOrder(observer); inOrdr.verify(observer, times(1)).onNext(0L) inOrdr.verify(observer, times(1)).onNext(1L) - inOrdr.verify(observer, never()).onNext(2L) - verify(observer, never()).onCompleted() - verify(observer, never()).onError(any(classOf[Throwable])) + inOrdr.verify(observer, never).onNext(2L) + verify(observer, never).onCompleted() + verify(observer, never).onError(any(classOf[Throwable])) sub.unsubscribe(); scheduler.advanceTimeTo(4 seconds) - verify(observer, never()).onNext(2L) + verify(observer, never).onNext(2L) verify(observer, times(1)).onCompleted() - verify(observer, never()).onError(any(classOf[Throwable])) + verify(observer, never).onError(any(classOf[Throwable])) } } From 9e81debab3936ba31dd682a0ba95c6c3695393e5 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Sun, 29 Sep 2013 20:58:08 +0200 Subject: [PATCH 124/333] make all constructors private --- .../src/main/scala/rx/lang/scala/Notification.scala | 2 +- .../rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala | 1 + .../scala/rx/lang/scala/observables/BlockingObservable.scala | 1 + .../src/main/scala/rx/lang/scala/util/Timestamped.scala | 3 ++- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala index c659ecdd6b..62ec5baf3b 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala @@ -8,7 +8,7 @@ sealed trait Notification[+T] { } /** - * Provides pattern matching support for Notifications. + * Provides pattern matching support and constructors for Notifications. * * Example: * {{{ diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 0937c4e6a6..763a5f54f4 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -20,6 +20,7 @@ package rx.lang.scala /** * The Observable interface that implements the Reactive Pattern. */ +// constructor is private because users should use apply in companion class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) // Uncommenting this line combined with `new Observable(...)` instead of `new Observable[T](...)` // makes the compiler crash diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala index eb2bb3bf27..8d9323ba32 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala @@ -23,6 +23,7 @@ import rx.lang.scala.ImplicitFunctionConversions._ * * You can obtain a BlockingObservable from an Observable using [[Observable.toBlockingObservable]] */ +// constructor is private because users should use Observable.toBlockingObservable class BlockingObservable[+T] private[scala] (val asJava: rx.observables.BlockingObservable[_ <: T]) extends AnyVal { diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala index e40cc0599b..e00b9bb17e 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala @@ -3,7 +3,8 @@ package rx.lang.scala.util /** * Wraps a value and a timestamp. */ -class Timestamped[+T](val asJava: rx.util.Timestamped[_ <: T]) extends AnyVal { +// constructor is private because users should use apply from companion +class Timestamped[+T] private[util] (val asJava: rx.util.Timestamped[_ <: T]) extends AnyVal { /** * Returns the timestamp, in milliseconds. */ From 6635e613fcf29826f5273714c3bd5e2a69a39ef7 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Mon, 30 Sep 2013 00:19:18 +0200 Subject: [PATCH 125/333] update TODO --- language-adaptors/rxjava-scala/TODO.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/language-adaptors/rxjava-scala/TODO.md b/language-adaptors/rxjava-scala/TODO.md index 4dcf7f2c40..abf88d06f3 100644 --- a/language-adaptors/rxjava-scala/TODO.md +++ b/language-adaptors/rxjava-scala/TODO.md @@ -4,8 +4,8 @@ TODOs for Scala Adapter This is a (probably incomplete) list of what still needs to be done in the Scala adaptor: -* integrating Scala Futures, should there be a common base interface for Futures and Observables? -* Add methods present in Scala collections library, but not in RxJava, e.g. aggregate à la Scala, collect, exists, tails, ... +* Integrating Scala Futures: Should there be a common base interface for Futures and Observables? And if all subscribers of an Observable wrapping a Future unsubscribe, the Future should be cancelled, but Futures do not support cancellation. +* Add methods present in Scala collections library, but not in RxJava, e.g. aggregate à la Scala, collect, tails, ... * combineLatest with arities > 2 * Avoid text duplication in scaladoc using templates, add examples, distinction between use case signature and full signature * other small TODOs From 5e6e1a55d87bc28c5c7cca8cc4be31ef01dbfcf2 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 30 Sep 2013 10:56:30 +0800 Subject: [PATCH 126/333] Implemented the 'Empty' operator with scheduler --- rxjava-core/src/main/java/rx/Observable.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 5a0f668dc5..a28554393e 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -547,6 +547,23 @@ public static Observable empty() { return from(new ArrayList()); } + /** + * Returns an Observable that emits no data to the {@link Observer} and immediately invokes + * its {@link Observer#onCompleted onCompleted} method with the specified scheduler. + *

+ * + * @param scheduler + * the scheduler to call the {@link Observer#onCompleted onCompleted} method. + * @param + * the type of the items (ostensibly) emitted by the Observable + * @return an Observable that returns no data to the {@link Observer} and immediately invokes + * the {@link Observer}'s {@link Observer#onCompleted() onCompleted} method with + * the specified scheduler. + */ + public static Observable empty(Scheduler scheduler) { + return Observable.empty().subscribeOn(scheduler); + } + /** * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it *

From 7f3210a9968606b45f21580043700543d4e0d155 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 30 Sep 2013 13:58:19 +0800 Subject: [PATCH 127/333] Added MSDN links --- rxjava-core/src/main/java/rx/Observable.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index a28554393e..51a0ded03b 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -542,6 +542,7 @@ public static Observable create(OnSubscribeFunc func) { * the type of the items (ostensibly) emitted by the Observable * @return an Observable that returns no data to the {@link Observer} and immediately invokes * the {@link Observer}'s {@link Observer#onCompleted() onCompleted} method + * @see MSDN: Observable.Empty Method */ public static Observable empty() { return from(new ArrayList()); @@ -559,6 +560,7 @@ public static Observable empty() { * @return an Observable that returns no data to the {@link Observer} and immediately invokes * the {@link Observer}'s {@link Observer#onCompleted() onCompleted} method with * the specified scheduler. + * @see MSDN: Observable.Empty Method (IScheduler) */ public static Observable empty(Scheduler scheduler) { return Observable.empty().subscribeOn(scheduler); From be3b3700ddc3839c1dceb73d790cf3528fa0abf1 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 30 Sep 2013 16:11:29 +0800 Subject: [PATCH 128/333] Added missing license header --- .../main/java/rx/operators/OperationSkipLast.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java b/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java index f8518b21df..10da0c06b4 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.any; From 67449fb8be018a9c6d82c1333d9f87dec913801c Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Mon, 30 Sep 2013 23:49:36 +0200 Subject: [PATCH 129/333] allow to construct Observables ina similar way as futures --- .../rx/lang/scala/examples/RxScalaDemo.scala | 34 +++++++++++++++++++ .../main/scala/rx/lang/scala/package.scala | 21 +++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index c7856b4ea4..61726df33f 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -420,6 +420,40 @@ class RxScalaDemo extends JUnitSuite { assertEquals("!!", Observable("a", "b", "c").drop(10).firstOrElse("!!").toBlockingObservable.single) } + @Test def observableLikeFuture1() { + implicit val scheduler = Schedulers.threadPoolForIO + val o1 = observable { + Thread.sleep(1000) + 5 + } + val o2 = observable { + Thread.sleep(500) + 4 + } + Thread.sleep(500) + val t1 = System.currentTimeMillis + println((o1 merge o2).first.toBlockingObservable.single) + println(System.currentTimeMillis - t1) + } + + @Test def observableLikeFuture2() { + class Friend {} + val session = new Object { + def getFriends: List[Friend] = List(new Friend, new Friend) + } + + implicit val scheduler = Schedulers.threadPoolForIO + val o: Observable[List[Friend]] = observable { + session.getFriends + } + o.subscribe( + friendList => println(friendList), + err => println(err.getMessage) + ) + + Thread.sleep(1500) // or convert to BlockingObservable + } + def output(s: String): Unit = println(s) // blocks until obs has completed diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala index 63e686250f..df1dd391fd 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala @@ -114,7 +114,26 @@ package object scala { type Subscription = rx.Subscription //#endif - + + /** + * Allows to construct observables in a similar way as futures. + * + * Example: + * + * {{{ + * implicit val scheduler = Schedulers.threadPoolForIO + * val o: Observable[List[Friend]] = observable { + * session.getFriends + * } + * o.subscribe( + * friendList => println(friendList), + * err => println(err.getMessage) + * ) + * }}} + */ + def observable[T](body: => T)(implicit scheduler: Scheduler): Observable[T] = { + Observable(1).observeOn(scheduler).map(_ => body) + } } /* From 866d0b3b1d734ce72c60a428784023dff17babdf Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 1 Oct 2013 11:26:51 +0200 Subject: [PATCH 130/333] implicit function conversion hack just for nicer scaladoc --- .../src/main/scala/rx/lang/scala/package.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala index df1dd391fd..9356b08f6b 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala @@ -104,6 +104,13 @@ package object scala { def unsubscribe() = s.unsubscribe() } + private[scala] implicit def schedulerActionToFunc2[T](action: (Scheduler, T) => Subscription) = + new rx.util.functions.Func2[rx.Scheduler, T, rx.Subscription] { + def call(s: rx.Scheduler, t: T): rx.Subscription = { + action(ImplicitFunctionConversions.javaSchedulerToScalaScheduler(s), t) + } + } + private[scala] implicit def fakeObserver2RxObserver[T](o: Observer[T]): rx.Observer[_ >: T] = ??? private[scala] implicit def rxObserver2fakeObserver[T](o: rx.Observer[_ >: T]): Observer[T] = ??? From 06797f81262da4327a54d47ddff6c87e78a8d8ec Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 1 Oct 2013 11:29:18 +0200 Subject: [PATCH 131/333] make defer implementation more explicit --- .../rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 763a5f54f4..7bb6e34392 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1893,7 +1893,7 @@ object Observable { * factory function */ def defer[T](observable: => Observable[T]): Observable[T] = { - Observable[T](JObservable.defer(observable.asJava)) + Observable[T](JObservable.defer[T](() => observable.asJava)) } /** @@ -2094,3 +2094,4 @@ private[scala] class UnitTestSuite extends org.scalatest.junit.JUnitSuite { } } + From 373f5b3834e9b0e299e734e29a9759c044efe13e Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 1 Oct 2013 11:42:20 +0200 Subject: [PATCH 132/333] apply review patch --- .../main/scala/rx/lang/scala/Observable.scala | 115 +++--------------- 1 file changed, 20 insertions(+), 95 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 7bb6e34392..321d29db10 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1341,7 +1341,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) val f: Func2[_ >: T, _ >: U, _ <: (T, U)] = (t: T, u: U) => (t, u) Observable[(T, U)](rx.Observable.combineLatest[T, U, (T, U)](this.asJava, that.asJava, f)) } - +// Review completed till here!!! /** * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. * @@ -1530,6 +1530,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable that emits only the very first item from the source, or a default value * if the source Observable completes without emitting any item. */ + // TODO def headOrElse def firstOrElse[U >: T](default: => U): Observable[U] = { this.take(1).fold[Option[U]](None)((v: Option[U], e: U) => Some(e)).map({ case Some(element) => element @@ -1546,6 +1547,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable that emits only the very first item from the source, or none if the * source Observable completes without emitting a single item. */ + // TODO def head + // TODO def tail def first: Observable[T] = { take(1) } @@ -1576,37 +1579,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) Observable[T](asJava.distinctUntilChanged[U](keySelector)) } - /** - * Returns an Observable that forwards all items emitted from the source Observable that are sequentially - * distinct according to an equality function. - * - * - * - * @param equality - * an equality function for deciding whether two emitted items are equal or not - * @return an Observable of sequentially distinct items - */ - // def distinctUntilChanged[U](equality: (T, T) => Boolean): Observable[T] = { - // TODO once https://github.com/Netflix/RxJava/issues/395 is fixed - // } - - /** - * Returns an Observable that forwards all items emitted from the source Observable that are sequentially - * distinct according to a key selector function and a comparator. - * - * - * - * @param keySelector - * a function that projects an emitted item to a key value which is used for deciding whether an item is sequentially - * distinct from another one or not - * @param equality - * an equality function for deciding whether two emitted item keys are equal or not - * @return an Observable of sequentially distinct items - */ - // def distinctUntilChanged[U](keySelector: T => U, equality: (T, T) => Boolean): Observable[T] = { - // TODO once https://github.com/Netflix/RxJava/issues/395 is fixed - // } - /** * Returns an Observable that forwards all distinct items emitted from the source Observable. * @@ -1618,20 +1590,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) Observable[T](asJava.distinct()) } - /** - * Returns an Observable that forwards all items emitted from the source Observable that are distinct according - * to a comparator. - * - * - * - * @param equality - * an equality function for deciding whether two emitted items are equal or not - * @return an Observable of distinct items - */ - // def distinct(equality: (T, T) => Boolean): Observable[T] = { - // TODO once https://github.com/Netflix/RxJava/issues/395 is fixed - // } - /** * Returns an Observable that forwards all items emitted from the source Observable that are distinct according * to a key selector function. @@ -1673,6 +1631,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable emitting the number of counted elements of the source Observable * as its single item. */ + //TODO Both size and length def length: Observable[Int] = { Observable[Integer](asJava.count()).map(_.intValue()) } @@ -1868,6 +1827,7 @@ object Observable { Observable[T](JObservable.from(args.toIterable.asJava)) } + // TODO (SG) documentation def apply(range: Range): Observable[Int] = { Observable[Int](JObservable.from(range.toIterable.asJava)) } @@ -1895,29 +1855,6 @@ object Observable { def defer[T](observable: => Observable[T]): Observable[T] = { Observable[T](JObservable.defer[T](() => observable.asJava)) } - - /** - * Returns an Observable that emits a single item and then completes. - * - * - * - * To convert any object into an Observable that emits that object, pass that object into the - * `just` method. - * - * This is similar to the [[Observable.apply[T](T*)]] method, except that - * [[Observable.apply[T](T*)]] will convert an `Iterable` object into an Observable that emits - * each of the items in the Iterable, one at a time, while the `just()` method - * converts an Iterable into an Observable that emits the entire Iterable as a single item. - * - * @param value - * the item to pass to the [[Observer]]'s [[Observer.onNext onNext]] method - * @tparam T - * the type of that item - * @return an Observable that emits a single item and then completes - */ - def just[T](value: T): Observable[T] = { - Observable[T](JObservable.just(value)) - } /** * Returns an Observable that never sends any items or notifications to an [[Observer]]. @@ -1932,25 +1869,6 @@ object Observable { Observable[Nothing](JObservable.never()) } - // TODO also support Scala Futures, but think well before. Do we want to Future and Observable - // to share a common base interface? - - // private because it's not RxScala's responsability to provide this alias - private type Future[+T] = java.util.concurrent.Future[_ <: T] - - def apply[T](f: Future[T]): Observable[T] = { - Observable[T](rx.Observable.from(f)) - } - - def apply[T](f: Future[T], scheduler: Scheduler): Observable[T] = { - val sched: rx.Scheduler = scheduler - Observable[T](rx.Observable.from(f, sched)) - } - - def apply[T](f: Future[T], duration: Duration): Observable[T] = { - Observable[T](rx.Observable.from(f, duration.length, duration.unit)) - } - /** * Given a Seq of N observables, returns an observable that emits Seqs of N elements each. * The first emitted Seq will contain the first element of each source observable, @@ -1959,8 +1877,10 @@ object Observable { * @param observables * A Seq of source Observables * @return an Observable that emits the zipped Seqs - */ - def zip[T](observables: Seq[Observable[T]]): Observable[Seq[T]] = { + */ + def zip[A,B,C](obA: Observable[A], obB: Observable[B], obC: Observable[B]): Observable[(A, B, C)] + // TODO until 6 + def zip[T](observables: Observable[T]*): Observable[Seq[T]] = { val f: FuncN[Seq[T]] = (args: Seq[java.lang.Object]) => { val asSeq: Seq[Object] = args.toSeq asSeq.asInstanceOf[Seq[T]] @@ -1989,11 +1909,16 @@ object Observable { Observable[Seq[T]](o) } - def interval(duration: Duration): Observable[Long] = { - (new Observable[java.lang.Long](JObservable.interval(duration.length, duration.unit))).map(_.longValue()) - } - - def interval(duration: Duration, scheduler: Scheduler): Observable[Long] = { + /** + * TODO (SG) ScalaDoc + * TODO Provide implicit scheduler: + * + * def interval(duration: Duration)(implicit scheduler: Scheduler): Observable[Long] + * def interval(duration: Duration)(scheduler: Scheduler): Observable[Long] + * def interval(scheduler: Scheduler)(duration: Duration): Observable[Long] + * def interval(duration: Duration, scheduler: Scheduler): Observable[Long] && def interval(duration: Duration): Observable[Long] + */ + def interval(duration: Duration)(implicit scheduler: Scheduler): Observable[Long] = { (new Observable[java.lang.Long](JObservable.interval(duration.length, duration.unit, scheduler))).map(_.longValue()) } From 2654f60e05a24fc910f73284e32290403d5bffcf Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 1 Oct 2013 13:28:46 +0200 Subject: [PATCH 133/333] head, headOrElse, zip(3), zip(4) --- .../main/scala/rx/lang/scala/Observable.scala | 116 +++++++++++++----- 1 file changed, 82 insertions(+), 34 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 321d29db10..fa2f4d1404 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1524,13 +1524,12 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * @param defaultValue + * @param default * The default value to emit if the source Observable doesn't emit anything. * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything. * @return an Observable that emits only the very first item from the source, or a default value * if the source Observable completes without emitting any item. */ - // TODO def headOrElse def firstOrElse[U >: T](default: => U): Observable[U] = { this.take(1).fold[Option[U]](None)((v: Option[U], e: U) => Some(e)).map({ case Some(element) => element @@ -1538,6 +1537,20 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) }) } + /** + * Returns an Observable that emits only the very first item emitted by the source Observable, or + * a default value if the source Observable is empty. + * + * + * + * @param default + * The default value to emit if the source Observable doesn't emit anything. + * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything. + * @return an Observable that emits only the very first item from the source, or a default value + * if the source Observable completes without emitting any item. + */ + def headOrElse[U >: T](default: => U): Observable[U] = firstOrElse(default) + /** * Returns an Observable that emits only the very first item emitted by the source Observable. * This is just a shorthand for `take(1)`. @@ -1547,12 +1560,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable that emits only the very first item from the source, or none if the * source Observable completes without emitting a single item. */ - // TODO def head - // TODO def tail - def first: Observable[T] = { - take(1) - } + def first: Observable[T] = take(1) + def head: Observable[T] = { + this.take(1).fold[Option[T]](None)((v: Option[T], e: T) => Some(e)).map({ + case Some(element) => element + case None => throw new NoSuchElementException("head of empty Observable") + }) + } + + // TODO def tail + /** * Returns an Observable that forwards all sequentially distinct items emitted from the source Observable. * @@ -1870,31 +1888,36 @@ object Observable { } /** - * Given a Seq of N observables, returns an observable that emits Seqs of N elements each. - * The first emitted Seq will contain the first element of each source observable, - * the second Seq the second element of each source observable, and so on. + * Given 3 observables, returns an observable that emits Tuples of 3 elements each. + * The first emitted Tuple will contain the first element of each source observable, + * the second Tuple the second element of each source observable, and so on. * - * @param observables - * A Seq of source Observables - * @return an Observable that emits the zipped Seqs - */ - def zip[A,B,C](obA: Observable[A], obB: Observable[B], obC: Observable[B]): Observable[(A, B, C)] - // TODO until 6 - def zip[T](observables: Observable[T]*): Observable[Seq[T]] = { - val f: FuncN[Seq[T]] = (args: Seq[java.lang.Object]) => { - val asSeq: Seq[Object] = args.toSeq - asSeq.asInstanceOf[Seq[T]] - } - val list = observables.map(_.asJava).asJava - val o = rx.Observable.zip(list, f) - Observable[Seq[T]](o) + * @return an Observable that emits the zipped Observables + */ + def zip[A, B, C](obA: Observable[A], obB: Observable[B], obC: Observable[C]): Observable[(A, B, C)] = { + Observable[(A, B, C)](rx.Observable.zip[A, B, C, (A, B, C)](obA.asJava, obB.asJava, obC.asJava, (a: A, b: B, c: C) => (a, b, c))) } /** - * Given an Observable emitting N source observables, returns an observable that emits Seqs of N elements each. + * Given 4 observables, returns an observable that emits Tuples of 4 elements each. + * The first emitted Tuple will contain the first element of each source observable, + * the second Tuple the second element of each source observable, and so on. + * + * @return an Observable that emits the zipped Observables + */ + def zip[A, B, C, D](obA: Observable[A], obB: Observable[B], obC: Observable[C], obD: Observable[D]): Observable[(A, B, C, D)] = { + Observable[(A, B, C, D)](rx.Observable.zip[A, B, C, D, (A, B, C, D)](obA.asJava, obB.asJava, obC.asJava, obD.asJava, (a: A, b: B, c: C, d: D) => (a, b, c, d))) + } + + /** + * Given an Observable emitting `N` source observables, returns an observable that + * emits Seqs of `N` elements each. * The first emitted Seq will contain the first element of each source observable, * the second Seq the second element of each source observable, and so on. * + * Note that the returned Observable will only start emitting items once the given + * `Observable[Observable[T]]` has completed, because otherwise it cannot know `N`. + * * @param observables * An Observable emitting N source Observables * @return an Observable that emits the zipped Seqs @@ -1908,17 +1931,32 @@ object Observable { val o = rx.Observable.zip(list, f) Observable[Seq[T]](o) } - + + /** + * Emits 0, 1, 2, ... with a delay of `duration` between consecutive numbers. + * + * + * + * @param duration + * duration between two consecutive numbers + * @return An Observable that emits a number each time interval. + */ + def interval(duration: Duration): Observable[Long] = { + (new Observable[java.lang.Long](JObservable.interval(duration.length, duration.unit))).map(_.longValue()) + } + /** - * TODO (SG) ScalaDoc - * TODO Provide implicit scheduler: + * Emits 0, 1, 2, ... with a delay of `duration` between consecutive numbers. * - * def interval(duration: Duration)(implicit scheduler: Scheduler): Observable[Long] - * def interval(duration: Duration)(scheduler: Scheduler): Observable[Long] - * def interval(scheduler: Scheduler)(duration: Duration): Observable[Long] - * def interval(duration: Duration, scheduler: Scheduler): Observable[Long] && def interval(duration: Duration): Observable[Long] + * + * + * @param duration + * duration between two consecutive numbers + * @param scheduler + * the scheduler to use + * @return An Observable that emits a number each time interval. */ - def interval(duration: Duration)(implicit scheduler: Scheduler): Observable[Long] = { + def interval(duration: Duration, scheduler: Scheduler): Observable[Long] = { (new Observable[java.lang.Long](JObservable.interval(duration.length, duration.unit, scheduler))).map(_.longValue()) } @@ -1949,7 +1987,7 @@ private[scala] class UnitTestSuite extends org.scalatest.junit.JUnitSuite { import scala.concurrent.duration._ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ - import org.mockito.Matchers.any + import org.mockito.Matchers._ import org.mockito.Mockito._ import org.mockito.{ MockitoAnnotations, Mock } @@ -2012,6 +2050,16 @@ private[scala] class UnitTestSuite extends org.scalatest.junit.JUnitSuite { assertEquals(receivedMsg, msg) } + @Test def testHead() { + val observer = mock(classOf[Observer[Int]]) + val o = Observable().head + val sub = o.subscribe(observer) + + verify(observer, never).onNext(any(classOf[Int])) + verify(observer, never).onCompleted() + verify(observer, times(1)).onError(any(classOf[NoSuchElementException])) + } + @Test def testTest() = { val a: Observable[Int] = Observable() assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) From df0436c9293f05de2f00f9ef50b6be0b053d99fc Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Tue, 1 Oct 2013 17:39:56 +0200 Subject: [PATCH 134/333] changes from review + scaladoc improvements --- language-adaptors/rxjava-scala/TODO.md | 9 + .../rx/lang/scala/examples/RxScalaDemo.scala | 12 +- .../main/scala/rx/lang/scala/Observable.scala | 259 +++++++++--------- .../scala/rx/lang/scala/util/package.scala | 8 +- 4 files changed, 143 insertions(+), 145 deletions(-) diff --git a/language-adaptors/rxjava-scala/TODO.md b/language-adaptors/rxjava-scala/TODO.md index abf88d06f3..a3f4b8fd53 100644 --- a/language-adaptors/rxjava-scala/TODO.md +++ b/language-adaptors/rxjava-scala/TODO.md @@ -7,7 +7,16 @@ This is a (probably incomplete) list of what still needs to be done in the Scala * Integrating Scala Futures: Should there be a common base interface for Futures and Observables? And if all subscribers of an Observable wrapping a Future unsubscribe, the Future should be cancelled, but Futures do not support cancellation. * Add methods present in Scala collections library, but not in RxJava, e.g. aggregate à la Scala, collect, tails, ... * combineLatest with arities > 2 +* Implicit schedulers? * Avoid text duplication in scaladoc using templates, add examples, distinction between use case signature and full signature * other small TODOs +(Implicit) schedulers for interval: Options: + +```scala +def interval(duration: Duration)(implicit scheduler: Scheduler): Observable[Long] +def interval(duration: Duration)(scheduler: Scheduler): Observable[Long] +def interval(scheduler: Scheduler)(duration: Duration): Observable[Long] +def interval(duration: Duration, scheduler: Scheduler): Observable[Long] && def interval(duration: Duration): Observable[Long] +```` diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index 61726df33f..a449737acf 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -323,13 +323,13 @@ class RxScalaDemo extends JUnitSuite { .toBlockingObservable.foreach(println(_)) } - // source Observables are in a List: - @Test def zipManySeqExample() { - val observables = List(Observable(1, 2), Observable(10, 20), Observable(100, 200)) - (for (seq <- Observable.zip(observables)) yield seq.mkString("(", ", ", ")")) + // source Observables are all known: + @Test def zip3Example() { + val o = Observable.zip(Observable(1, 2), Observable(10, 20), Observable(100, 200)) + (for ((n1, n2, n3) <- o) yield s"$n1, $n2 and $n3") .toBlockingObservable.foreach(println(_)) } - + // source Observables are in an Observable: @Test def zipManyObservableExample() { val observables = Observable(Observable(1, 2), Observable(10, 20), Observable(100, 200)) @@ -453,7 +453,7 @@ class RxScalaDemo extends JUnitSuite { Thread.sleep(1500) // or convert to BlockingObservable } - + def output(s: String): Unit = println(s) // blocks until obs has completed diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index fa2f4d1404..1fc26ee3d8 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -14,11 +14,36 @@ * limitations under the License. */ - package rx.lang.scala /** * The Observable interface that implements the Reactive Pattern. + * + * @define subscribeObserver + * Call this method to subscribe an [[Observer]] for receiving + * items and notifications from the Observable. + * + * A typical implementation of `subscribe` does the following: + * + * It stores a reference to the Observer in a collection object, such as a `List[T]` object. + * + * It returns a reference to the [[Subscription]] interface. This enables Observers to + * unsubscribe, that is, to stop receiving items and notifications before the Observable stops + * sending them, which also invokes the Observer's [[Observer.onCompleted onCompleted]] method. + * + * An `Observable[T]` instance is responsible for accepting all subscriptions + * and notifying all Observers. Unless the documentation for a particular + * `Observable[T]` implementation indicates otherwise, Observers should make no + * assumptions about the order in which multiple Observers will receive their notifications. + * + * @param observer + * the observer + * @param scheduler + * the [[Scheduler]] on which Observers subscribe to the Observable + * @return a [[Subscription]] reference with which the [[Observer]] can stop receiving items + * before the Observable has finished sending them + * @throws IllegalArgumentException + * if the [[Observer]] provided as the argument to `subscribe()` is `null` */ // constructor is private because users should use apply in companion class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) @@ -35,65 +60,20 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) import rx.lang.scala.subjects.Subject import rx.lang.scala.observables.BlockingObservable import rx.lang.scala.ImplicitFunctionConversions._ - - /** - * An [[Observer]] must call an Observable's `subscribe` method in order to - * receive items and notifications from the Observable. - * - * A typical implementation of `subscribe` does the following: - * - * It stores a reference to the Observer in a collection object, such as a `List[T]` object. - * - * It returns a reference to the [[Subscription]] interface. This enables Observers to - * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's [[Observer.onCompleted onCompleted]] method. - * - * An `Observable[T]` instance is responsible for accepting all subscriptions - * and notifying all Observers. Unless the documentation for a particular - * `Observable[T]` implementation indicates otherwise, Observers should make no - * assumptions about the order in which multiple Observers will receive their notifications. - * - * - * @param observer - * the observer - * @return a [[Subscription]] reference with which the [[Observer]] can stop receiving items - * before the Observable has finished sending them - * @throws IllegalArgumentException - * if the [[Observer]] provided as the argument to `subscribe()` is `null` - */ - def subscribe(observer: Observer[T]): Subscription = { - asJava.subscribe(observer) - } /** - * An [[Observer]] must call an Observable's `subscribe` method in order to - * receive items and notifications from the Observable. - * - * A typical implementation of `subscribe` does the following: - * - * It stores a reference to the Observer in a collection object, such as a `List[T]` object. - * - * It returns a reference to the [[Subscription]] interface. This enables Observers to - * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's [[Observer.onCompleted onCompleted]] method. - * - * An `Observable[T]` instance is responsible for accepting all subscriptions - * and notifying all Observers. Unless the documentation for a particular `Observable[T]` implementation indicates otherwise, Observers should make no - * assumptions about the order in which multiple Observers will receive their notifications. - * - * - * @param observer - * the observer - * @param scheduler - * the [[Scheduler]] on which Observers subscribe to the Observable - * @return a [[Subscription]] reference with which Observers can stop receiving items and - * notifications before the Observable has finished sending them - * @throws IllegalArgumentException - * if an argument to `subscribe()` is `null` + * $subscribeObserver */ def subscribe(observer: Observer[T], scheduler: Scheduler): Subscription = { asJava.subscribe(observer, scheduler) } + + /** + * $subscribeObserver + */ + def subscribe(observer: Observer[T]): Subscription = { + asJava.subscribe(observer) + } def subscribe(onNext: T => Unit): Subscription = { asJava.subscribe(onNext) @@ -102,7 +82,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) def subscribe(onNext: T => Unit, onError: Throwable => Unit): Subscription = { asJava.subscribe(onNext, onError) } - + def subscribe(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit): Subscription = { asJava.subscribe(onNext, onError, onComplete) } @@ -120,16 +100,15 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Returns a [[ConnectableObservable]] that upon connection causes the source Observable to + * Returns a pair of a start function and an [[Observable]] that upon calling the start function causes the source Observable to * push results into the specified subject. * * @param subject - * the [[Subject]] for the [[ConnectableObservable]] to push source items - * into + * the `rx.lang.scala.subjects.Subject` to push source items into * @tparam R * result type * @return a pair of a start function and an [[Observable]] such that when the start function - * is called, the Observable starts to push results into the specified [[Subject]] + * is called, the Observable starts to push results into the specified Subject */ def multicast[R](subject: Subject[T, R]): (() => Subscription, Observable[R]) = { val javaCO = asJava.multicast[R](subject) @@ -192,7 +171,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Wraps each item emitted by a source Observable in a [[Timestamped]] object. + * Wraps each item emitted by a source Observable in a [[rx.lang.scala.util.Timestamped]] object. * * * @@ -230,16 +209,16 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Creates an Observable which produces buffers of collected values. * * This Observable produces connected non-overlapping buffers. The current buffer is - * emitted and replaced with a new buffer when the Observable produced by the specified [[Func0]] produces a [[rx.lang.scala.util.Closing]] object. The * [[Func0]] will then + * emitted and replaced with a new buffer when the Observable produced by the specified function produces a [[rx.lang.scala.util.Closing]] object. The function will then * be used to create a new Observable to listen for the end of the next buffer. * * @param bufferClosingSelector - * The [[Func0]] which is used to produce an [[Observable]] for every buffer created. + * The function which is used to produce an [[Observable]] for every buffer created. * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer * is emitted and replaced with a new one. * @return * An [[Observable]] which produces connected non-overlapping buffers, which are emitted - * when the current [[Observable]] created with the [[Func0]] argument produces a [[rx.lang.scala.util.Closing]] object. + * when the current [[Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. */ def buffer(bufferClosingSelector: () => Observable[Closing]) : Observable[Seq[T]] = { val f: Func0[_ <: rx.Observable[_ <: Closing]] = bufferClosingSelector().asJava @@ -251,7 +230,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Creates an Observable which produces buffers of collected values. * * This Observable produces buffers. Buffers are created when the specified "bufferOpenings" - * Observable produces a [[rx.lang.scala.util.Opening]] object. Additionally the [[Func0]] argument + * Observable produces a [[rx.lang.scala.util.Opening]] object. Additionally the function argument * is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. When this * Observable produces such an object, the associated buffer is emitted. * @@ -259,7 +238,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * The [[Observable]] which, when it produces a [[rx.lang.scala.util.Opening]] object, will cause * another buffer to be created. * @param bufferClosingSelector - * The [[Func0]] which is used to produce an [[Observable]] for every buffer created. + * The function which is used to produce an [[Observable]] for every buffer created. * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer * is emitted. * @return @@ -301,7 +280,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * The maximum size of each buffer before it should be emitted. * @param skip * How many produced values need to be skipped before starting a new buffer. Note that when "skip" and - * "count" are equals that this is the same operation as [[Observable.buffer(int)]]. + * "count" are equals that this is the same operation as `buffer(int)`. * @return * An [[Observable]] which produces buffers every "skipped" values containing at most * "count" produced values. @@ -440,16 +419,16 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows. The current window is emitted and replaced with a new window when the - * Observable produced by the specified [[Func0]] produces a [[rx.lang.scala.util.Closing]] object. The [[Func0]] will then be used to create a new Observable to listen for the end of the next + * Observable produced by the specified function produces a [[rx.lang.scala.util.Closing]] object. The function will then be used to create a new Observable to listen for the end of the next * window. * * @param closingSelector - * The [[Func0]] which is used to produce an [[Observable]] for every window created. + * The function which is used to produce an [[Observable]] for every window created. * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window * is emitted and replaced with a new one. * @return * An [[Observable]] which produces connected non-overlapping windows, which are emitted - * when the current [[Observable]] created with the [[Func0]] argument produces a [[rx.lang.scala.util.Closing]] object. + * when the current [[Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. */ def window(closingSelector: () => Observable[Closing]): Observable[Observable[T]] = { val func : Func0[_ <: rx.Observable[_ <: Closing]] = closingSelector().asJava @@ -464,14 +443,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces windows. * Chunks are created when the specified "windowOpenings" Observable produces a [[rx.lang.scala.util.Opening]] object. - * Additionally the [[Func0]] argument is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. When this Observable produces such an object, the associated window is + * Additionally the `closingSelector` argument is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. When this Observable produces such an object, the associated window is * emitted. * * @param windowOpenings * The [[Observable]] which when it produces a [[rx.lang.scala.util.Opening]] object, will cause * another window to be created. * @param closingSelector - * The [[Func0]] which is used to produce an [[Observable]] for every window created. + * The function which is used to produce an [[Observable]] for every window created. * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window * is emitted. * @return @@ -502,17 +481,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces windows every - * "skip" values, each containing "count" elements. When the source Observable completes or encounters an error, + * `skip` values, each containing `count` elements. When the source Observable completes or encounters an error, * the current window is emitted and the event is propagated. * * @param count * The maximum size of each window before it should be emitted. * @param skip - * How many produced values need to be skipped before starting a new window. Note that when "skip" and - * "count" are equals that this is the same operation as [[Observable.window(Observable, int)]]. + * How many produced values need to be skipped before starting a new window. Note that when `skip` and + * `count` are equal that this is the same operation as `window(int)`. * @return * An [[Observable]] which produces windows every "skipped" values containing at most - * "count" produced values. + * `count` produced values. */ def window(count: Int, skip: Int): Observable[Observable[T]] = { Observable.jObsOfJObsToScObsOfScObs(asJava.window(count, skip)) @@ -821,9 +800,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Instruct an Observable to pass control to another Observable rather than invoking [[Observer.onError onError]] if it encounters an error of type [[java.lang.Exception]]. + * Instruct an Observable to pass control to another Observable rather than invoking [[Observer.onError onError]] if it encounters an error of type `java.lang.Exception`. * - * This differs from [[Observable.onErrorResumeNext]] in that this one does not handle [[java.lang.Throwable]] or [[java.lang.Error]] but lets those continue through. + * This differs from `Observable.onErrorResumeNext` in that this one does not handle `java.lang.Throwable` or `java.lang.Error` but lets those continue through. * * * @@ -909,7 +888,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Returns a [[ConnectableObservable]] that shares a single subscription to the underlying + * Returns a pair of a start function and an [[Observable]] that shares a single subscription to the underlying * Observable that will replay all of its items and notifications to any future [[Observer]]. * * @@ -924,7 +903,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * This method has similar behavior to [[Observable.replay]] except that this auto-subscribes to - * the source Observable rather than returning a [[ConnectableObservable]]. + * the source Observable rather than returning a start function and an Observable. * * * @@ -943,7 +922,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Returns a [[ConnectableObservable]], which waits until its [[ConnectableObservable.connect connect]] method is called before it begins emitting + * Returns a a pair of a start function and an [[Observable]], which waits until the start function is called before it begins emitting * items to those [[Observer]]s that have subscribed to it. * * @@ -1192,7 +1171,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Normally, an Observable that returns multiple items will do so by invoking its [[Observer]]'s [[Observer.onNext onNext]] method for each such item. You can change * this behavior, instructing the Observable to compose a list of all of these items and then to * invoke the Observer's `onNext` function once, passing it the entire list, by - * calling the Observable's `toList` method prior to calling its [[Observable.subscribe]] method. + * calling the Observable's `toList` method prior to calling its `Observable.subscribe` method. * * Be careful not to use this operator on Observables that emit infinite or very large numbers * of items, as you do not have the option to unsubscribe. @@ -1341,7 +1320,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) val f: Func2[_ >: T, _ >: U, _ <: (T, U)] = (t: T, u: U) => (t, u) Observable[(T, U)](rx.Observable.combineLatest[T, U, (T, U)](this.asJava, that.asJava, f)) } -// Review completed till here!!! + /** * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. * @@ -1351,17 +1330,15 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * Information on debounce vs throttle: * - * [ul] - * [li]http://drupalmotion.com/article/debounce-and-throttle-visual-explanation - * [li]http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ - * [li]http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ - * + * - http://drupalmotion.com/article/debounce-and-throttle-visual-explanation + * - http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ + * - http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ * * @param timeout * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. * * @return An [[Observable]] which filters out values which are too quickly followed up with newer values. - * @see [[Observable.debounce]] + * @see `Observable.debounce` */ def throttleWithTimeout(timeout: Duration): Observable[T] = { Observable[T](asJava.throttleWithTimeout(timeout.length, timeout.unit)) @@ -1376,17 +1353,15 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * Information on debounce vs throttle: * - * [ul] - * [li]http://drupalmotion.com/article/debounce-and-throttle-visual-explanation - * [li]http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ - * [li]http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ - * + * - http://drupalmotion.com/article/debounce-and-throttle-visual-explanation + * - http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ + * - http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ * * @param timeout * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. * * @return An [[Observable]] which filters out values which are too quickly followed up with newer values. - * @see [[Observable.throttleWithTimeout]]; + * @see `Observable.throttleWithTimeout` */ def debounce(timeout: Duration): Observable[T] = { Observable[T](asJava.debounce(timeout.length, timeout.unit)) @@ -1401,18 +1376,16 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * Information on debounce vs throttle: * - * [ul] - * [li]http://drupalmotion.com/article/debounce-and-throttle-visual-explanation - * [li]http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ - * [li]http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ - * + * - http://drupalmotion.com/article/debounce-and-throttle-visual-explanation + * - http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ + * - http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ * * @param timeout * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. * @param scheduler * The [[Scheduler]] to use internally to manage the timers which handle timeout for each event. * @return Observable which performs the throttle operation. - * @see [[Observable.throttleWithTimeout]]; + * @see `Observable.throttleWithTimeout` */ def debounce(timeout: Duration, scheduler: Scheduler): Observable[T] = { Observable[T](asJava.debounce(timeout.length, timeout.unit, scheduler)) @@ -1430,7 +1403,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param scheduler * The [[Scheduler]] to use internally to manage the timers which handle timeout for each event. * @return Observable which performs the throttle operation. - * @see [[Observable.debounce]] + * @see `Observable.debounce` */ def throttleWithTimeout(timeout: Duration, scheduler: Scheduler): Observable[T] = { Observable[T](asJava.throttleWithTimeout(timeout.length, timeout.unit, scheduler)) @@ -1439,7 +1412,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Throttles by skipping value until `skipDuration` passes and then emits the next received value. * - * This differs from [[Observable.throttleLast]] in that this only tracks passage of time whereas [[Observable.throttleLast]] ticks at scheduled intervals. + * This differs from `Observable.throttleLast` in that this only tracks passage of time whereas `Observable.throttleLast` ticks at scheduled intervals. * * * @@ -1456,7 +1429,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Throttles by skipping value until `skipDuration` passes and then emits the next received value. * - * This differs from [[Observable.throttleLast]] in that this only tracks passage of time whereas [[Observable.throttleLast]] ticks at scheduled intervals. + * This differs from `Observable.throttleLast` in that this only tracks passage of time whereas `Observable.throttleLast` ticks at scheduled intervals. * * * @@ -1471,7 +1444,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Throttles by returning the last value of each interval defined by 'intervalDuration'. * - * This differs from [[Observable.throttleFirst]] in that this ticks along at a scheduled interval whereas [[Observable.throttleFirst]] does not tick, it just tracks passage of time. + * This differs from `Observable.throttleFirst` in that this ticks along at a scheduled interval whereas `Observable.throttleFirst` does not tick, it just tracks passage of time. * * * @@ -1486,7 +1459,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Throttles by returning the last value of each interval defined by 'intervalDuration'. * - * This differs from [[Observable.throttleFirst]] in that this ticks along at a scheduled interval whereas [[Observable.throttleFirst]] does not tick, it just tracks passage of time. + * This differs from `Observable.throttleFirst` in that this ticks along at a scheduled interval whereas `Observable.throttleFirst` does not tick, it just tracks passage of time. * * * @@ -1561,7 +1534,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * source Observable completes without emitting a single item. */ def first: Observable[T] = take(1) - + + /* + + TODO once https://github.com/Netflix/RxJava/issues/417 is fixed, we can add head and tail methods + + /** + * emits NoSuchElementException("head of empty Observable") if empty + */ def head: Observable[T] = { this.take(1).fold[Option[T]](None)((v: Option[T], e: T) => Some(e)).map({ case Some(element) => element @@ -1569,7 +1549,12 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) }) } - // TODO def tail + /** + * emits an UnsupportedOperationException("tail of empty list") if empty + */ + def tail: Observable[T] = ??? + + */ /** * Returns an Observable that forwards all sequentially distinct items emitted from the source Observable. @@ -1624,23 +1609,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Returns an Observable that forwards all items emitted from the source Observable that are distinct according - * to a key selector function and a comparator. + * Returns an Observable that counts the total number of elements in the source Observable. * - * + * * - * @param keySelector - * a function that projects an emitted item to a key value which is used for deciding whether an item is - * distinct from another one or not - * @param equality - * an equality function for deciding whether two emitted item keys are equal or not - * @return an Observable of distinct items - * @see MSDN: Observable.distinct + * @return an Observable emitting the number of counted elements of the source Observable + * as its single item. */ - // def distinct[U](keySelector: T => U, equality: (T, T) => Boolean): Observable[T] = { - // TODO once https://github.com/Netflix/RxJava/issues/395 is fixed - //} - + def length: Observable[Int] = { + Observable[Integer](asJava.count()).map(_.intValue()) + } + /** * Returns an Observable that counts the total number of elements in the source Observable. * @@ -1649,10 +1628,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable emitting the number of counted elements of the source Observable * as its single item. */ - //TODO Both size and length - def length: Observable[Int] = { - Observable[Integer](asJava.count()).map(_.intValue()) - } + def size: Observable[Int] = length /** * Retry subscription to origin Observable upto given retry count. @@ -1692,7 +1668,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Converts an Observable into a [[BlockingObservable]] (an Observable with blocking + * Converts an Observable into a [[rx.lang.scala.observables.BlockingObservable]] (an Observable with blocking * operators). * * @see Blocking Observable Operators @@ -1702,10 +1678,10 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Perform work in parallel by sharding an `Observable[T]` on a [[Schedulers.threadPoolForComputation()]] [[Scheduler]] and return an `Observable[R]` with the output. + * Perform work in parallel by sharding an `Observable[T]` on a [[rx.lang.scala.concurrency.Schedulers.threadPoolForComputation]] [[Scheduler]] and return an `Observable[R]` with the output. * * @param f - * a [[Func1]] that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` + * a function that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` * @return an Observable with the output of the function executed on a [[Scheduler]] */ def parallel[R](f: Observable[T] => Observable[R]): Observable[R] = { @@ -1718,10 +1694,10 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Perform work in parallel by sharding an `Observable[T]` on a [[Scheduler]] and return an `Observable[R]` with the output. * * @param f - * a [[Func1]] that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` + * a function that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` * @param s * a [[Scheduler]] to perform the work on. - * @return an Observable with the output of the [[Func1]] executed on a [[Scheduler]] + * @return an Observable with the output of the function executed on a [[Scheduler]] */ def parallel[R](f: Observable[T] => Observable[R], scheduler: Scheduler): Observable[R] = { val fJava: Func1[rx.Observable[T], rx.Observable[R]] = @@ -1844,8 +1820,19 @@ object Observable { def apply[T](args: T*): Observable[T] = { Observable[T](JObservable.from(args.toIterable.asJava)) } - - // TODO (SG) documentation + + /** + * Generates an Observable that emits a sequence of integers within a specified range. + * + * + * + * Implementation note: the entire range will be immediately emitted each time an [[Observer]] subscribes. + * Since this occurs before the [[Subscription]] is returned, + * it in not possible to unsubscribe from the sequence before it completes. + * + * @param range the range + * @return an Observable that emits a range of sequential integers + */ def apply(range: Range): Observable[Int] = { Observable[Int](JObservable.from(range.toIterable.asJava)) } @@ -2050,6 +2037,7 @@ private[scala] class UnitTestSuite extends org.scalatest.junit.JUnitSuite { assertEquals(receivedMsg, msg) } + /* @Test def testHead() { val observer = mock(classOf[Observer[Int]]) val o = Observable().head @@ -2059,6 +2047,7 @@ private[scala] class UnitTestSuite extends org.scalatest.junit.JUnitSuite { verify(observer, never).onCompleted() verify(observer, times(1)).onError(any(classOf[NoSuchElementException])) } + */ @Test def testTest() = { val a: Observable[Int] = Observable() diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala index 07820f6ea1..7913808794 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala @@ -22,25 +22,25 @@ package object util { /** * Tagging interface for objects which can open buffers. - * @see [[Observable.buffer]] + * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ type Opening = rx.util.Opening /** * Creates an object which can open buffers. - * @see [[Observable.buffer]] + * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ def Opening() = rx.util.Openings.create() /** * Tagging interface for objects which can close buffers. - * @see [[Observable.buffer]] + * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ type Closing = rx.util.Closing /** * Creates an object which can close buffers. - * @see [[Observable.buffer]] + * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ def Closing() = rx.util.Closings.create() From b90634d94b9fe1669cb2f4cd9354dc200ab1b5dd Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 2 Oct 2013 13:40:49 +0200 Subject: [PATCH 135/333] improve scaladoc --- .../scala/ImplicitFunctionConversions.scala | 28 -- .../scala/rx/lang/scala/Notification.scala | 2 +- .../main/scala/rx/lang/scala/Observable.scala | 295 ++++++++++++------ .../main/scala/rx/lang/scala/Scheduler.scala | 2 +- .../rx/lang/scala/observables/package.scala | 2 +- .../main/scala/rx/lang/scala/package.scala | 24 +- .../scala/rx/lang/scala/util/package.scala | 2 +- 7 files changed, 213 insertions(+), 142 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index 7bdcdf6f36..5db1c673f6 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -45,41 +45,26 @@ object ImplicitFunctionConversions { } } - /** - * Converts a by-name parameter to a Rx Func0 - */ implicit def scalaByNameParamToFunc0[B](param: => B): Func0[B] = new Func0[B] { def call(): B = param } - /** - * Converts 0-arg function to Rx Action0 - */ implicit def scalaFunction0ProducingUnitToAction0(f: (() => Unit)): Action0 = new Action0 { def call(): Unit = f() } - /** - * Converts 1-arg function to Rx Action1 - */ implicit def scalaFunction1ProducingUnitToAction1[A](f: (A => Unit)): Action1[A] = new Action1[A] { def call(a: A): Unit = f(a) } - /** - * Converts 1-arg predicate to Rx Func1[A, java.lang.Boolean] - */ implicit def scalaBooleanFunction1ToRxBooleanFunc1[A](f: (A => Boolean)): Func1[A, jlang.Boolean] = new Func1[A, jlang.Boolean] { def call(a: A): jlang.Boolean = f(a).booleanValue } - /** - * Converts 2-arg predicate to Rx Func2[A, B, java.lang.Boolean] - */ implicit def scalaBooleanFunction2ToRxBooleanFunc1[A, B](f: ((A, B) => Boolean)): Func2[A, B, jlang.Boolean] = new Func2[A, B, jlang.Boolean] { def call(a: A, b: B): jlang.Boolean = f(a, b).booleanValue @@ -90,34 +75,21 @@ object ImplicitFunctionConversions { def call(args: java.lang.Object*): R = f(args) } - /** - * Converts a specific function shape (used in takeWhile) to the equivalent Java types with an Rx Func2 - */ implicit def convertTakeWhileFuncToRxFunc2[A](f: (A, Int) => Boolean): Func2[A, jlang.Integer, jlang.Boolean] = new Func2[A, jlang.Integer, jlang.Boolean] { def call(a: A, b: jlang.Integer): jlang.Boolean = f(a, b).booleanValue } - /** - * Converts a function shaped ilke compareTo into the equivalent Rx Func2 - */ implicit def convertComparisonFuncToRxFunc2[A](f: (A, A) => Int): Func2[A, A, jlang.Integer] = new Func2[A, A, jlang.Integer] { def call(a1: A, a2: A): jlang.Integer = f(a1, a2).intValue } - /** - * This implicit allows Scala code to use any exception type and still work - * with invariant Func1 interface - */ implicit def exceptionFunction1ToRxExceptionFunc1[A <: Exception, B](f: (A => B)): Func1[Exception, B] = new Func1[Exception, B] { def call(ex: Exception): B = f(ex.asInstanceOf[A]) } - /** - * The following implicits convert functions of different arities into the Rx equivalents - */ implicit def scalaFunction0ToRxFunc0[A](f: () => A): Func0[A] = new Func0[A] { def call(): A = f() diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala index 62ec5baf3b..27fb82a69e 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala @@ -1,7 +1,7 @@ package rx.lang.scala /** - * Emitted by Observables returned by [[rx.lang.scala.Observable.materialize]]. + * Emitted by Observables returned by [[Observable.materialize]]. */ sealed trait Notification[+T] { def asJava: rx.Notification[_ <: T] diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 1fc26ee3d8..cbbac22aa0 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -19,7 +19,9 @@ package rx.lang.scala /** * The Observable interface that implements the Reactive Pattern. * - * @define subscribeObserver + * @param asJava the underlying Java observable + * + * @define subscribeObserverMain * Call this method to subscribe an [[Observer]] for receiving * items and notifications from the Observable. * @@ -36,14 +38,36 @@ package rx.lang.scala * `Observable[T]` implementation indicates otherwise, Observers should make no * assumptions about the order in which multiple Observers will receive their notifications. * - * @param observer - * the observer - * @param scheduler - * the [[Scheduler]] on which Observers subscribe to the Observable - * @return a [[Subscription]] reference with which the [[Observer]] can stop receiving items + * @define subscribeObserverParamObserver + * the observer + * @define subscribeObserverParamScheduler + * the [[Scheduler]] on which Observers subscribe to the Observable + * @define subscribeAllReturn + * a [[Subscription]] reference whose `unsubscribe` method can be called to stop receiving items * before the Observable has finished sending them - * @throws IllegalArgumentException - * if the [[Observer]] provided as the argument to `subscribe()` is `null` + * + * @define subscribeCallbacksMainWithNotifications + * Call this method to receive items and notifications from this observable. + * + * @define subscribeCallbacksMainNoNotifications + * Call this method to receive items from this observable. + * + * @define subscribeCallbacksParamOnNext + * this function will be called whenever the Observable emits an item + * @define subscribeCallbacksParamOnError + * this function will be called if an error occurs + * @define subscribeCallbacksParamOnComplete + * this function will be called when this Observable has finished emitting items + * @define subscribeCallbacksParamScheduler + * the scheduler to use + * + * @define debounceVsThrottle + * Information on debounce vs throttle: + * - [[http://drupalmotion.com/article/debounce-and-throttle-visual-explanation]] + * - [[http://unscriptable.com/2009/03/20/debouncing-javascript-methods/]] + * - [[http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/]] + * + * */ // constructor is private because users should use apply in companion class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) @@ -62,39 +86,91 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) import rx.lang.scala.ImplicitFunctionConversions._ /** - * $subscribeObserver + * $subscribeObserverMain + * + * @param observer $subscribeObserverParamObserver + * @param scheduler $subscribeObserverParamScheduler + * @return $subscribeAllReturn */ def subscribe(observer: Observer[T], scheduler: Scheduler): Subscription = { asJava.subscribe(observer, scheduler) } /** - * $subscribeObserver + * $subscribeObserverMain + * + * @param observer $subscribeObserverParamObserver + * @return $subscribeAllReturn */ def subscribe(observer: Observer[T]): Subscription = { asJava.subscribe(observer) } - + + /** + * $subscribeCallbacksMainNoNotifications + * + * @param onNext $subscribeCallbacksParamOnNext + * @return $subscribeAllReturn + */ def subscribe(onNext: T => Unit): Subscription = { asJava.subscribe(onNext) } + /** + * $subscribeCallbacksMainWithNotifications + * + * @param onNext $subscribeCallbacksParamOnNext + * @param onError $subscribeCallbacksParamOnError + * @return $subscribeAllReturn + */ def subscribe(onNext: T => Unit, onError: Throwable => Unit): Subscription = { asJava.subscribe(onNext, onError) } + /** + * $subscribeCallbacksMainWithNotifications + * + * @param onNext $subscribeCallbacksParamOnNext + * @param onError $subscribeCallbacksParamOnError + * @param onComplete $subscribeCallbacksParamOnComplete + * @return $subscribeAllReturn + */ def subscribe(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit): Subscription = { asJava.subscribe(onNext, onError, onComplete) } - + + /** + * $subscribeCallbacksMainWithNotifications + * + * @param onNext $subscribeCallbacksParamOnNext + * @param onError $subscribeCallbacksParamOnError + * @param onComplete $subscribeCallbacksParamOnComplete + * @param scheduler $subscribeCallbacksParamScheduler + * @return $subscribeAllReturn + */ def subscribe(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit, scheduler: Scheduler): Subscription = { asJava.subscribe(onNext, onError, onComplete, scheduler) } + /** + * $subscribeCallbacksMainWithNotifications + * + * @param onNext $subscribeCallbacksParamOnNext + * @param onError $subscribeCallbacksParamOnError + * @param scheduler $subscribeCallbacksParamScheduler + * @return $subscribeAllReturn + */ def subscribe(onNext: T => Unit, onError: Throwable => Unit, scheduler: Scheduler): Subscription = { asJava.subscribe(onNext, onError, scheduler) } - + + /** + * $subscribeCallbacksMainNoNotifications + * + * @param onNext $subscribeCallbacksParamOnNext + * @param scheduler $subscribeCallbacksParamScheduler + * @return $subscribeAllReturn + */ def subscribe(onNext: T => Unit, scheduler: Scheduler): Subscription = { asJava.subscribe(onNext, scheduler) } @@ -116,8 +192,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Returns an Observable that first emits the items emitted by this, and then the items emitted - * by that. + * Returns an Observable that first emits the items emitted by `this`, and then the items emitted + * by `that`. * * * @@ -133,13 +209,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the + * Returns an Observable that emits the items emitted by several Observables, one after the * other. - * - * - * - * @return an Observable that emits items that are the result of combining the items emitted by - * the source Observables, one after the other + * + * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, + * otherwise you'll get a compilation error. + * + * @usecase def concat[U]: Observable[U] + * @inheritdoc */ def concat[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this @@ -161,8 +238,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * @param observable * the source Observable - * @tparam T - * the type of item emitted by the source Observable * @return an Observable that is a chronologically well-behaved version of the source * Observable, and that synchronously notifies its [[Observer]]s */ @@ -190,9 +265,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) def zip[U](that: Observable[U]): Observable[(T, U)] = { Observable[(T, U)](JObservable.zip[T, U, (T, U)](this.asJava, that.asJava, (t: T, u: U) => (t, u))) } - - // public static [R] Observable[R] zip(Observable> ws, final FuncN zipFunction) { - + /** * Zips this Observable with its indices. * @@ -212,7 +285,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * emitted and replaced with a new buffer when the Observable produced by the specified function produces a [[rx.lang.scala.util.Closing]] object. The function will then * be used to create a new Observable to listen for the end of the next buffer. * - * @param bufferClosingSelector + * @param closings * The function which is used to produce an [[Observable]] for every buffer created. * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer * is emitted and replaced with a new one. @@ -220,8 +293,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * An [[Observable]] which produces connected non-overlapping buffers, which are emitted * when the current [[Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. */ - def buffer(bufferClosingSelector: () => Observable[Closing]) : Observable[Seq[T]] = { - val f: Func0[_ <: rx.Observable[_ <: Closing]] = bufferClosingSelector().asJava + def buffer(closings: () => Observable[Closing]) : Observable[Seq[T]] = { + val f: Func0[_ <: rx.Observable[_ <: Closing]] = closings().asJava val jObs: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(f) Observable.jObsOfListToScObsOfSeq(jObs.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } @@ -229,24 +302,24 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. * - * This Observable produces buffers. Buffers are created when the specified "bufferOpenings" + * This Observable produces buffers. Buffers are created when the specified `openings` * Observable produces a [[rx.lang.scala.util.Opening]] object. Additionally the function argument * is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. When this * Observable produces such an object, the associated buffer is emitted. * - * @param bufferOpenings + * @param openings * The [[Observable]] which, when it produces a [[rx.lang.scala.util.Opening]] object, will cause * another buffer to be created. - * @param bufferClosingSelector + * @param closings * The function which is used to produce an [[Observable]] for every buffer created. * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer * is emitted. * @return * An [[Observable]] which produces buffers which are created and emitted when the specified [[Observable]]s publish certain objects. */ - def buffer(bufferOpenings: Observable[Opening], bufferClosingSelector: Opening => Observable[Closing]): Observable[Seq[T]] = { - val opening: rx.Observable[_ <: Opening] = bufferOpenings.asJava - val closing: Func1[Opening, _ <: rx.Observable[_ <: Closing]] = (o: Opening) => bufferClosingSelector(o).asJava + def buffer(openings: Observable[Opening], closings: Opening => Observable[Closing]): Observable[Seq[T]] = { + val opening: rx.Observable[_ <: Opening] = openings.asJava + val closing: Func1[Opening, _ <: rx.Observable[_ <: Closing]] = (o: Opening) => closings(o).asJava val jObs: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(opening, closing) Observable.jObsOfListToScObsOfSeq(jObs.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } @@ -254,7 +327,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. * - * This Observable produces connected non-overlapping buffers, each containing "count" + * This Observable produces connected non-overlapping buffers, each containing `count` * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. * @@ -262,7 +335,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * The maximum size of each buffer before it should be emitted. * @return * An [[Observable]] which produces connected non-overlapping buffers containing at most - * "count" produced values. + * `count` produced values. */ def buffer(count: Int): Observable[Seq[T]] = { val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(count) @@ -272,18 +345,18 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. * - * This Observable produces buffers every "skip" values, each containing "count" + * This Observable produces buffers every `skip` values, each containing `count` * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. * * @param count * The maximum size of each buffer before it should be emitted. * @param skip - * How many produced values need to be skipped before starting a new buffer. Note that when "skip" and - * "count" are equals that this is the same operation as `buffer(int)`. + * How many produced values need to be skipped before starting a new buffer. Note that when `skip` and + * `count` are equals that this is the same operation as `buffer(int)`. * @return - * An [[Observable]] which produces buffers every "skipped" values containing at most - * "count" produced values. + * An [[Observable]] which produces buffers every `skip` values containing at most + * `count` produced values. */ def buffer(count: Int, skip: Int): Observable[Seq[T]] = { val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(count, skip) @@ -331,7 +404,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. This Observable produces connected * non-overlapping buffers, each of a fixed duration specified by the `timespan` argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes + * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. * * @param timespan @@ -351,7 +424,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces buffers of collected values. This Observable produces connected * non-overlapping buffers, each of a fixed duration specified by the `timespan` argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes + * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. * * @param timespan @@ -419,10 +492,11 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows. The current window is emitted and replaced with a new window when the - * Observable produced by the specified function produces a [[rx.lang.scala.util.Closing]] object. The function will then be used to create a new Observable to listen for the end of the next + * Observable produced by the specified function produces a [[rx.lang.scala.util.Closing]] object. + * The function will then be used to create a new Observable to listen for the end of the next * window. * - * @param closingSelector + * @param closings * The function which is used to produce an [[Observable]] for every window created. * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window * is emitted and replaced with a new one. @@ -430,8 +504,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * An [[Observable]] which produces connected non-overlapping windows, which are emitted * when the current [[Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. */ - def window(closingSelector: () => Observable[Closing]): Observable[Observable[T]] = { - val func : Func0[_ <: rx.Observable[_ <: Closing]] = closingSelector().asJava + def window(closings: () => Observable[Closing]): Observable[Observable[T]] = { + val func : Func0[_ <: rx.Observable[_ <: Closing]] = closings().asJava val o1: rx.Observable[_ <: rx.Observable[_]] = asJava.window(func) val o2 = new Observable[rx.Observable[_]](o1).map((x: rx.Observable[_]) => { val x2 = x.asInstanceOf[rx.Observable[_ <: T]] @@ -442,36 +516,36 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces windows. - * Chunks are created when the specified "windowOpenings" Observable produces a [[rx.lang.scala.util.Opening]] object. - * Additionally the `closingSelector` argument is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. When this Observable produces such an object, the associated window is - * emitted. + * Chunks are created when the specified `openings` Observable produces a [[rx.lang.scala.util.Opening]] object. + * Additionally the `closings` argument is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. + * When this Observable produces such an object, the associated window is emitted. * - * @param windowOpenings + * @param openings * The [[Observable]] which when it produces a [[rx.lang.scala.util.Opening]] object, will cause * another window to be created. - * @param closingSelector + * @param closings * The function which is used to produce an [[Observable]] for every window created. * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window * is emitted. * @return * An [[Observable]] which produces windows which are created and emitted when the specified [[Observable]]s publish certain objects. */ - def window(windowOpenings: Observable[Opening], closingSelector: Opening => Observable[Closing]) = { + def window(openings: Observable[Opening], closings: Opening => Observable[Closing]) = { Observable.jObsOfJObsToScObsOfScObs( - asJava.window(windowOpenings.asJava, (op: Opening) => closingSelector(op).asJava)) + asJava.window(openings.asJava, (op: Opening) => closings(op).asJava)) : Observable[Observable[T]] // SI-7818 } /** * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each containing "count" elements. When the source Observable completes or + * non-overlapping windows, each containing `count` elements. When the source Observable completes or * encounters an error, the current window is emitted, and the event is propagated. * * @param count * The maximum size of each window before it should be emitted. * @return * An [[Observable]] which produces connected non-overlapping windows containing at most - * "count" produced values. + * `count` produced values. */ def window(count: Int): Observable[Observable[T]] = { // this unnecessary ascription is needed because of this bug (without, compiler crashes): @@ -490,7 +564,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * How many produced values need to be skipped before starting a new window. Note that when `skip` and * `count` are equal that this is the same operation as `window(int)`. * @return - * An [[Observable]] which produces windows every "skipped" values containing at most + * An [[Observable]] which produces windows every `skip` values containing at most * `count` produced values. */ def window(count: Int, skip: Int): Observable[Observable[T]] = { @@ -535,7 +609,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the `timespan` argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes + * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current window is emitted and the event is propagated. * * @param timespan @@ -555,7 +629,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the `timespan` argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes + * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current window is emitted and the event is propagated. * * @param timespan @@ -642,7 +716,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param action * an function to be invoked when the source Observable finishes * @return an Observable that emits the same items as the source Observable, then invokes the function - * @see MSDN: Observable.Finally Method */ def finallyDo(action: () => Unit): Observable[T] = { Observable[T](asJava.finallyDo(action)) @@ -688,7 +761,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * @return an Observable whose items are the result of materializing the items and * notifications of the source Observable - * @see MSDN: Observable.materialize */ def materialize: Observable[Notification[T]] = { Observable[rx.Notification[_ <: T]](asJava.materialize()).map(Notification(_)) @@ -726,10 +798,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Returns an Observable that reverses the effect of [[Observable.materialize]] by * transforming the [[Notification]] objects emitted by the source Observable into the items * or notifications they represent. + * + * This operation is only available if `this` is of type `Observable[Notification[U]]` for some `U`, + * otherwise you will get a compilation error. * * * * @return an Observable that emits the items and notifications embedded in the [[Notification]] objects emitted by the source Observable + * + * @usecase def dematerialize[U]: Observable[U] + * @inheritdoc + * */ // with =:= it does not work, why? def dematerialize[U](implicit evidence: Observable[T] <:< Observable[Notification[U]]): Observable[U] = { @@ -751,7 +830,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * function that returns an Observable (`resumeFunction`) to * `onErrorResumeNext`, if the original Observable encounters an error, instead of * invoking its Observer's `onError` method, it will instead relinquish control to - * the Observable returned from `resumeFunction`, which will invoke the Observer's [[Observer.onNext onNext]] method if it is able to do so. In such a case, because no + * the Observable returned from `resumeFunction`, which will invoke the Observer's + * [[Observer.onNext onNext]] method if it is able to do so. In such a case, because no * Observable necessarily invokes `onError`, the Observer may never know that an * error happened. * @@ -781,7 +861,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * another Observable (`resumeSequence`) to an Observable's * `onErrorResumeNext` method, if the original Observable encounters an error, * instead of invoking its Observer's `onError` method, it will instead relinquish - * control to `resumeSequence` which will invoke the Observer's [[Observer.onNext onNext]] method if it is able to do so. In such a case, because no + * control to `resumeSequence` which will invoke the Observer's [[Observer.onNext onNext]] + * method if it is able to do so. In such a case, because no * Observable necessarily invokes `onError`, the Observer may never know that an * error happened. * @@ -813,7 +894,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * another Observable (`resumeSequence`) to an Observable's * `onErrorResumeNext` method, if the original Observable encounters an error, * instead of invoking its Observer's `onError` method, it will instead relinquish - * control to `resumeSequence` which will invoke the Observer's [[Observer.onNext onNext]] method if it is able to do so. In such a case, because no + * control to `resumeSequence` which will invoke the Observer's [[Observer.onNext onNext]] + * method if it is able to do so. In such a case, because no * Observable necessarily invokes `onError`, the Observer may never know that an * error happened. * @@ -878,8 +960,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Observable, whose result will be used in the next accumulator call * @return an Observable that emits a single item that is the result of accumulating the * output from the source Observable - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) */ def reduce[U >: T](f: (U, U) => U): Observable[U] = { val func: Func2[_ >: U, _ >: U, _ <: U] = f @@ -957,8 +1037,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Observable, the result of which will be used in the next accumulator call * @return an Observable that emits a single item that is the result of accumulating the output * from the items emitted by the source Observable - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) */ def fold[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { Observable[R](asJava.reduce(initialValue, accumulator)) @@ -1019,7 +1097,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * an accumulator function to be invoked on each item emitted by the source * Observable, whose result will be emitted to [[Observer]]s via [[Observer.onNext onNext]] and used in the next accumulator call. * @return an Observable that emits the results of each call to the accumulator function - * @see MSDN: Observable.Scan */ def scan[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { Observable[R](asJava.scan(initialValue, accumulator)) @@ -1049,9 +1126,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * You can ignore the first `num` items emitted by an Observable and attend only to - * those items that come after, by modifying the Observable with the `skip` method. - * * @param num * the number of items to skip * @return an Observable that is identical to the source Observable except that it does not @@ -1082,7 +1156,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * This method returns an Observable that will invoke a subscribing [[Observer]]'s [[Observer.onNext onNext]] function a maximum of `num` times before invoking + * This method returns an Observable that will invoke a subscribing [[Observer]]'s + * [[Observer.onNext onNext]] function a maximum of `num` times before invoking * [[Observer.onCompleted onCompleted]]. * * @param num @@ -1168,7 +1243,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * Normally, an Observable that returns multiple items will do so by invoking its [[Observer]]'s [[Observer.onNext onNext]] method for each such item. You can change + * Normally, an Observable that returns multiple items will do so by invoking its [[Observer]]'s + * [[Observer.onNext onNext]] method for each such item. You can change * this behavior, instructing the Observable to compose a list of all of these items and then to * invoke the Observer's `onNext` function once, passing it the entire list, by * calling the Observable's `toList` method prior to calling its `Observable.subscribe` method. @@ -1206,10 +1282,16 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * + * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, + * otherwise you'll get a compilation error. + * * @param sequenceOfSequences * the source Observable that emits Observables * @return an Observable that emits only the items emitted by the most recently published * Observable + * + * @usecase def switch[U]: Observable[U] + * @inheritdoc */ def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this @@ -1271,8 +1353,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * You can combine the items emitted by multiple Observables so that they act like a single * Observable by using this method. * + * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, + * otherwise you'll get a compilation error. + * * @return an Observable that emits items that are the result of flattening the items emitted * by the Observables emitted by `this` + * + * @usecase def flatten[U]: Observable[U] + * @inheritdoc */ def flatten[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this @@ -1283,7 +1371,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * This behaves like [[Observable.flatten]] except that if any of the merged Observables + * This behaves like `flatten` except that if any of the merged Observables * notify of an error via [[Observer.onError onError]], this method will * refrain from propagating that error notification until all of the merged Observables have * finished emitting items. @@ -1295,9 +1383,15 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. + * + * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, + * otherwise you'll get a compilation error. * * @return an Observable that emits items that are the result of flattening the items emitted by * the Observables emitted by the this Observable + * + * @usecase def flattenDelayError[U]: Observable[U] + * @inheritdoc */ def flattenDelayError[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this @@ -1328,11 +1422,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * Information on debounce vs throttle: - * - * - http://drupalmotion.com/article/debounce-and-throttle-visual-explanation - * - http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ - * - http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ + * $debounceVsThrottle * * @param timeout * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. @@ -1351,11 +1441,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * Information on debounce vs throttle: - * - * - http://drupalmotion.com/article/debounce-and-throttle-visual-explanation - * - http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ - * - http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ + * $debounceVsThrottle * * @param timeout * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. @@ -1374,11 +1460,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * Information on debounce vs throttle: - * - * - http://drupalmotion.com/article/debounce-and-throttle-visual-explanation - * - http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ - * - http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/ + * $debounceVsThrottle * * @param timeout * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. @@ -1474,8 +1556,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that sums up the elements of this Observable. * + * This operation is only available if the elements of this Observable are numbers, otherwise + * you will get a compilation error. + * * @return an Observable emitting the sum of all the elements of the source Observable * as its single item. + * + * @usecase def sum: Observable[T] + * @inheritdoc */ def sum[U >: T](implicit num: Numeric[U]): Observable[U] = { fold(num.zero)(num.plus) @@ -1484,8 +1572,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) /** * Returns an Observable that multiplies up the elements of this Observable. * + * This operation is only available if the elements of this Observable are numbers, otherwise + * you will get a compilation error. + * * @return an Observable emitting the product of all the elements of the source Observable * as its single item. + * + * @usecase def product: Observable[T] + * @inheritdoc */ def product[U >: T](implicit num: Numeric[U]): Observable[U] = { fold(num.one)(num.times) @@ -1678,7 +1772,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Perform work in parallel by sharding an `Observable[T]` on a [[rx.lang.scala.concurrency.Schedulers.threadPoolForComputation]] [[Scheduler]] and return an `Observable[R]` with the output. + * Perform work in parallel by sharding an `Observable[T]` on a + * [[rx.lang.scala.concurrency.Schedulers.threadPoolForComputation computation]] + * [[Scheduler]] and return an `Observable[R]` with the output. * * @param f * a function that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` @@ -1753,7 +1849,10 @@ object Observable { val oScala1: Observable[rx.Observable[_ <: T]] = new Observable[rx.Observable[_ <: T]](jObs) oScala1.map((oJava: rx.Observable[_ <: T]) => new Observable[T](oJava)) } - + + /** + * Creates a new Scala Observable from a given Java Observable. + */ def apply[T](asJava: rx.Observable[_ <: T]): Observable[T] = { new Observable[T](asJava) } @@ -1807,7 +1906,8 @@ object Observable { * * * - * Implementation note: the entire array will be immediately emitted each time an [[Observer]] subscribes. Since this occurs before the [[Subscription]] is returned, + * Implementation note: the entire array will be immediately emitted each time an [[Observer]] subscribes. + * Since this occurs before the [[Subscription]] is returned, * it in not possible to unsubscribe from the sequence before it completes. * * @param items @@ -1839,10 +1939,9 @@ object Observable { /** * Returns an Observable that calls an Observable factory to create its Observable for each - * new Observer that subscribes. That is, for each subscriber, the actuall Observable is determined + * new Observer that subscribes. That is, for each subscriber, the actual Observable is determined * by the factory function. * - * * * * The defer operator allows you to defer or delay emitting items from an Observable until such @@ -1920,7 +2019,7 @@ object Observable { } /** - * Emits 0, 1, 2, ... with a delay of `duration` between consecutive numbers. + * Emits `0`, `1`, `2`, `...` with a delay of `duration` between consecutive numbers. * * * @@ -1933,7 +2032,7 @@ object Observable { } /** - * Emits 0, 1, 2, ... with a delay of `duration` between consecutive numbers. + * Emits `0`, `1`, `2`, `...` with a delay of `duration` between consecutive numbers. * * * diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala index 12f452db59..c717a94af5 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala @@ -129,7 +129,7 @@ trait Scheduler { } /** - * @return the scheduler's notion of current absolute time in milliseconds. + * Returns the scheduler's notion of current absolute time in milliseconds. */ def now: Long = { asJava.now diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala index 8a2241d086..8507f0a54c 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala @@ -3,7 +3,7 @@ package rx.lang.scala /** * Contains special Observables. * - * In Scala, this package only contains [[rx.lang.scala.observables.BlockingObservable]]. + * In Scala, this package only contains [[BlockingObservable]]. * In the corresponding Java package `rx.observables`, there is also a * `GroupedObservable` and a `ConnectableObservable`, but these are not needed * in Scala, because we use a pair `(key, observable)` instead of `GroupedObservable` diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala index 9356b08f6b..8aa0e63760 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala @@ -50,40 +50,40 @@ package object scala { /** * Provides a mechanism for receiving push-based notifications. * - * After an Observer calls an [[rx.lang.scala.Observable]]'s `subscribe` method, the Observable + * After an Observer calls an [[Observable]]'s `subscribe` method, the Observable * calls the Observer's `onNext` method to provide notifications. A well-behaved Observable will * call an Observer's `onCompleted` method exactly once or the Observer's `onError` method exactly once. */ trait Observer[-T] { /** - * Notifies the Observer that the [[rx.lang.scala.Observable]] has finished sending push-based notifications. + * Notifies the Observer that the [[Observable]] has finished sending push-based notifications. * - * The [[rx.lang.scala.Observable]] will not call this method if it calls `onError`. + * The [[Observable]] will not call this method if it calls `onError`. */ def onCompleted(): Unit /** - * Notifies the Observer that the [[rx.lang.scala.Observable]] has experienced an error condition. + * Notifies the Observer that the [[Observable]] has experienced an error condition. * - * If the [[rx.lang.scala.Observable]] calls this method, it will not thereafter call `onNext` or `onCompleted`. + * If the [[Observable]] calls this method, it will not thereafter call `onNext` or `onCompleted`. */ def onError(e: Throwable): Unit /** * Provides the Observer with new data. * - * The [[rx.lang.scala.Observable]] calls this closure 0 or more times. + * The [[Observable]] calls this closure 0 or more times. * - * The [[rx.lang.scala.Observable]] will not call this method again after it calls either `onCompleted` or `onError`. + * The [[Observable]] will not call this method again after it calls either `onCompleted` or `onError`. */ def onNext(arg: T): Unit } /** - * Subscriptions are returned from all Observable.subscribe methods to allow unsubscribing. + * Subscriptions are returned from all `Observable.subscribe` methods to allow unsubscribing. * - * This interface is the equivalent of IDisposable in the .NET Rx implementation. + * This interface is the equivalent of `IDisposable` in the .NET Rx implementation. */ trait Subscription { /** @@ -115,13 +115,13 @@ package object scala { private[scala] implicit def rxObserver2fakeObserver[T](o: rx.Observer[_ >: T]): Observer[T] = ??? *///#else - + type Observer[-T] = rx.Observer[_ >: T] type Subscription = rx.Subscription - + //#endif - + /** * Allows to construct observables in a similar way as futures. * diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala index 7913808794..ed19d849ab 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala @@ -16,7 +16,7 @@ package rx.lang.scala /** - * Provides [[rx.lang.scala.util.Opening]]s, [[rx.lang.scala.util.Closing]]s, and [[rx.lang.scala.util.Timestamped]]. + * Provides [[Opening]]s, [[Closing]]s, and [[Timestamped]]. */ package object util { From 01f54644229db8061d1a507abe5dd18b169ec317 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 2 Oct 2013 17:32:40 +0200 Subject: [PATCH 136/333] rename fold to foldLeft --- .../scala/rx/lang/scala/examples/RxScalaDemo.scala | 2 +- .../src/main/scala/rx/lang/scala/Observable.scala | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index a449737acf..cb59c7246b 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -289,7 +289,7 @@ class RxScalaDemo extends JUnitSuite { // We can't put a general average method into Observable.scala, because Scala's Numeric // does not have scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum) def doubleAverage(o: Observable[Double]): Observable[Double] = { - for ((finalSum, finalCount) <- o.fold((0.0, 0))({case ((sum, count), elem) => (sum+elem, count+1)})) + for ((finalSum, finalCount) <- o.foldLeft((0.0, 0))({case ((sum, count), elem) => (sum+elem, count+1)})) yield finalSum / finalCount } diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index cbbac22aa0..8e6a2d658e 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1038,7 +1038,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable that emits a single item that is the result of accumulating the output * from the items emitted by the source Observable */ - def fold[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { + def foldLeft[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { Observable[R](asJava.reduce(initialValue, accumulator)) } @@ -1117,7 +1117,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) // type mismatch; found : rx.Observable[java.lang.Boolean] required: rx.Observable[_ <: scala.Boolean] // new Observable[Boolean](asJava.all(predicate)) // it's more fun in Scala: - this.map(predicate).fold(true)(_ && _) + this.map(predicate).foldLeft(true)(_ && _) } /** @@ -1566,7 +1566,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @inheritdoc */ def sum[U >: T](implicit num: Numeric[U]): Observable[U] = { - fold(num.zero)(num.plus) + foldLeft(num.zero)(num.plus) } /** @@ -1582,7 +1582,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @inheritdoc */ def product[U >: T](implicit num: Numeric[U]): Observable[U] = { - fold(num.one)(num.times) + foldLeft(num.one)(num.times) } /** @@ -1598,7 +1598,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * if the source Observable completes without emitting any item. */ def firstOrElse[U >: T](default: => U): Observable[U] = { - this.take(1).fold[Option[U]](None)((v: Option[U], e: U) => Some(e)).map({ + this.take(1).foldLeft[Option[U]](None)((v: Option[U], e: U) => Some(e)).map({ case Some(element) => element case None => default }) From f3734bfad009063f75be0bb75f5379dd5bedd593 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 2 Oct 2013 17:37:40 +0200 Subject: [PATCH 137/333] remove takeWhileWithIndex --- .../rx/lang/scala/examples/RxScalaDemo.scala | 5 +++++ .../main/scala/rx/lang/scala/Observable.scala | 17 ----------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index cb59c7246b..950081e8ce 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -454,6 +454,11 @@ class RxScalaDemo extends JUnitSuite { Thread.sleep(1500) // or convert to BlockingObservable } + @Test def takeWhileWithIndexAlternative { + val condition = true + Observable("a", "b").zipWithIndex.takeWhile{case (elem, index) => condition}.map(_._1) + } + def output(s: String): Unit = println(s) // blocks until obs has completed diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 8e6a2d658e..776640357b 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1185,23 +1185,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) def takeWhile(predicate: T => Boolean): Observable[T] = { Observable[T](asJava.takeWhile(predicate)) } - - /** - * Returns an Observable that emits the items emitted by a source Observable so long as a given - * predicate remains true, where the predicate can operate on both the item and its index - * relative to the complete sequence. - * - * - * - * @param predicate - * a function to test each item emitted by the source Observable for a condition; - * the second parameter of the function represents the index of the source item - * @return an Observable that emits items from the source Observable so long as the predicate - * continues to return `true` for each item, then completes - */ - def takeWhileWithIndex(predicate: (T, Integer) => Boolean): Observable[T] = { - Observable[T](asJava.takeWhileWithIndex(predicate)) - } /** * Returns an Observable that emits only the last `count` items emitted by the source From c66b2ebabfd695b8060b8614c73a22442cb4a4a1 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 2 Oct 2013 17:43:58 +0200 Subject: [PATCH 138/333] use Tuple instead of Timestamped --- .../rx/lang/scala/examples/RxScalaDemo.scala | 3 +- .../main/scala/rx/lang/scala/Observable.scala | 7 ++-- .../rx/lang/scala/util/Timestamped.scala | 34 ------------------- 3 files changed, 5 insertions(+), 39 deletions(-) delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index 950081e8ce..fe1747a1e6 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -22,7 +22,6 @@ import scala.concurrent.duration._ import org.junit.{Before, Test, Ignore} import org.junit.Assert._ import rx.lang.scala.concurrency.Schedulers -import rx.lang.scala.util.Timestamped import java.io.IOException @Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily @@ -379,7 +378,7 @@ class RxScalaDemo extends JUnitSuite { @Test def timestampExample() { val timestamped = Observable.interval(100 millis).take(3).timestamp.toBlockingObservable - for (Timestamped(millis, value) <- timestamped if value > 0) { + for ((millis, value) <- timestamped if value > 0) { println(value + " at t = " + millis) } } diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 776640357b..d845a65fa5 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -246,14 +246,15 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) } /** - * Wraps each item emitted by a source Observable in a [[rx.lang.scala.util.Timestamped]] object. + * Wraps each item emitted by a source Observable in a timestamped tuple. * * * * @return an Observable that emits timestamped items from the source Observable */ - def timestamp: Observable[Timestamped[T]] = { - Observable[rx.util.Timestamped[_ <: T]](asJava.timestamp()).map(Timestamped(_)) + def timestamp: Observable[(Long, T)] = { + Observable[rx.util.Timestamped[_ <: T]](asJava.timestamp()) + .map((t: rx.util.Timestamped[_ <: T]) => (t.getTimestampMillis, t.getValue())) } /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala deleted file mode 100644 index e00b9bb17e..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/Timestamped.scala +++ /dev/null @@ -1,34 +0,0 @@ -package rx.lang.scala.util - -/** - * Wraps a value and a timestamp. - */ -// constructor is private because users should use apply from companion -class Timestamped[+T] private[util] (val asJava: rx.util.Timestamped[_ <: T]) extends AnyVal { - /** - * Returns the timestamp, in milliseconds. - */ - def millis: Long = asJava.getTimestampMillis - - /** - * Returns the value. - */ - def value: T = asJava.getValue : T -} - -/** - * Provides constructor and pattern matching functionality for `Timestamped`. - */ -object Timestamped { - def apply[T](timestampMillis: Long, value: T): Timestamped[T] = { - new Timestamped(new rx.util.Timestamped(timestampMillis, value)) - } - - def apply[T](asJava: rx.util.Timestamped[_ <: T]): Timestamped[T] = { - new Timestamped(asJava) - } - - def unapply[T](v: Timestamped[T]): Option[(Long, T)] = { - Some((v.millis, v.value)) - } -} From 677f84c4c37186367e448652f5e364120ecf4c9e Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Wed, 2 Oct 2013 17:53:42 +0200 Subject: [PATCH 139/333] update completeness test --- .../scala/rx/lang/scala/CompletenessTest.scala | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index cf22630a3a..d9c26c54c0 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -22,11 +22,14 @@ class CompletenessTest extends JUnitSuite { "You can use `fold` instead to accumulate `sum` and `numberOfElements` and divide at the end.]" val commentForFirstWithPredicate = "[use `.filter(condition).first`]" + + val fromFuture = "[TODO: Decide how Scala Futures should relate to Observables. Should there be a " + + "common base interface for Future and Observable? And should Futures also have an unsubscribe method?]" val correspondence = defaultMethodCorrespondence ++ Map( // manually added entries for Java instance methods "aggregate(Func2[T, T, T])" -> "reduce((U, U) => U)", - "aggregate(R, Func2[R, _ >: T, R])" -> "fold(R)((R, T) => R)", + "aggregate(R, Func2[R, _ >: T, R])" -> "foldLeft(R)((R, T) => R)", "all(Func1[_ >: T, Boolean])" -> "forall(T => Boolean)", "buffer(Long, Long, TimeUnit)" -> "buffer(Duration, Duration)", "buffer(Long, Long, TimeUnit, Scheduler)" -> "buffer(Duration, Duration, Scheduler)", @@ -47,7 +50,7 @@ class CompletenessTest extends JUnitSuite { "parallel(Func1[Observable[T], Observable[R]])" -> "parallel(Observable[T] => Observable[R])", "parallel(Func1[Observable[T], Observable[R]], Scheduler)" -> "parallel(Observable[T] => Observable[R], Scheduler)", "reduce(Func2[T, T, T])" -> "reduce((U, U) => U)", - "reduce(R, Func2[R, _ >: T, R])" -> "fold(R)((R, T) => R)", + "reduce(R, Func2[R, _ >: T, R])" -> "foldLeft(R)((R, T) => R)", "scan(Func2[T, T, T])" -> unnecessary, "scan(R, Func2[R, _ >: T, R])" -> "scan(R)((R, T) => R)", "skip(Int)" -> "drop(Int)", @@ -57,6 +60,7 @@ class CompletenessTest extends JUnitSuite { "takeFirst()" -> "first", "takeFirst(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, "takeLast(Int)" -> "takeRight(Int)", + "takeWhileWithIndex(Func2[_ >: T, _ >: Integer, Boolean])" -> "[use `.zipWithIndex.takeWhile{case (elem, index) => condition}.map(_._1)`]", "toList()" -> "toSeq", "toSortedList()" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sorted)`]", "toSortedList(Func2[_ >: T, _ >: T, Integer])" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sortWith(f))`]", @@ -77,9 +81,10 @@ class CompletenessTest extends JUnitSuite { "error(Throwable)" -> "apply(Throwable)", "from(Array[T])" -> "apply(T*)", "from(Iterable[_ <: T])" -> "apply(T*)", - "from(Future[_ <: T])" -> "apply(Future[T])", - "from(Future[_ <: T], Long, TimeUnit)" -> "apply(Future[T], Duration)", - "from(Future[_ <: T], Scheduler)" -> "apply(Future[T], Scheduler)", + "from(Future[_ <: T])" -> fromFuture, + "from(Future[_ <: T], Long, TimeUnit)" -> fromFuture, + "from(Future[_ <: T], Scheduler)" -> fromFuture, + "just(T)" -> "apply(T*)", "merge(Observable[_ <: T], Observable[_ <: T])" -> "merge(Observable[U])", "merge(Observable[_ <: Observable[_ <: T]])" -> "flatten(<:<[Observable[T], Observable[Observable[U]]])", "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[U])", @@ -298,7 +303,7 @@ class CompletenessTest extends JUnitSuite { (if (p._1.startsWith("average")) "average" else p._1.takeWhile(_ != '('), p._2) def formatJavaCol(name: String, alternatives: Iterable[String]): String = { alternatives.toList.sorted.map(scalaToJavaSignature(_)).map(s => { - if (s.length > 50) { + if (s.length > 64) { val toolTip = escapeJava(s) "" + name + "(...)" } else { From 99bbfd375ae19d4c99810300a2ae7f734664577c Mon Sep 17 00:00:00 2001 From: Mike Ragalie Date: Sun, 22 Sep 2013 21:25:49 -0700 Subject: [PATCH 140/333] Implement JRuby function wrapping support Whenever a RubyProc is passed into an Observable method, wrap it in a Java class that implements the correct RxJava interfaces. This avoids ambiguous method errors and costly proxy instantiations by JRuby's default method delegation logic. --- language-adaptors/rxjava-jruby/build.gradle | 18 ++ .../rx/lang/jruby/JRubyActionWrapper.java | 77 ++++++++ .../rx/lang/jruby/JRubyFunctionWrapper.java | 173 ++++++++++++++++++ .../main/resources/rx/lang/jruby/interop.rb | 81 ++++++++ settings.gradle | 1 + 5 files changed, 350 insertions(+) create mode 100644 language-adaptors/rxjava-jruby/build.gradle create mode 100644 language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyActionWrapper.java create mode 100644 language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyFunctionWrapper.java create mode 100644 language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb diff --git a/language-adaptors/rxjava-jruby/build.gradle b/language-adaptors/rxjava-jruby/build.gradle new file mode 100644 index 0000000000..ef2175fa98 --- /dev/null +++ b/language-adaptors/rxjava-jruby/build.gradle @@ -0,0 +1,18 @@ +apply plugin: 'osgi' + +dependencies { + compile project(':rxjava-core') + compile 'org.jruby:jruby:1.7+' + provided 'junit:junit-dep:4.10' + provided 'org.mockito:mockito-core:1.8.5' +} + +jar { + manifest { + name = 'rxjava-jruby' + instruction 'Bundle-Vendor', 'Netflix' + instruction 'Bundle-DocURL', 'https://github.com/Netflix/RxJava' + instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*' + instruction 'Fragment-Host', 'com.netflix.rxjava.core' + } +} diff --git a/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyActionWrapper.java b/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyActionWrapper.java new file mode 100644 index 0000000000..6beb4f1d59 --- /dev/null +++ b/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyActionWrapper.java @@ -0,0 +1,77 @@ +/** + * Copyright 2013 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.lang.jruby; + +import org.jruby.RubyProc; +import org.jruby.Ruby; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.javasupport.JavaUtil; + +import rx.util.functions.Action; +import rx.util.functions.Action0; +import rx.util.functions.Action1; +import rx.util.functions.Action2; +import rx.util.functions.Action3; + +/** + * Concrete wrapper that accepts a {@link RubyProc} and produces any needed Rx {@link Action}. + * + * @param + * @param + * @param + * @param + */ +public class JRubyActionWrapper implements Action, Action0, Action1, Action2, Action3 { + + private final RubyProc proc; + private final ThreadContext context; + private final Ruby runtime; + + public JRubyActionWrapper(ThreadContext context, RubyProc proc) { + this.proc = proc; + this.context = context; + this.runtime = context.getRuntime(); + } + + @Override + public void call() { + IRubyObject[] array = new IRubyObject[0]; + proc.call(context, array); + } + + @Override + public void call(T1 t1) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1)}; + proc.call(context, array); + } + + @Override + public void call(T1 t1, T2 t2) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), + JavaUtil.convertJavaToRuby(runtime, t2)}; + proc.call(context, array); + } + + @Override + public void call(T1 t1, T2 t2, T3 t3) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), + JavaUtil.convertJavaToRuby(runtime, t2), + JavaUtil.convertJavaToRuby(runtime, t3)}; + proc.call(context, array); + } + +} diff --git a/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyFunctionWrapper.java b/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyFunctionWrapper.java new file mode 100644 index 0000000000..f049827d8e --- /dev/null +++ b/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyFunctionWrapper.java @@ -0,0 +1,173 @@ +/** + * Copyright 2013 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.lang.jruby; + +import org.jruby.RubyProc; +import org.jruby.Ruby; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.javasupport.JavaUtil; + +import rx.util.functions.Func0; +import rx.util.functions.Func1; +import rx.util.functions.Func2; +import rx.util.functions.Func3; +import rx.util.functions.Func4; +import rx.util.functions.Func5; +import rx.util.functions.Func6; +import rx.util.functions.Func7; +import rx.util.functions.Func8; +import rx.util.functions.Func9; +import rx.util.functions.FuncN; +import rx.util.functions.Function; + +/** + * Concrete wrapper that accepts a {@link RubyProc} and produces any needed Rx {@link Function}. + * + * @param + * @param + * @param + * @param + * @param + */ +public class JRubyFunctionWrapper implements + Func0, + Func1, + Func2, + Func3, + Func4, + Func5, + Func6, + Func7, + Func8, + Func9, + FuncN { + + private final RubyProc proc; + private final ThreadContext context; + private final Ruby runtime; + + public JRubyFunctionWrapper(ThreadContext context, RubyProc proc) { + this.proc = proc; + this.context = context; + this.runtime = context.getRuntime(); + } + + @Override + public R call() { + IRubyObject[] array = new IRubyObject[0]; + return (R) proc.call(context, array); + } + + @Override + public R call(T1 t1) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1)}; + return (R) proc.call(context, array); + } + + @Override + public R call(T1 t1, T2 t2) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), + JavaUtil.convertJavaToRuby(runtime, t2)}; + return (R) proc.call(context, array); + } + + @Override + public R call(T1 t1, T2 t2, T3 t3) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), + JavaUtil.convertJavaToRuby(runtime, t2), + JavaUtil.convertJavaToRuby(runtime, t3)}; + return (R) proc.call(context, array); + } + + @Override + public R call(T1 t1, T2 t2, T3 t3, T4 t4) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), + JavaUtil.convertJavaToRuby(runtime, t2), + JavaUtil.convertJavaToRuby(runtime, t3), + JavaUtil.convertJavaToRuby(runtime, t4)}; + return (R) proc.call(context, array); + } + + @Override + public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), + JavaUtil.convertJavaToRuby(runtime, t2), + JavaUtil.convertJavaToRuby(runtime, t3), + JavaUtil.convertJavaToRuby(runtime, t4), + JavaUtil.convertJavaToRuby(runtime, t5)}; + return (R) proc.call(context, array); + } + + @Override + public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), + JavaUtil.convertJavaToRuby(runtime, t2), + JavaUtil.convertJavaToRuby(runtime, t3), + JavaUtil.convertJavaToRuby(runtime, t4), + JavaUtil.convertJavaToRuby(runtime, t5), + JavaUtil.convertJavaToRuby(runtime, t6)}; + return (R) proc.call(context, array); + } + + @Override + public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), + JavaUtil.convertJavaToRuby(runtime, t2), + JavaUtil.convertJavaToRuby(runtime, t3), + JavaUtil.convertJavaToRuby(runtime, t4), + JavaUtil.convertJavaToRuby(runtime, t5), + JavaUtil.convertJavaToRuby(runtime, t6), + JavaUtil.convertJavaToRuby(runtime, t7)}; + return (R) proc.call(context, array); + } + + @Override + public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), + JavaUtil.convertJavaToRuby(runtime, t2), + JavaUtil.convertJavaToRuby(runtime, t3), + JavaUtil.convertJavaToRuby(runtime, t4), + JavaUtil.convertJavaToRuby(runtime, t5), + JavaUtil.convertJavaToRuby(runtime, t6), + JavaUtil.convertJavaToRuby(runtime, t7), + JavaUtil.convertJavaToRuby(runtime, t8)}; + return (R) proc.call(context, array); + } + + @Override + public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) { + IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), + JavaUtil.convertJavaToRuby(runtime, t2), + JavaUtil.convertJavaToRuby(runtime, t3), + JavaUtil.convertJavaToRuby(runtime, t4), + JavaUtil.convertJavaToRuby(runtime, t5), + JavaUtil.convertJavaToRuby(runtime, t6), + JavaUtil.convertJavaToRuby(runtime, t7), + JavaUtil.convertJavaToRuby(runtime, t8), + JavaUtil.convertJavaToRuby(runtime, t9)}; + return (R) proc.call(context, array); + } + + @Override + public R call(Object... args) { + IRubyObject[] array = new IRubyObject[args.length]; + for (int i = 0; i < args.length; i++) { + array[i] = JavaUtil.convertJavaToRuby(runtime, args[i]); + } + return (R) proc.call(context, array); + } +} diff --git a/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb b/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb new file mode 100644 index 0000000000..478be677b3 --- /dev/null +++ b/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb @@ -0,0 +1,81 @@ +module Rx + module Lang + module Jruby + class Interop + WRAPPERS = { + Java::RxUtilFunctions::Action => Java::RxLangJruby::JRubyActionWrapper + } + + WRAPPERS.default = Java::RxLangJruby::JRubyFunctionWrapper + + KLASSES = [Java::Rx::Observable, Java::RxObservables::BlockingObservable] + FUNCTION = Java::RxUtilFunctions::Function.java_class + RUNTIME = JRuby.runtime + + def self.instance + @instance ||= new + end + + def initialize + KLASSES.each do |klass| + function_methods = (klass.java_class.declared_instance_methods + klass.java_class.declared_class_methods).select do |method| + method.public? && method.parameter_types.any? {|type| FUNCTION.assignable_from?(type)} + end + + parameter_types = {} + function_methods.each do |method| + parameter_types[[method.name, method.static?]] ||= [] + + method.parameter_types.each_with_index do |type, idx| + next unless FUNCTION.assignable_from?(type) + + constructor = WRAPPERS.find do |java_class, wrapper| + type.ruby_class.ancestors.include?(java_class) + end + + constructor = (constructor && constructor.last) || WRAPPERS.default + + parameter_types[[method.name, method.static?]][idx] ||= [] + parameter_types[[method.name, method.static?]][idx] << constructor + end + end + + parameter_types.each_pair do |_, types| + types.map! do |type| + next type.first if type && type.uniq.length == 1 + nil + end + end + + parameter_types.each_pair do |(method_name, static), types| + next if types.all?(&:nil?) + + klass_to_open = static ? klass.singleton_class : klass + + klass_to_open.send(:alias_method, "#{method_name}_without_wrapping", method_name) + klass_to_open.send(:define_method, method_name) do |*args, &block| + context = RUNTIME.get_current_context + + args = args.each_with_index.map do |arg, idx| + if arg.is_a?(Proc) && types[idx] + types[idx].new(context, arg) + else + arg + end + end + + if block && types[args.length] + block = types[args.length].new(context, block) + end + + send("#{method_name}_without_wrapping", *(args + [block].compact)) + end + end + end + end + end + end + end +end + +Rx::Lang::Jruby::Interop.instance diff --git a/settings.gradle b/settings.gradle index 8750fab727..1412d8b263 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,6 +2,7 @@ rootProject.name='rxjava' include 'rxjava-core', \ 'language-adaptors:rxjava-groovy', \ 'language-adaptors:rxjava-clojure', \ +'language-adaptors:rxjava-jruby', \ 'language-adaptors:rxjava-scala', \ 'language-adaptors:rxjava-scala-java', \ 'rxjava-contrib:rxjava-swing', \ From e7eafe29507beb843bd6f8dfbbae1db3ce6de474 Mon Sep 17 00:00:00 2001 From: Mike Ragalie Date: Tue, 1 Oct 2013 19:20:53 -0700 Subject: [PATCH 141/333] JRuby wrapper specs --- language-adaptors/rxjava-jruby/build.gradle | 29 +++++++++++ .../main/resources/rx/lang/jruby/interop.rb | 44 +++++++++++----- .../spec/ruby/rx/lang/jruby/interop_spec.rb | 43 +++++++++++++++ .../lang/jruby/jruby_action_wrapper_spec.rb | 32 ++++++++++++ .../lang/jruby/jruby_function_wrapper_spec.rb | 52 +++++++++++++++++++ .../spec/ruby/rx/lang/jruby/spec_helper.rb | 1 + 6 files changed, 187 insertions(+), 14 deletions(-) create mode 100644 language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb create mode 100644 language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_action_wrapper_spec.rb create mode 100644 language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_function_wrapper_spec.rb create mode 100644 language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/spec_helper.rb diff --git a/language-adaptors/rxjava-jruby/build.gradle b/language-adaptors/rxjava-jruby/build.gradle index ef2175fa98..e1774be16e 100644 --- a/language-adaptors/rxjava-jruby/build.gradle +++ b/language-adaptors/rxjava-jruby/build.gradle @@ -1,12 +1,41 @@ apply plugin: 'osgi' +repositories { + maven { + url 'http://deux.gemjars.org' + } + mavenCentral() +} + +configurations { + rspec +} + +sourceSets { + test { + resources { + srcDir 'src/spec/ruby' + } + } +} + dependencies { compile project(':rxjava-core') compile 'org.jruby:jruby:1.7+' provided 'junit:junit-dep:4.10' provided 'org.mockito:mockito-core:1.8.5' + rspec 'org.jruby:jruby-complete:1.7.4' + rspec 'org.rubygems:rspec:2.10.0' } +task(rspec, type: JavaExec) { + main 'org.jruby.Main' + classpath configurations.rspec + runtimeClasspath + args 'classpath:bin/rspec', 'src/spec/ruby' +} + +tasks.build.dependsOn << 'rspec' + jar { manifest { name = 'rxjava-jruby' diff --git a/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb b/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb index 478be677b3..a782c3e22c 100644 --- a/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb +++ b/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb @@ -3,7 +3,7 @@ module Lang module Jruby class Interop WRAPPERS = { - Java::RxUtilFunctions::Action => Java::RxLangJruby::JRubyActionWrapper + Java::RxUtilFunctions::Action => Java::RxLangJruby::JRubyActionWrapper } WRAPPERS.default = Java::RxLangJruby::JRubyFunctionWrapper @@ -52,27 +52,43 @@ def initialize klass_to_open = static ? klass.singleton_class : klass - klass_to_open.send(:alias_method, "#{method_name}_without_wrapping", method_name) - klass_to_open.send(:define_method, method_name) do |*args, &block| - context = RUNTIME.get_current_context + [method_name, underscore(method_name)].each do |name| + klass_to_open.send(:alias_method, "#{name}_without_wrapping", name) + klass_to_open.send(:define_method, name) do |*args, &block| + context = RUNTIME.get_current_context + + args = args.each_with_index.map do |arg, idx| + if arg.is_a?(Proc) && types[idx] + types[idx].new(context, arg) + else + arg + end + end - args = args.each_with_index.map do |arg, idx| - if arg.is_a?(Proc) && types[idx] - types[idx].new(context, arg) - else - arg + if block && types[args.length] + block = types[args.length].new(context, block) end - end - if block && types[args.length] - block = types[args.length].new(context, block) + send("#{name}_without_wrapping", *(args + [block].compact)) end - - send("#{method_name}_without_wrapping", *(args + [block].compact)) end end end end + + private + + # File activesupport/lib/active_support/inflector/methods.rb, line 89 + def underscore(camel_cased_word) + word = camel_cased_word.to_s.dup + word.gsub!('::', '/') + word.gsub!(/(?:([A-Za-z\d])|^)((?=a)b)(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" } + word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2') + word.gsub!(/([a-z\d])([A-Z])/,'\1_\2') + word.tr!("-", "_") + word.downcase! + word + end end end end diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb new file mode 100644 index 0000000000..cf41bfc98a --- /dev/null +++ b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb @@ -0,0 +1,43 @@ +require_relative "spec_helper" + +describe Rx::Lang::Jruby::Interop do + subject { described_class.instance } + + let(:observable) { Java::Rx::Observable.from([1, 2, 3]) } + + context "with a normal, non-function method signature" do + it "calls straight through to the original Java method" do + observable.should_not_receive(:toBlockingObservable_without_wrapping) + observable.toBlockingObservable.should be_a(Java::RxObservables::BlockingObservable) + end + end + + context "with a method with a function method signature" do + it "wraps function arguments if they're in the right position" do + observable.should_receive(:subscribe_without_wrapping).with(kind_of(Java::RxLangJruby::JRubyActionWrapper)) + observable.subscribe(lambda {}) + end + + it "doesn't wrap function arguments if they're in the wrong position" do + proc = lambda {} + observable.should_receive(:subscribe_without_wrapping).with(1, 1, 1, proc) + observable.subscribe(1, 1, 1, proc) + end + + it "doesn't wrap non-function arguments" do + observable.should_receive(:subscribe_without_wrapping).with(1) + observable.subscribe(1) + end + + it "works with underscoreized method names" do + observable.should_receive(:finally_do_without_wrapping).with(kind_of(Java::RxLangJruby::JRubyActionWrapper)) + observable.finally_do(lambda {}) + end + + it "passes a block through as the last argument" do + proc = lambda {} + observable.should_receive(:subscribe_without_wrapping).with(1, 1, 1, 1, proc) + observable.subscribe(1, 1, 1, 1, &proc) # intentionally bogus call so it doesn't wrap the proc + end + end +end diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_action_wrapper_spec.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_action_wrapper_spec.rb new file mode 100644 index 0000000000..708ee965c1 --- /dev/null +++ b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_action_wrapper_spec.rb @@ -0,0 +1,32 @@ +require_relative "spec_helper" + +describe Java::RxLangJruby::JRubyActionWrapper do + let(:spy) { double(:spy, :call => nil) } + subject { described_class.new(JRuby.runtime.get_current_context, lambda {|*args| spy.call(args)}) } + + let(:interfaces) do + [Java::RxUtilFunctions::Action, + Java::RxUtilFunctions::Action0, + Java::RxUtilFunctions::Action1, + Java::RxUtilFunctions::Action2, + Java::RxUtilFunctions::Action3] + end + + it "implements the interfaces" do + interfaces.each do |interface| + subject.is_a?(interface) + end + end + + it "successfully uses the interfaces" do + spy.should_receive(:call).with([]) + spy.should_receive(:call).with([1]) + spy.should_receive(:call).with([1, 2]) + spy.should_receive(:call).with([1, 2, 3]) + + subject.call + subject.call(1) + subject.call(1, 2) + subject.call(1, 2, 3) + end +end diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_function_wrapper_spec.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_function_wrapper_spec.rb new file mode 100644 index 0000000000..34597c171a --- /dev/null +++ b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_function_wrapper_spec.rb @@ -0,0 +1,52 @@ +require_relative "spec_helper" + +describe Java::RxLangJruby::JRubyFunctionWrapper do + let(:spy) { double(:spy, :call => nil) } + subject { described_class.new(JRuby.runtime.get_current_context, lambda {|*args| spy.call(args); args}) } + + let(:interfaces) do + [Java::RxUtilFunctions::Func0, + Java::RxUtilFunctions::Func1, + Java::RxUtilFunctions::Func2, + Java::RxUtilFunctions::Func3, + Java::RxUtilFunctions::Func4, + Java::RxUtilFunctions::Func5, + Java::RxUtilFunctions::Func6, + Java::RxUtilFunctions::Func7, + Java::RxUtilFunctions::Func8, + Java::RxUtilFunctions::Func9, + Java::RxUtilFunctions::FuncN] + end + + it "implements the interfaces" do + interfaces.each do |interface| + subject.is_a?(interface) + end + end + + it "successfully uses the interfaces" do + spy.should_receive(:call).with([]) + spy.should_receive(:call).with([1]) + spy.should_receive(:call).with([1, 2]) + spy.should_receive(:call).with([1, 2, 3]) + spy.should_receive(:call).with([1, 2, 3, 4]) + spy.should_receive(:call).with([1, 2, 3, 4, 5]) + spy.should_receive(:call).with([1, 2, 3, 4, 5, 6]) + spy.should_receive(:call).with([1, 2, 3, 4, 5, 6, 7]) + spy.should_receive(:call).with([1, 2, 3, 4, 5, 6, 7, 8]) + spy.should_receive(:call).with([1, 2, 3, 4, 5, 6, 7, 8, 9]) + spy.should_receive(:call).with([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + + subject.call.should == [] + subject.call(1).should == [1] + subject.call(1, 2).should == [1, 2] + subject.call(1, 2, 3).should == [1, 2, 3] + subject.call(1, 2, 3, 4).should == [1, 2, 3, 4] + subject.call(1, 2, 3, 4, 5).should == [1, 2, 3, 4, 5] + subject.call(1, 2, 3, 4, 5, 6).should == [1, 2, 3, 4, 5, 6] + subject.call(1, 2, 3, 4, 5, 6, 7).should == [1, 2, 3, 4, 5, 6, 7] + subject.call(1, 2, 3, 4, 5, 6, 7, 8).should == [1, 2, 3, 4, 5, 6, 7, 8] + subject.call(1, 2, 3, 4, 5, 6, 7, 8, 9).should == [1, 2, 3, 4, 5, 6, 7, 8, 9] + subject.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).should == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + end +end diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/spec_helper.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/spec_helper.rb new file mode 100644 index 0000000000..c8451bc051 --- /dev/null +++ b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/spec_helper.rb @@ -0,0 +1 @@ +require "rx/lang/jruby/interop" From 2f44a8d8c17f369162c4b54b87205e86c25640ef Mon Sep 17 00:00:00 2001 From: Mike Ragalie Date: Thu, 26 Sep 2013 22:53:07 -0700 Subject: [PATCH 142/333] JRuby performance test --- .../spec/ruby/rx/lang/jruby/performance.rb | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/performance.rb diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/performance.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/performance.rb new file mode 100644 index 0000000000..200273f69e --- /dev/null +++ b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/performance.rb @@ -0,0 +1,38 @@ +# Current execution times: +# Without rxjava-jruby: 3.31s +# With rxjava-jruby: 2.18s + +require 'optparse' + +options = {} +OptionParser.new do |opts| + opts.banner = "Usage: jruby --profile.api performance.rb [options]" + + opts.on("-c", "--core CORE-PATH", "Path to the rxjava-core.jar") {|core| options[:core] = core} + opts.on("-j", "--jruby [JRUBY-PATH]", "Path to the rxjava-jruby.jar (optional)") {|jruby| options[:jruby] = jruby} +end.parse! + +require options[:core] + +if options[:jruby] + require options[:jruby] + require 'rx/lang/jruby/interop' +end + +require 'jruby/profiler' + +profile_data = JRuby::Profiler.profile do + 10000.times do + o = Java::Rx::Observable.create do |observer| + observer.onNext("one") + observer.onNext("two") + observer.onNext("three") + observer.onCompleted + Java::RxSubscriptions::Subscription.empty + end + o.map {|n| n * 2}.subscribe {|n| n} + end +end + +profile_printer = JRuby::Profiler::FlatProfilePrinter.new(profile_data) +profile_printer.printProfile(STDOUT) From fd910988f182d0673b42f891419b6e8d44a340da Mon Sep 17 00:00:00 2001 From: Mike Ragalie Date: Sun, 6 Oct 2013 12:42:57 -0700 Subject: [PATCH 143/333] Handle OnSubscribeFuncs correctly in JRuby interop logic OnSubscribeFunc#onSubscribe expects to return a Subscription. I am unable to successfully cast the return value of a RubyProc, even if that value _is_ an object that implements the Subscription interface, into a Subscription in Java-land (Java reports that ConcreteJavaProxy cannot be cast). Instead I allow JRuby to handle OnSubscribeFunc arguments through its default proxy logic, which works correctly. --- .../src/main/resources/rx/lang/jruby/interop.rb | 8 ++++++-- .../src/spec/ruby/rx/lang/jruby/interop_spec.rb | 12 +++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb b/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb index a782c3e22c..899ec12002 100644 --- a/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb +++ b/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb @@ -3,7 +3,8 @@ module Lang module Jruby class Interop WRAPPERS = { - Java::RxUtilFunctions::Action => Java::RxLangJruby::JRubyActionWrapper + Java::RxUtilFunctions::Action => Java::RxLangJruby::JRubyActionWrapper, + Java::Rx::Observable::OnSubscribeFunc => false } WRAPPERS.default = Java::RxLangJruby::JRubyFunctionWrapper @@ -33,6 +34,9 @@ def initialize type.ruby_class.ancestors.include?(java_class) end + # Skip OnSubscribeFuncs + next if constructor && constructor.last == false + constructor = (constructor && constructor.last) || WRAPPERS.default parameter_types[[method.name, method.static?]][idx] ||= [] @@ -52,7 +56,7 @@ def initialize klass_to_open = static ? klass.singleton_class : klass - [method_name, underscore(method_name)].each do |name| + [method_name, underscore(method_name)].uniq.each do |name| klass_to_open.send(:alias_method, "#{name}_without_wrapping", name) klass_to_open.send(:define_method, name) do |*args, &block| context = RUNTIME.get_current_context diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb index cf41bfc98a..45ddb20d13 100644 --- a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb +++ b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb @@ -29,8 +29,18 @@ observable.subscribe(1) end + it "doesn't wrap OnSubscribeFunc arguments" do + proc = lambda {|observer| observer.onNext("hi")} + Java::Rx::Observable.should_not_receive(:create_without_wrapping) + Java::Rx::Observable.create(proc).should be_a(Java::Rx::Observable) + end + it "works with underscoreized method names" do - observable.should_receive(:finally_do_without_wrapping).with(kind_of(Java::RxLangJruby::JRubyActionWrapper)) + observable. + should_receive(:finally_do_without_wrapping). + with(kind_of(Java::RxLangJruby::JRubyActionWrapper)). + and_call_original + observable.finally_do(lambda {}) end From e1b81b43eb9bde1e9314ad5af19965f3f42c0b9b Mon Sep 17 00:00:00 2001 From: Mike Ragalie Date: Sun, 6 Oct 2013 16:54:59 -0700 Subject: [PATCH 144/333] JRuby README --- language-adaptors/rxjava-jruby/README.md | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 language-adaptors/rxjava-jruby/README.md diff --git a/language-adaptors/rxjava-jruby/README.md b/language-adaptors/rxjava-jruby/README.md new file mode 100644 index 0000000000..3232e073f9 --- /dev/null +++ b/language-adaptors/rxjava-jruby/README.md @@ -0,0 +1,37 @@ +# JRuby Adaptor for RxJava + +This adaptor improves the success and performance of RxJava when Ruby `Proc` is passed to an RxJava method. + +This enables correct and efficient execution of code such as: + +```ruby + Observable.from("one", "two", "three"). + take(2). + subscribe {|val| puts val} +``` + +# Binaries + +Binaries and dependency information for Maven, Ivy, Gradle and others can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22rxjava-jruby%22). + +Example for Maven: + +```xml + + com.netflix.rxjava + rxjava-jruby + x.y.z + +``` + +and for Ivy: + +```xml + +``` + +and for Gradle: + +```groovy +compile 'com.netflix.rxjava:rxjava-jruby:x.y.z' +``` From 16a773544793491d525a190c1b3baa1c10fec613 Mon Sep 17 00:00:00 2001 From: Mike Ragalie Date: Sun, 6 Oct 2013 17:49:37 -0700 Subject: [PATCH 145/333] JRuby license headers --- .../src/main/resources/rx/lang/jruby/interop.rb | 14 ++++++++++++++ .../src/spec/ruby/rx/lang/jruby/interop_spec.rb | 15 +++++++++++++++ .../rx/lang/jruby/jruby_action_wrapper_spec.rb | 14 ++++++++++++++ .../rx/lang/jruby/jruby_function_wrapper_spec.rb | 14 ++++++++++++++ .../src/spec/ruby/rx/lang/jruby/performance.rb | 14 ++++++++++++++ .../src/spec/ruby/rx/lang/jruby/spec_helper.rb | 14 ++++++++++++++ 6 files changed, 85 insertions(+) diff --git a/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb b/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb index 899ec12002..a080639728 100644 --- a/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb +++ b/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb @@ -1,3 +1,17 @@ +# Copyright 2013 Mike Ragalie +# +# 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. + module Rx module Lang module Jruby diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb index 45ddb20d13..222caf34ed 100644 --- a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb +++ b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb @@ -1,3 +1,17 @@ +# Copyright 2013 Mike Ragalie +# +# 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. + require_relative "spec_helper" describe Rx::Lang::Jruby::Interop do @@ -31,6 +45,7 @@ it "doesn't wrap OnSubscribeFunc arguments" do proc = lambda {|observer| observer.onNext("hi")} + Java::Rx::Observable.__persistent__ = true Java::Rx::Observable.should_not_receive(:create_without_wrapping) Java::Rx::Observable.create(proc).should be_a(Java::Rx::Observable) end diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_action_wrapper_spec.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_action_wrapper_spec.rb index 708ee965c1..dae1973625 100644 --- a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_action_wrapper_spec.rb +++ b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_action_wrapper_spec.rb @@ -1,3 +1,17 @@ +# Copyright 2013 Mike Ragalie +# +# 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. + require_relative "spec_helper" describe Java::RxLangJruby::JRubyActionWrapper do diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_function_wrapper_spec.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_function_wrapper_spec.rb index 34597c171a..4c25f6ec1c 100644 --- a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_function_wrapper_spec.rb +++ b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_function_wrapper_spec.rb @@ -1,3 +1,17 @@ +# Copyright 2013 Mike Ragalie +# +# 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. + require_relative "spec_helper" describe Java::RxLangJruby::JRubyFunctionWrapper do diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/performance.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/performance.rb index 200273f69e..fc9cde1b0b 100644 --- a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/performance.rb +++ b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/performance.rb @@ -1,3 +1,17 @@ +# Copyright 2013 Mike Ragalie +# +# 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. + # Current execution times: # Without rxjava-jruby: 3.31s # With rxjava-jruby: 2.18s diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/spec_helper.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/spec_helper.rb index c8451bc051..99b095960f 100644 --- a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/spec_helper.rb +++ b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/spec_helper.rb @@ -1 +1,15 @@ +# Copyright 2013 Mike Ragalie +# +# 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. + require "rx/lang/jruby/interop" From 13992a1b73796d05d109c59deaef77a64d0daf4b Mon Sep 17 00:00:00 2001 From: Mike Ragalie Date: Sun, 6 Oct 2013 18:29:51 -0700 Subject: [PATCH 146/333] Up rspec version --- language-adaptors/rxjava-jruby/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-jruby/build.gradle b/language-adaptors/rxjava-jruby/build.gradle index e1774be16e..eaefe7a81a 100644 --- a/language-adaptors/rxjava-jruby/build.gradle +++ b/language-adaptors/rxjava-jruby/build.gradle @@ -25,7 +25,7 @@ dependencies { provided 'junit:junit-dep:4.10' provided 'org.mockito:mockito-core:1.8.5' rspec 'org.jruby:jruby-complete:1.7.4' - rspec 'org.rubygems:rspec:2.10.0' + rspec 'org.rubygems:rspec:2.14.1' } task(rspec, type: JavaExec) { From 24807e94506715701e944c0ef4d554156c93e01b Mon Sep 17 00:00:00 2001 From: Mike Ragalie Date: Sun, 6 Oct 2013 18:33:36 -0700 Subject: [PATCH 147/333] JRuby README include usage instructions --- language-adaptors/rxjava-jruby/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/language-adaptors/rxjava-jruby/README.md b/language-adaptors/rxjava-jruby/README.md index 3232e073f9..da75bac1fb 100644 --- a/language-adaptors/rxjava-jruby/README.md +++ b/language-adaptors/rxjava-jruby/README.md @@ -10,6 +10,14 @@ This enables correct and efficient execution of code such as: subscribe {|val| puts val} ``` +# Usage + +Require the JAR file as usual. After requiring the JAR, you must also require the interop code: + +```ruby +require "rx/lang/jruby/interop" +``` + # Binaries Binaries and dependency information for Maven, Ivy, Gradle and others can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22rxjava-jruby%22). From 08a70ff5d4b0026c2f234c7b4c4c6dd804ac0aad Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 8 Oct 2013 22:31:29 -0700 Subject: [PATCH 148/333] Unit Test while testing refCount submission --- .../src/test/java/rx/RefCountTest.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 rxjava-core/src/test/java/rx/RefCountTest.java diff --git a/rxjava-core/src/test/java/rx/RefCountTest.java b/rxjava-core/src/test/java/rx/RefCountTest.java new file mode 100644 index 0000000000..3cc09b4076 --- /dev/null +++ b/rxjava-core/src/test/java/rx/RefCountTest.java @@ -0,0 +1,103 @@ +package rx; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import rx.concurrency.TestScheduler; +import rx.util.functions.Action1; + +public class RefCountTest { + + @Test + public void testRefCount() { + TestScheduler s = new TestScheduler(); + Observable interval = Observable.interval(100, TimeUnit.MILLISECONDS, s).publish().refCount(); + + // subscribe list1 + final List list1 = new ArrayList(); + Subscription s1 = interval.subscribe(new Action1() { + + @Override + public void call(Long t1) { + list1.add(t1); + } + + }); + s.advanceTimeBy(200, TimeUnit.MILLISECONDS); + + assertEquals(2, list1.size()); + assertEquals(0L, list1.get(0).longValue()); + assertEquals(1L, list1.get(1).longValue()); + + // subscribe list2 + final List list2 = new ArrayList(); + Subscription s2 = interval.subscribe(new Action1() { + + @Override + public void call(Long t1) { + list2.add(t1); + } + + }); + s.advanceTimeBy(300, TimeUnit.MILLISECONDS); + + // list 1 should have 5 items + assertEquals(5, list1.size()); + assertEquals(2L, list1.get(2).longValue()); + assertEquals(3L, list1.get(3).longValue()); + assertEquals(4L, list1.get(4).longValue()); + + // list 2 should only have 3 items + assertEquals(3, list2.size()); + assertEquals(2L, list2.get(0).longValue()); + assertEquals(3L, list2.get(1).longValue()); + assertEquals(4L, list2.get(2).longValue()); + + // unsubscribe list1 + s1.unsubscribe(); + + // advance further + s.advanceTimeBy(300, TimeUnit.MILLISECONDS); + + // list 1 should still have 5 items + assertEquals(5, list1.size()); + + // list 2 should have 6 items + assertEquals(6, list2.size()); + assertEquals(5L, list2.get(3).longValue()); + assertEquals(6L, list2.get(4).longValue()); + assertEquals(7L, list2.get(5).longValue()); + + // unsubscribe list2 + s2.unsubscribe(); + + // advance further + s.advanceTimeBy(1000, TimeUnit.MILLISECONDS); + + // the following is not working as it seems the PublishSubject does not allow re-subscribing. TODO fix that in subsequent pull request + + +// // subscribing a new one should start over because the source should have been unsubscribed +// // subscribe list1 +// final List list3 = new ArrayList(); +// Subscription s3 = interval.subscribe(new Action1() { +// +// @Override +// public void call(Long t1) { +// list3.add(t1); +// } +// +// }); +// s.advanceTimeBy(200, TimeUnit.MILLISECONDS); +// +// assertEquals(2, list3.size()); +// assertEquals(0L, list3.get(0).longValue()); +// assertEquals(1L, list3.get(1).longValue()); + + } +} From 500909d3a605ec69a5883b5952fe1b6e4981cfb0 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 8 Oct 2013 23:31:11 -0700 Subject: [PATCH 149/333] Combine RefCountTest and RefCountTests --- .../src/test/java/rx/RefCountTest.java | 103 ------------------ .../src/test/java/rx/RefCountTests.java | 94 ++++++++++++++++ 2 files changed, 94 insertions(+), 103 deletions(-) delete mode 100644 rxjava-core/src/test/java/rx/RefCountTest.java diff --git a/rxjava-core/src/test/java/rx/RefCountTest.java b/rxjava-core/src/test/java/rx/RefCountTest.java deleted file mode 100644 index 3cc09b4076..0000000000 --- a/rxjava-core/src/test/java/rx/RefCountTest.java +++ /dev/null @@ -1,103 +0,0 @@ -package rx; - -import static org.junit.Assert.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.junit.Test; - -import rx.concurrency.TestScheduler; -import rx.util.functions.Action1; - -public class RefCountTest { - - @Test - public void testRefCount() { - TestScheduler s = new TestScheduler(); - Observable interval = Observable.interval(100, TimeUnit.MILLISECONDS, s).publish().refCount(); - - // subscribe list1 - final List list1 = new ArrayList(); - Subscription s1 = interval.subscribe(new Action1() { - - @Override - public void call(Long t1) { - list1.add(t1); - } - - }); - s.advanceTimeBy(200, TimeUnit.MILLISECONDS); - - assertEquals(2, list1.size()); - assertEquals(0L, list1.get(0).longValue()); - assertEquals(1L, list1.get(1).longValue()); - - // subscribe list2 - final List list2 = new ArrayList(); - Subscription s2 = interval.subscribe(new Action1() { - - @Override - public void call(Long t1) { - list2.add(t1); - } - - }); - s.advanceTimeBy(300, TimeUnit.MILLISECONDS); - - // list 1 should have 5 items - assertEquals(5, list1.size()); - assertEquals(2L, list1.get(2).longValue()); - assertEquals(3L, list1.get(3).longValue()); - assertEquals(4L, list1.get(4).longValue()); - - // list 2 should only have 3 items - assertEquals(3, list2.size()); - assertEquals(2L, list2.get(0).longValue()); - assertEquals(3L, list2.get(1).longValue()); - assertEquals(4L, list2.get(2).longValue()); - - // unsubscribe list1 - s1.unsubscribe(); - - // advance further - s.advanceTimeBy(300, TimeUnit.MILLISECONDS); - - // list 1 should still have 5 items - assertEquals(5, list1.size()); - - // list 2 should have 6 items - assertEquals(6, list2.size()); - assertEquals(5L, list2.get(3).longValue()); - assertEquals(6L, list2.get(4).longValue()); - assertEquals(7L, list2.get(5).longValue()); - - // unsubscribe list2 - s2.unsubscribe(); - - // advance further - s.advanceTimeBy(1000, TimeUnit.MILLISECONDS); - - // the following is not working as it seems the PublishSubject does not allow re-subscribing. TODO fix that in subsequent pull request - - -// // subscribing a new one should start over because the source should have been unsubscribed -// // subscribe list1 -// final List list3 = new ArrayList(); -// Subscription s3 = interval.subscribe(new Action1() { -// -// @Override -// public void call(Long t1) { -// list3.add(t1); -// } -// -// }); -// s.advanceTimeBy(200, TimeUnit.MILLISECONDS); -// -// assertEquals(2, list3.size()); -// assertEquals(0L, list3.get(0).longValue()); -// assertEquals(1L, list3.get(1).longValue()); - - } -} diff --git a/rxjava-core/src/test/java/rx/RefCountTests.java b/rxjava-core/src/test/java/rx/RefCountTests.java index bf035e0aa4..bf529b4076 100644 --- a/rxjava-core/src/test/java/rx/RefCountTests.java +++ b/rxjava-core/src/test/java/rx/RefCountTests.java @@ -3,9 +3,15 @@ import org.junit.Before; import org.junit.Test; import org.mockito.MockitoAnnotations; + +import rx.concurrency.TestScheduler; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import rx.util.functions.Action1; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertEquals; @@ -45,4 +51,92 @@ public void call() { second.unsubscribe(); assertEquals(1, unsubscriptionCount.get()); } + + @Test + public void testRefCount() { + TestScheduler s = new TestScheduler(); + Observable interval = Observable.interval(100, TimeUnit.MILLISECONDS, s).publish().refCount(); + + // subscribe list1 + final List list1 = new ArrayList(); + Subscription s1 = interval.subscribe(new Action1() { + + @Override + public void call(Long t1) { + list1.add(t1); + } + + }); + s.advanceTimeBy(200, TimeUnit.MILLISECONDS); + + assertEquals(2, list1.size()); + assertEquals(0L, list1.get(0).longValue()); + assertEquals(1L, list1.get(1).longValue()); + + // subscribe list2 + final List list2 = new ArrayList(); + Subscription s2 = interval.subscribe(new Action1() { + + @Override + public void call(Long t1) { + list2.add(t1); + } + + }); + s.advanceTimeBy(300, TimeUnit.MILLISECONDS); + + // list 1 should have 5 items + assertEquals(5, list1.size()); + assertEquals(2L, list1.get(2).longValue()); + assertEquals(3L, list1.get(3).longValue()); + assertEquals(4L, list1.get(4).longValue()); + + // list 2 should only have 3 items + assertEquals(3, list2.size()); + assertEquals(2L, list2.get(0).longValue()); + assertEquals(3L, list2.get(1).longValue()); + assertEquals(4L, list2.get(2).longValue()); + + // unsubscribe list1 + s1.unsubscribe(); + + // advance further + s.advanceTimeBy(300, TimeUnit.MILLISECONDS); + + // list 1 should still have 5 items + assertEquals(5, list1.size()); + + // list 2 should have 6 items + assertEquals(6, list2.size()); + assertEquals(5L, list2.get(3).longValue()); + assertEquals(6L, list2.get(4).longValue()); + assertEquals(7L, list2.get(5).longValue()); + + // unsubscribe list2 + s2.unsubscribe(); + + // advance further + s.advanceTimeBy(1000, TimeUnit.MILLISECONDS); + + // the following is not working as it seems the PublishSubject does not allow re-subscribing. TODO fix that in subsequent pull request + + +// // subscribing a new one should start over because the source should have been unsubscribed +// // subscribe list1 +// final List list3 = new ArrayList(); +// Subscription s3 = interval.subscribe(new Action1() { +// +// @Override +// public void call(Long t1) { +// list3.add(t1); +// } +// +// }); +// s.advanceTimeBy(200, TimeUnit.MILLISECONDS); +// +// assertEquals(2, list3.size()); +// assertEquals(0L, list3.get(0).longValue()); +// assertEquals(1L, list3.get(1).longValue()); + + } } From f03577038ac029bd8098b67254df15f0c3240e2a Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 8 Oct 2013 23:55:18 -0700 Subject: [PATCH 150/333] Remove PublishSubject Terminal State Behavior The PublishSubject implementation was performing onError/onCompleted unsubscribe logic that was put in place long ago and I am now pretty sure it was wrong. This was revealed while playing with `refCount` which intends on allowing a re-subscription to the source once new Observers arrive. PublishSubject was preventing that. The one use case that I'm still wondering about though is if someone subscribes to a PublishSubject after it has emitted onCompleted and isn't "restarted". That Observer would wait forever if it is a "single-shot" PublishSubject use case. I'm not sure if that's just a bad use and fits into the "don't do that" scenario, or if it's a legit issue that has a solution. Rightn now this code is "thread-safe" in the visibility sense, but it's not atomic and could have race conditions between adding/removing Observers and event notifications. I don't think that's an issue as if someone is concurrently adding/removing it's always a race, but am not 100% sure if there's a use case I'm missing. This also assumes (as it always did) that someone is not invoking onNext concurrently as that would break the Rx contract. --- .../main/java/rx/subjects/PublishSubject.java | 232 +++++++----------- .../src/test/java/rx/RefCountTests.java | 35 ++- 2 files changed, 109 insertions(+), 158 deletions(-) diff --git a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java index 39c19d9b7f..5588dd485b 100644 --- a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -51,7 +51,7 @@ *

*

 {@code
 
-  PublishSubject subject = PublishSubject.create();
+ * ublishSubject subject = PublishSubject.create();
   // observer1 will receive all onNext and onCompleted events
   subject.subscribe(observer1);
   subject.onNext("one");
@@ -62,21 +62,16 @@
   subject.onCompleted();
 
   } 
- *
+ * 
  * @param 
  */
 public class PublishSubject extends Subject {
     public static  PublishSubject create() {
         final ConcurrentHashMap> observers = new ConcurrentHashMap>();
-        final AtomicReference> terminalState = new AtomicReference>();
 
         OnSubscribeFunc onSubscribe = new OnSubscribeFunc() {
             @Override
             public Subscription onSubscribe(Observer observer) {
-                // shortcut check if terminal state exists already
-                Subscription s = checkTerminalState(observer);
-                if(s != null) return s;
-
                 final SafeObservableSubscription subscription = new SafeObservableSubscription();
 
                 subscription.wrap(new Subscription() {
@@ -87,67 +82,26 @@ public void unsubscribe() {
                     }
                 });
 
-                /**
-                 * NOTE: We are synchronizing to avoid a race condition between terminalState being set and
-                 * a new observer being added to observers.
-                 *
-                 * The synchronization only occurs on subscription and terminal states, it does not affect onNext calls
-                 * so a high-volume hot-observable will not pay this cost for emitting data.
-                 *
-                 * Due to the restricted impact of blocking synchronization here I have not pursued more complicated
-                 * approaches to try and stay completely non-blocking.
-                 */
-                synchronized (terminalState) {
-                    // check terminal state again
-                    s = checkTerminalState(observer);
-                    if (s != null)
-                        return s;
-
-                    // on subscribe add it to the map of outbound observers to notify
-                    observers.put(subscription, observer);
-
-                    return subscription;
-                }
-            }
+                // on subscribe add it to the map of outbound observers to notify
+                observers.put(subscription, observer);
 
-            private Subscription checkTerminalState(Observer observer) {
-                Notification n = terminalState.get();
-                if (n != null) {
-                    // we are terminated to immediately emit and don't continue with subscription
-                    if (n.isOnCompleted()) {
-                        observer.onCompleted();
-                    } else {
-                        observer.onError(n.getThrowable());
-                    }
-                    return Subscriptions.empty();
-                } else {
-                    return null;
-                }
+                return subscription;
             }
+
         };
 
-        return new PublishSubject(onSubscribe, observers, terminalState);
+        return new PublishSubject(onSubscribe, observers);
     }
 
     private final ConcurrentHashMap> observers;
-    private final AtomicReference> terminalState;
 
-    protected PublishSubject(OnSubscribeFunc onSubscribe, ConcurrentHashMap> observers, AtomicReference> terminalState) {
+    protected PublishSubject(OnSubscribeFunc onSubscribe, ConcurrentHashMap> observers) {
         super(onSubscribe);
         this.observers = observers;
-        this.terminalState = terminalState;
     }
 
     @Override
     public void onCompleted() {
-        /**
-         * Synchronizing despite terminalState being an AtomicReference because of multi-step logic in subscription.
-         * Why use AtomicReference then? Convenient for passing around a mutable reference holder between the
-         * onSubscribe function and PublishSubject instance... and it's a "better volatile" for the shortcut codepath.
-         */
-        synchronized (terminalState) {
-            terminalState.set(new Notification());
-        }
         for (Observer observer : snapshotOfValues()) {
             observer.onCompleted();
         }
@@ -156,14 +110,6 @@ public void onCompleted() {
 
     @Override
     public void onError(Throwable e) {
-        /**
-         * Synchronizing despite terminalState being an AtomicReference because of multi-step logic in subscription.
-         * Why use AtomicReference then? Convenient for passing around a mutable reference holder between the
-         * onSubscribe function and PublishSubject instance... and it's a "better volatile" for the shortcut codepath.
-         */
-        synchronized (terminalState) {
-            terminalState.set(new Notification(e));
-        }
         for (Observer observer : snapshotOfValues()) {
             observer.onError(e);
         }
@@ -179,12 +125,12 @@ public void onNext(T args) {
 
     /**
      * Current snapshot of 'values()' so that concurrent modifications aren't included.
-     *
+     * 
      * This makes it behave deterministically in a single-threaded execution when nesting subscribes.
-     *
+     * 
      * In multi-threaded execution it will cause new subscriptions to wait until the following onNext instead
      * of possibly being included in the current onNext iteration.
-     *
+     * 
      * @return List>
      */
     private Collection> snapshotOfValues() {
@@ -378,75 +324,6 @@ private void assertObservedUntilTwo(Observer aObserver)
             verify(aObserver, Mockito.never()).onCompleted();
         }
 
-        /**
-         * Test that subscribing after onError/onCompleted immediately terminates instead of causing it to hang.
-         *
-         * Nothing is mentioned in Rx Guidelines for what to do in this case so I'm doing what seems to make sense
-         * which is:
-         *
-         * - cache terminal state (onError/onCompleted)
-         * - any subsequent subscriptions will immediately receive the terminal state rather than start a new subscription
-         *
-         */
-        @Test
-        public void testUnsubscribeAfterOnCompleted() {
-            PublishSubject subject = PublishSubject.create();
-
-            @SuppressWarnings("unchecked")
-            Observer anObserver = mock(Observer.class);
-            subject.subscribe(anObserver);
-
-            subject.onNext("one");
-            subject.onNext("two");
-            subject.onCompleted();
-
-            InOrder inOrder = inOrder(anObserver);
-            inOrder.verify(anObserver, times(1)).onNext("one");
-            inOrder.verify(anObserver, times(1)).onNext("two");
-            inOrder.verify(anObserver, times(1)).onCompleted();
-            inOrder.verify(anObserver, Mockito.never()).onError(any(Throwable.class));
-
-            @SuppressWarnings("unchecked")
-            Observer anotherObserver = mock(Observer.class);
-            subject.subscribe(anotherObserver);
-
-            inOrder = inOrder(anotherObserver);
-            inOrder.verify(anotherObserver, Mockito.never()).onNext("one");
-            inOrder.verify(anotherObserver, Mockito.never()).onNext("two");
-            inOrder.verify(anotherObserver, times(1)).onCompleted();
-            inOrder.verify(anotherObserver, Mockito.never()).onError(any(Throwable.class));
-        }
-
-        @Test
-        public void testUnsubscribeAfterOnError() {
-            PublishSubject subject = PublishSubject.create();
-            RuntimeException exception = new RuntimeException("failure");
-
-            @SuppressWarnings("unchecked")
-            Observer anObserver = mock(Observer.class);
-            subject.subscribe(anObserver);
-
-            subject.onNext("one");
-            subject.onNext("two");
-            subject.onError(exception);
-
-            InOrder inOrder = inOrder(anObserver);
-            inOrder.verify(anObserver, times(1)).onNext("one");
-            inOrder.verify(anObserver, times(1)).onNext("two");
-            inOrder.verify(anObserver, times(1)).onError(exception);
-            inOrder.verify(anObserver, Mockito.never()).onCompleted();
-
-            @SuppressWarnings("unchecked")
-            Observer anotherObserver = mock(Observer.class);
-            subject.subscribe(anotherObserver);
-
-            inOrder = inOrder(anotherObserver);
-            inOrder.verify(anotherObserver, Mockito.never()).onNext("one");
-            inOrder.verify(anotherObserver, Mockito.never()).onNext("two");
-            inOrder.verify(anotherObserver, times(1)).onError(exception);
-            inOrder.verify(anotherObserver, Mockito.never()).onCompleted();
-        }
-
         @Test
         public void testUnsubscribe()
         {
@@ -519,8 +396,7 @@ public void call(String v) {
 
             });
 
-
-            for(int i=0; i<10; i++) {
+            for (int i = 0; i < 10; i++) {
                 s.onNext(i);
             }
             s.onCompleted();
@@ -533,5 +409,83 @@ public void call(String v) {
             assertEquals(45, list.size());
         }
 
+        /**
+         * Should be able to unsubscribe all Observers, have it stop emitting, then subscribe new ones and it start emitting again.
+         */
+        @Test
+        public void testReSubscribe() {
+            final PublishSubject ps = PublishSubject.create();
+
+            Observer o1 = mock(Observer.class);
+            Subscription s1 = ps.subscribe(o1);
+
+            // emit
+            ps.onNext(1);
+
+            // validate we got it
+            InOrder inOrder1 = inOrder(o1);
+            inOrder1.verify(o1, times(1)).onNext(1);
+            inOrder1.verifyNoMoreInteractions();
+
+            // unsubscribe
+            s1.unsubscribe();
+
+            // emit again but nothing will be there to receive it
+            ps.onNext(2);
+
+            Observer o2 = mock(Observer.class);
+            Subscription s2 = ps.subscribe(o2);
+
+            // emit
+            ps.onNext(3);
+
+            // validate we got it
+            InOrder inOrder2 = inOrder(o2);
+            inOrder2.verify(o2, times(1)).onNext(3);
+            inOrder2.verifyNoMoreInteractions();
+
+            s2.unsubscribe();
+        }
+
+        /**
+         * Even if subject received an onError/onCompleted, new subscriptions should be able to restart it.
+         */
+        @Test
+        public void testReSubscribeAfterTerminalState() {
+            final PublishSubject ps = PublishSubject.create();
+
+            Observer o1 = mock(Observer.class);
+            Subscription s1 = ps.subscribe(o1);
+
+            // emit
+            ps.onNext(1);
+
+            // validate we got it
+            InOrder inOrder1 = inOrder(o1);
+            inOrder1.verify(o1, times(1)).onNext(1);
+            inOrder1.verifyNoMoreInteractions();
+
+            // unsubscribe
+            s1.unsubscribe();
+
+            ps.onCompleted();
+
+            // emit again but nothing will be there to receive it
+            ps.onNext(2);
+
+            Observer o2 = mock(Observer.class);
+            Subscription s2 = ps.subscribe(o2);
+
+            // emit
+            ps.onNext(3);
+
+            // validate we got it
+            InOrder inOrder2 = inOrder(o2);
+            inOrder2.verify(o2, times(1)).onNext(3);
+            inOrder2.verifyNoMoreInteractions();
+
+            s2.unsubscribe();
+        }
+
     }
 }
diff --git a/rxjava-core/src/test/java/rx/RefCountTests.java b/rxjava-core/src/test/java/rx/RefCountTests.java
index bf529b4076..ff99e60e1c 100644
--- a/rxjava-core/src/test/java/rx/RefCountTests.java
+++ b/rxjava-core/src/test/java/rx/RefCountTests.java
@@ -118,25 +118,22 @@ public void call(Long t1) {
         // advance further
         s.advanceTimeBy(1000, TimeUnit.MILLISECONDS);
 
-        // the following is not working as it seems the PublishSubject does not allow re-subscribing. TODO fix that in subsequent pull request
-        
-        
-//        // subscribing a new one should start over because the source should have been unsubscribed
-//        // subscribe list1
-//        final List list3 = new ArrayList();
-//        Subscription s3 = interval.subscribe(new Action1() {
-//
-//            @Override
-//            public void call(Long t1) {
-//                list3.add(t1);
-//            }
-//
-//        });
-//        s.advanceTimeBy(200, TimeUnit.MILLISECONDS);
-//
-//        assertEquals(2, list3.size());
-//        assertEquals(0L, list3.get(0).longValue());
-//        assertEquals(1L, list3.get(1).longValue());
+        // subscribing a new one should start over because the source should have been unsubscribed
+        // subscribe list3
+        final List list3 = new ArrayList();
+        Subscription s3 = interval.subscribe(new Action1() {
+
+            @Override
+            public void call(Long t1) {
+                list3.add(t1);
+            }
+
+        });
+        s.advanceTimeBy(200, TimeUnit.MILLISECONDS);
+
+        assertEquals(2, list3.size());
+        assertEquals(0L, list3.get(0).longValue());
+        assertEquals(1L, list3.get(1).longValue());
 
     }
 }

From 5e68f2524cb59273ba01627721527f69c3d5bf09 Mon Sep 17 00:00:00 2001
From: zsxwing 
Date: Tue, 8 Oct 2013 19:09:36 +0800
Subject: [PATCH 151/333] Implemented the 'IgnoreElements' operator

---
 rxjava-core/src/main/java/rx/Observable.java         | 11 +++++++++++
 .../src/main/java/rx/util/functions/Functions.java   | 12 ++++++++++++
 rxjava-core/src/test/java/rx/ObservableTests.java    | 11 +++++++++++
 3 files changed, 34 insertions(+)

diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java
index 2e11afc654..86c4424026 100644
--- a/rxjava-core/src/main/java/rx/Observable.java
+++ b/rxjava-core/src/main/java/rx/Observable.java
@@ -4477,6 +4477,17 @@ public Boolean call(T t) {
         }).cast(klass);
     }
 
+    /**
+     * Ignores all values in an observable sequence and only calls onCompleted or onError method.
+     *
+     * @return An empty observable sequence that only call onCompleted, or onError method.
+     *
+     * @see MSDN: Observable.IgnoreElements
+     */
+    public Observable ignoreElements() {
+        return filter(alwaysFalse());
+    }
+
     /**
      * Whether a given {@link Function} is an internal implementation inside rx.* packages or not.
      * 

diff --git a/rxjava-core/src/main/java/rx/util/functions/Functions.java b/rxjava-core/src/main/java/rx/util/functions/Functions.java index 30728600a8..356ce41173 100644 --- a/rxjava-core/src/main/java/rx/util/functions/Functions.java +++ b/rxjava-core/src/main/java/rx/util/functions/Functions.java @@ -327,6 +327,10 @@ public static Func1 alwaysTrue() { return AlwaysTrue.INSTANCE; } + public static Func1 alwaysFalse() { + return AlwaysFalse.INSTANCE; + } + public static Func1 identity() { return new Func1() { @Override @@ -345,4 +349,12 @@ public Boolean call(Object o) { } } + private enum AlwaysFalse implements Func1 { + INSTANCE; + + @Override + public Boolean call(Object o) { + return false; + } + } } diff --git a/rxjava-core/src/test/java/rx/ObservableTests.java b/rxjava-core/src/test/java/rx/ObservableTests.java index 470fdac18b..de0c7d6e81 100644 --- a/rxjava-core/src/test/java/rx/ObservableTests.java +++ b/rxjava-core/src/test/java/rx/ObservableTests.java @@ -838,4 +838,15 @@ public void testContainsWithEmptyObservable() { org.mockito.Matchers.any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } + + public void testIgnoreElements() { + Observable observable = Observable.from(1, 2, 3).ignoreElements(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(any(Integer.class)); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } } \ No newline at end of file From 3a23a9b4bf0a486fada64c9d110cdf10c26debf8 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 30 Sep 2013 13:50:47 +0800 Subject: [PATCH 152/333] Implemented the 'Throw' operator with scheduler --- rxjava-core/src/main/java/rx/Observable.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 2e11afc654..3aae9b15f0 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -578,11 +578,30 @@ public static Observable empty(Scheduler scheduler) { * @param * the type of the items (ostensibly) emitted by the Observable * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it + * @see MSDN: Observable.Throw Method */ public static Observable error(Throwable exception) { return new ThrowObservable(exception); } + /** + * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method with the specified scheduler. + *

+ * + * + * @param exception + * the particular error to report + * @param scheduler + * the scheduler to call the {@link Observer#onError onError} method. + * @param + * the type of the items (ostensibly) emitted by the Observable + * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} method with the specified scheduler. + * @see MSDN: Observable.Throw Method + */ + public static Observable error(Throwable exception, Scheduler scheduler) { + return Observable. error(exception).observeOn(scheduler); + } + /** * Converts an {@link Iterable} sequence into an Observable. *

From 841764595e9fb13d11199aa8ad8cfcfbdd598589 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Wed, 9 Oct 2013 16:00:01 +0800 Subject: [PATCH 153/333] Used 'subscribeOn' instead of 'observeOn' --- rxjava-core/src/main/java/rx/Observable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 3aae9b15f0..6b226b24d0 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -599,7 +599,7 @@ public static Observable error(Throwable exception) { * @see MSDN: Observable.Throw Method */ public static Observable error(Throwable exception, Scheduler scheduler) { - return Observable. error(exception).observeOn(scheduler); + return Observable. error(exception).subscribeOn(scheduler); } /** From cb08eba223964bf2cbef42ac3a9b52038820cb37 Mon Sep 17 00:00:00 2001 From: Matthias Kaeppler Date: Wed, 9 Oct 2013 23:34:21 +0200 Subject: [PATCH 154/333] Revised version of Android UI component operator --- rxjava-contrib/rxjava-android/build.gradle | 5 +- .../OperationObserveInForeground.java | 281 ++++++++++++++++++ 2 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveInForeground.java diff --git a/rxjava-contrib/rxjava-android/build.gradle b/rxjava-contrib/rxjava-android/build.gradle index f3b7745c81..144d3cd68a 100644 --- a/rxjava-contrib/rxjava-android/build.gradle +++ b/rxjava-contrib/rxjava-android/build.gradle @@ -2,10 +2,13 @@ apply plugin: 'osgi' dependencies { compile project(':rxjava-core') + provided 'com.google.android:android:4.0.1.2' + provided 'com.google.android:support-v4:r7' + + // testing provided 'junit:junit-dep:4.10' provided 'org.mockito:mockito-core:1.8.5' provided 'org.robolectric:robolectric:2.1.1' - provided 'com.google.android:android:4.0.1.2' } javadoc { diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveInForeground.java b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveInForeground.java new file mode 100644 index 0000000000..e3b1239429 --- /dev/null +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveInForeground.java @@ -0,0 +1,281 @@ +package rx.operators; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.android.concurrency.AndroidSchedulers; +import rx.subjects.PublishSubject; + +import android.app.Activity; +import android.app.Fragment; +import android.util.Log; + +import java.lang.ref.WeakReference; + +public class OperationObserveInForeground { + + public static Observable observeOnFragment(Observable source, android.app.Fragment fragment) { + return Observable.create(new OnSubscribeFragment(source, new WeakReference(fragment))); + } + + public static Observable observeOnSupportFragment(Observable source, android.support.v4.app.Fragment fragment) { + return Observable.create(new OnSubscribeSupportFragment( + source, new WeakReference(fragment))); + } + + public static Observable observeOnActivity(Observable source, Activity activity) { + return Observable.create(new OnSubscribeActivity(source, new WeakReference(activity))); + } + + private static abstract class OnSubscribeBase implements Observable.OnSubscribeFunc { + + private final Observable source; + private final WeakReference componentRef; + private Observer actual; + + private OnSubscribeBase(Observable source, WeakReference componentRef) { + this.source = source; + this.componentRef = componentRef; + } + + protected abstract boolean isComponentValid(AndroidComponent component); + + @Override + public Subscription onSubscribe(Observer observer) { + actual = observer; + return source.observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer() { + @Override + public void onCompleted() { + AndroidComponent component = componentRef.get(); + if (component != null && isComponentValid(component)) { + actual.onCompleted(); + } else { + actual = null; + log("onComplete: target component released or detached; dropping message"); + } + } + + @Override + public void onError(Throwable e) { + AndroidComponent component = componentRef.get(); + if (component != null && isComponentValid(component)) { + actual.onError(e); + } else { + actual = null; + log("onError: target component released or detached; dropping message"); + } + } + + @Override + public void onNext(T args) { + AndroidComponent component = componentRef.get(); + if (component != null && isComponentValid(component)) { + actual.onNext(args); + } else { + actual = null; + log("onNext: target component released or detached; dropping message"); + } + } + + private void log(String message) { + Log.i(OperationObserveInForeground.class.getSimpleName(), message); + } + }); + } + } + + private static final class OnSubscribeFragment extends OnSubscribeBase { + + private OnSubscribeFragment(Observable source, WeakReference fragmentRef) { + super(source, fragmentRef); + } + + @Override + protected boolean isComponentValid(android.app.Fragment fragment) { + return fragment.isAdded(); + } + } + + private static final class OnSubscribeSupportFragment extends OnSubscribeBase { + + private OnSubscribeSupportFragment(Observable source, WeakReference fragment) { + super(source, fragment); + } + + @Override + protected boolean isComponentValid(android.support.v4.app.Fragment fragment) { + return fragment.isAdded(); + } + } + + private static final class OnSubscribeActivity extends OnSubscribeBase { + + private OnSubscribeActivity(Observable source, WeakReference activity) { + super(source, activity); + } + + @Override + protected boolean isComponentValid(Activity activity) { + return !activity.isFinishing(); + } + } + + @RunWith(RobolectricTestRunner.class) + @Config(manifest = Config.NONE) + public static final class UnitTest { + + @Mock + private Observer mockObserver; + + @Mock + private Fragment mockFragment; + + @Mock + private Activity mockActivity; + + @Mock + private Observable mockObservable; + + @Before + public void setupMocks() { + MockitoAnnotations.initMocks(this); + when(mockFragment.isAdded()).thenReturn(true); + } + + @Test + public void itObservesTheSourceSequenceOnTheMainUIThread() { + OperationObserveInForeground.observeOnFragment(mockObservable, mockFragment).subscribe(mockObserver); + verify(mockObservable).observeOn(AndroidSchedulers.mainThread()); + } + + @Test + public void itForwardsOnNextOnCompletedSequenceToTargetObserver() { + Observable source = Observable.from(1, 2, 3); + OperationObserveInForeground.observeOnFragment(source, mockFragment).subscribe(mockObserver); + verify(mockObserver, times(3)).onNext(anyInt()); + verify(mockObserver).onCompleted(); + verify(mockObserver, never()).onError(any(Exception.class)); + } + + @Test + public void itForwardsOnErrorToTargetObserver() { + final Exception exception = new Exception(); + Observable source = Observable.error(exception); + OperationObserveInForeground.observeOnFragment(source, mockFragment).subscribe(mockObserver); + verify(mockObserver).onError(exception); + verify(mockObserver, never()).onNext(anyInt()); + verify(mockObserver, never()).onCompleted(); + } + + @Test + public void itDropsOnNextOnCompletedSequenceIfTargetComponentIsGone() { + PublishSubject source = PublishSubject.create(); + WeakReference fragmentRef = new WeakReference(mockFragment); + + final OnSubscribeFragment operator = new OnSubscribeFragment(source, fragmentRef); + operator.onSubscribe(mockObserver); + + source.onNext(1); + fragmentRef.clear(); + + source.onNext(2); + source.onNext(3); + source.onCompleted(); + + verify(mockObserver).onNext(1); + verifyNoMoreInteractions(mockObserver); + } + + @Test + public void itDropsOnErrorIfTargetComponentIsGone() { + PublishSubject source = PublishSubject.create(); + WeakReference fragmentRef = new WeakReference(mockFragment); + + final OnSubscribeFragment operator = new OnSubscribeFragment(source, fragmentRef); + operator.onSubscribe(mockObserver); + + source.onNext(1); + fragmentRef.clear(); + + source.onError(new Exception()); + + verify(mockObserver).onNext(1); + verifyNoMoreInteractions(mockObserver); + } + + @Test + public void itDoesNotForwardOnNextOnCompletedSequenceIfFragmentIsDetached() { + PublishSubject source = PublishSubject.create(); + OperationObserveInForeground.observeOnFragment(source, mockFragment).subscribe(mockObserver); + + source.onNext(1); + + when(mockFragment.isAdded()).thenReturn(false); + source.onNext(2); + source.onNext(3); + source.onCompleted(); + + verify(mockObserver).onNext(1); + verify(mockObserver, never()).onCompleted(); + } + + @Test + public void itDoesNotForwardOnErrorIfFragmentIsDetached() { + PublishSubject source = PublishSubject.create(); + OperationObserveInForeground.observeOnFragment(source, mockFragment).subscribe(mockObserver); + + source.onNext(1); + + when(mockFragment.isAdded()).thenReturn(false); + source.onError(new Exception()); + + verify(mockObserver).onNext(1); + verify(mockObserver, never()).onError(any(Exception.class)); + } + + @Test + public void isDoesNotForwardOnNextOnCompletedSequenceIfActivityIsFinishing() { + PublishSubject source = PublishSubject.create(); + OperationObserveInForeground.observeOnActivity(source, mockActivity).subscribe(mockObserver); + + source.onNext(1); + + when(mockActivity.isFinishing()).thenReturn(true); + source.onNext(2); + source.onNext(3); + source.onCompleted(); + + verify(mockObserver).onNext(1); + verify(mockObserver, never()).onCompleted(); + } + + @Test + public void itDoesNotForwardOnErrorIfActivityIsFinishing() { + PublishSubject source = PublishSubject.create(); + OperationObserveInForeground.observeOnActivity(source, mockActivity).subscribe(mockObserver); + + source.onNext(1); + + when(mockActivity.isFinishing()).thenReturn(true); + source.onError(new Exception()); + + verify(mockObserver).onNext(1); + verify(mockObserver, never()).onError(any(Exception.class)); + } + } +} From bd8bdbdd74b83ddfa810787662db793523f35839 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 9 Oct 2013 22:54:10 -0700 Subject: [PATCH 155/333] Version 0.14.3 --- CHANGES.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index bc6f9e5a8e..7f93a0b1b3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,19 @@ # RxJava Releases # +### Version 0.14.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.3%22)) ### + +* [Pull 407](https://github.com/Netflix/RxJava/pull/407) Implemented RefCount Operator +* [Pull 410](https://github.com/Netflix/RxJava/pull/410) Implemented the 'Contains' operator +* [Pull 411](https://github.com/Netflix/RxJava/pull/411) Unit Test fix: update counter before triggering latch +* [Pull 413](https://github.com/Netflix/RxJava/pull/413) Fixed the issues of takeLast(items, 0) and null values +* [Pull 414](https://github.com/Netflix/RxJava/pull/414) Implemented the 'SkipLast' operator +* [Pull 415](https://github.com/Netflix/RxJava/pull/415) Implemented the 'Empty' operator with scheduler +* [Pull 416](https://github.com/Netflix/RxJava/pull/416) Implemented the 'Throw' operator with scheduler +* [Pull 420](https://github.com/Netflix/RxJava/pull/420) Scala Adaptor Improvements +* [Pull 422](https://github.com/Netflix/RxJava/pull/422) JRuby function wrapping support +* [Pull 424](https://github.com/Netflix/RxJava/pull/424) Implemented the 'IgnoreElements' operator +* [Pull 426](https://github.com/Netflix/RxJava/pull/426) PublishSubject ReSubscribe for publish().refCount() Behavior + ### Version 0.14.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.2%22)) ### * [Pull 403](https://github.com/Netflix/RxJava/pull/403) Operators: Cast and OfType From 715dcece5c781c394b59f86e32f1f514fc9f7a31 Mon Sep 17 00:00:00 2001 From: Matthias Kaeppler Date: Thu, 10 Oct 2013 17:19:07 +0200 Subject: [PATCH 156/333] Drop use of WeakReferences and use a custom subscription instead --- .../java/rx/android/AndroidObservables.java | 23 +++ ...OperationObserveFromAndroidComponent.java} | 131 +++++++++++------- 2 files changed, 104 insertions(+), 50 deletions(-) create mode 100644 rxjava-contrib/rxjava-android/src/main/java/rx/android/AndroidObservables.java rename rxjava-contrib/rxjava-android/src/main/java/rx/operators/{OperationObserveInForeground.java => OperationObserveFromAndroidComponent.java} (61%) diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/android/AndroidObservables.java b/rxjava-contrib/rxjava-android/src/main/java/rx/android/AndroidObservables.java new file mode 100644 index 0000000000..db5768d43b --- /dev/null +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/android/AndroidObservables.java @@ -0,0 +1,23 @@ +package rx.android; + +import rx.Observable; +import rx.operators.OperationObserveFromAndroidComponent; + +import android.app.Activity; +import android.app.Fragment; + +public class AndroidObservables { + + public static Observable fromActivity(Activity activity, Observable sourceObservable) { + return OperationObserveFromAndroidComponent.observeFromAndroidComponent(sourceObservable, activity); + } + + public static Observable fromFragment(Fragment fragment, Observable sourceObservable) { + return OperationObserveFromAndroidComponent.observeFromAndroidComponent(sourceObservable, fragment); + } + + public static Observable fromFragment(android.support.v4.app.Fragment fragment, Observable sourceObservable) { + return OperationObserveFromAndroidComponent.observeFromAndroidComponent(sourceObservable, fragment); + } + +} diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveInForeground.java b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java similarity index 61% rename from rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveInForeground.java rename to rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java index e3b1239429..73f0ef44e7 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveInForeground.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java @@ -2,6 +2,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -25,84 +26,97 @@ import android.app.Fragment; import android.util.Log; -import java.lang.ref.WeakReference; +import java.lang.reflect.Field; -public class OperationObserveInForeground { +public class OperationObserveFromAndroidComponent { - public static Observable observeOnFragment(Observable source, android.app.Fragment fragment) { - return Observable.create(new OnSubscribeFragment(source, new WeakReference(fragment))); + public static Observable observeFromAndroidComponent(Observable source, android.app.Fragment fragment) { + return Observable.create(new OnSubscribeFragment(source, fragment)); } - public static Observable observeOnSupportFragment(Observable source, android.support.v4.app.Fragment fragment) { - return Observable.create(new OnSubscribeSupportFragment( - source, new WeakReference(fragment))); + public static Observable observeFromAndroidComponent(Observable source, android.support.v4.app.Fragment fragment) { + return Observable.create(new OnSubscribeSupportFragment(source, fragment)); } - public static Observable observeOnActivity(Observable source, Activity activity) { - return Observable.create(new OnSubscribeActivity(source, new WeakReference(activity))); + public static Observable observeFromAndroidComponent(Observable source, Activity activity) { + return Observable.create(new OnSubscribeActivity(source, activity)); } private static abstract class OnSubscribeBase implements Observable.OnSubscribeFunc { + private static final String LOG_TAG = OperationObserveFromAndroidComponent.class.getSimpleName(); + private final Observable source; - private final WeakReference componentRef; - private Observer actual; + private volatile AndroidComponent componentRef; + private volatile Observer observerRef; - private OnSubscribeBase(Observable source, WeakReference componentRef) { + private OnSubscribeBase(Observable source, AndroidComponent component) { this.source = source; - this.componentRef = componentRef; + this.componentRef = component; + } + + private void log(String message) { + Log.d(LOG_TAG, "componentRef = " + componentRef); + Log.d(LOG_TAG, "observerRef = " + observerRef); + Log.d(LOG_TAG, message); } protected abstract boolean isComponentValid(AndroidComponent component); @Override public Subscription onSubscribe(Observer observer) { - actual = observer; - return source.observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer() { + observerRef = observer; + final Subscription sourceSub = source.observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer() { @Override public void onCompleted() { - AndroidComponent component = componentRef.get(); - if (component != null && isComponentValid(component)) { - actual.onCompleted(); + if (componentRef != null && isComponentValid(componentRef)) { + observerRef.onCompleted(); } else { - actual = null; log("onComplete: target component released or detached; dropping message"); + releaseReferences(); } } @Override public void onError(Throwable e) { - AndroidComponent component = componentRef.get(); - if (component != null && isComponentValid(component)) { - actual.onError(e); + if (componentRef != null && isComponentValid(componentRef)) { + observerRef.onError(e); } else { - actual = null; log("onError: target component released or detached; dropping message"); + releaseReferences(); } } @Override public void onNext(T args) { - AndroidComponent component = componentRef.get(); - if (component != null && isComponentValid(component)) { - actual.onNext(args); + if (componentRef != null && isComponentValid(componentRef)) { + observerRef.onNext(args); } else { - actual = null; log("onNext: target component released or detached; dropping message"); + releaseReferences(); } } - - private void log(String message) { - Log.i(OperationObserveInForeground.class.getSimpleName(), message); - } }); + return new Subscription() { + @Override + public void unsubscribe() { + log("unsubscribing from source sequence"); + releaseReferences(); + sourceSub.unsubscribe(); + } + }; + } + + private void releaseReferences() { + observerRef = null; + componentRef = null; } } private static final class OnSubscribeFragment extends OnSubscribeBase { - private OnSubscribeFragment(Observable source, WeakReference fragmentRef) { - super(source, fragmentRef); + private OnSubscribeFragment(Observable source, android.app.Fragment fragment) { + super(source, fragment); } @Override @@ -113,7 +127,7 @@ protected boolean isComponentValid(android.app.Fragment fragment) { private static final class OnSubscribeSupportFragment extends OnSubscribeBase { - private OnSubscribeSupportFragment(Observable source, WeakReference fragment) { + private OnSubscribeSupportFragment(Observable source, android.support.v4.app.Fragment fragment) { super(source, fragment); } @@ -125,7 +139,7 @@ protected boolean isComponentValid(android.support.v4.app.Fragment fragment) { private static final class OnSubscribeActivity extends OnSubscribeBase { - private OnSubscribeActivity(Observable source, WeakReference activity) { + private OnSubscribeActivity(Observable source, Activity activity) { super(source, activity); } @@ -159,14 +173,14 @@ public void setupMocks() { @Test public void itObservesTheSourceSequenceOnTheMainUIThread() { - OperationObserveInForeground.observeOnFragment(mockObservable, mockFragment).subscribe(mockObserver); + OperationObserveFromAndroidComponent.observeFromAndroidComponent(mockObservable, mockFragment).subscribe(mockObserver); verify(mockObservable).observeOn(AndroidSchedulers.mainThread()); } @Test public void itForwardsOnNextOnCompletedSequenceToTargetObserver() { Observable source = Observable.from(1, 2, 3); - OperationObserveInForeground.observeOnFragment(source, mockFragment).subscribe(mockObserver); + OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); verify(mockObserver, times(3)).onNext(anyInt()); verify(mockObserver).onCompleted(); verify(mockObserver, never()).onError(any(Exception.class)); @@ -176,22 +190,21 @@ public void itForwardsOnNextOnCompletedSequenceToTargetObserver() { public void itForwardsOnErrorToTargetObserver() { final Exception exception = new Exception(); Observable source = Observable.error(exception); - OperationObserveInForeground.observeOnFragment(source, mockFragment).subscribe(mockObserver); + OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); verify(mockObserver).onError(exception); verify(mockObserver, never()).onNext(anyInt()); verify(mockObserver, never()).onCompleted(); } @Test - public void itDropsOnNextOnCompletedSequenceIfTargetComponentIsGone() { + public void itDropsOnNextOnCompletedSequenceIfTargetComponentIsGone() throws Throwable { PublishSubject source = PublishSubject.create(); - WeakReference fragmentRef = new WeakReference(mockFragment); - final OnSubscribeFragment operator = new OnSubscribeFragment(source, fragmentRef); + final OnSubscribeFragment operator = new OnSubscribeFragment(source, mockFragment); operator.onSubscribe(mockObserver); source.onNext(1); - fragmentRef.clear(); + releaseComponentRef(operator); source.onNext(2); source.onNext(3); @@ -202,15 +215,14 @@ public void itDropsOnNextOnCompletedSequenceIfTargetComponentIsGone() { } @Test - public void itDropsOnErrorIfTargetComponentIsGone() { + public void itDropsOnErrorIfTargetComponentIsGone() throws Throwable { PublishSubject source = PublishSubject.create(); - WeakReference fragmentRef = new WeakReference(mockFragment); - final OnSubscribeFragment operator = new OnSubscribeFragment(source, fragmentRef); + final OnSubscribeFragment operator = new OnSubscribeFragment(source, mockFragment); operator.onSubscribe(mockObserver); source.onNext(1); - fragmentRef.clear(); + releaseComponentRef(operator); source.onError(new Exception()); @@ -218,10 +230,16 @@ public void itDropsOnErrorIfTargetComponentIsGone() { verifyNoMoreInteractions(mockObserver); } + private void releaseComponentRef(OnSubscribeFragment operator) throws NoSuchFieldException, IllegalAccessException { + final Field componentRef = operator.getClass().getSuperclass().getDeclaredField("componentRef"); + componentRef.setAccessible(true); + componentRef.set(operator, null); + } + @Test public void itDoesNotForwardOnNextOnCompletedSequenceIfFragmentIsDetached() { PublishSubject source = PublishSubject.create(); - OperationObserveInForeground.observeOnFragment(source, mockFragment).subscribe(mockObserver); + OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); source.onNext(1); @@ -237,7 +255,7 @@ public void itDoesNotForwardOnNextOnCompletedSequenceIfFragmentIsDetached() { @Test public void itDoesNotForwardOnErrorIfFragmentIsDetached() { PublishSubject source = PublishSubject.create(); - OperationObserveInForeground.observeOnFragment(source, mockFragment).subscribe(mockObserver); + OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); source.onNext(1); @@ -251,7 +269,7 @@ public void itDoesNotForwardOnErrorIfFragmentIsDetached() { @Test public void isDoesNotForwardOnNextOnCompletedSequenceIfActivityIsFinishing() { PublishSubject source = PublishSubject.create(); - OperationObserveInForeground.observeOnActivity(source, mockActivity).subscribe(mockObserver); + OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockActivity).subscribe(mockObserver); source.onNext(1); @@ -267,7 +285,7 @@ public void isDoesNotForwardOnNextOnCompletedSequenceIfActivityIsFinishing() { @Test public void itDoesNotForwardOnErrorIfActivityIsFinishing() { PublishSubject source = PublishSubject.create(); - OperationObserveInForeground.observeOnActivity(source, mockActivity).subscribe(mockObserver); + OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockActivity).subscribe(mockObserver); source.onNext(1); @@ -277,5 +295,18 @@ public void itDoesNotForwardOnErrorIfActivityIsFinishing() { verify(mockObserver).onNext(1); verify(mockObserver, never()).onError(any(Exception.class)); } + + @Test + public void itUnsubscribesFromTheSourceSequence() { + Subscription underlying = mock(Subscription.class); + when(mockObservable.observeOn(AndroidSchedulers.mainThread())).thenReturn(mockObservable); + when(mockObservable.subscribe(any(Observer.class))).thenReturn(underlying); + + Subscription sub = OperationObserveFromAndroidComponent.observeFromAndroidComponent( + mockObservable, mockActivity).subscribe(mockObserver); + sub.unsubscribe(); + + verify(underlying).unsubscribe(); + } } } From 8cdf30fcbbcc6474fbea85c9eeeae52457294ce2 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 10 Oct 2013 19:05:30 -0700 Subject: [PATCH 157/333] Update build.gradle Commenting out rspec until until internal Netflix build system can handle ruby gems so that I can build and release 0.14.3 --- language-adaptors/rxjava-jruby/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/language-adaptors/rxjava-jruby/build.gradle b/language-adaptors/rxjava-jruby/build.gradle index eaefe7a81a..0369d6e36e 100644 --- a/language-adaptors/rxjava-jruby/build.gradle +++ b/language-adaptors/rxjava-jruby/build.gradle @@ -28,6 +28,7 @@ dependencies { rspec 'org.rubygems:rspec:2.14.1' } +/* // benjchristensen => commenting out until internal Netflix build system can handle ruby gems task(rspec, type: JavaExec) { main 'org.jruby.Main' classpath configurations.rspec + runtimeClasspath @@ -35,6 +36,7 @@ task(rspec, type: JavaExec) { } tasks.build.dependsOn << 'rspec' +*/ jar { manifest { From 2b329170cc3031b5c4d7ea932fdab235857189ec Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Fri, 11 Oct 2013 02:11:22 +0000 Subject: [PATCH 158/333] [Gradle Release Plugin] - pre tag commit: '0.14.3'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 38ee218094..fb6c3a55ee 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.3-SNAPSHOT +version=0.14.3 From 8eb2c7558f35ec4efdc41c539833bc41ad460a7d Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Fri, 11 Oct 2013 02:11:25 +0000 Subject: [PATCH 159/333] [Gradle Release Plugin] - new version commit: '0.14.4-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index fb6c3a55ee..4b2501ab94 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.3 +version=0.14.4-SNAPSHOT From ae073dd39ea99e2d661449dd948d7b4f619a6f40 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 10 Oct 2013 19:27:06 -0700 Subject: [PATCH 160/333] Update CHANGES.md --- CHANGES.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7f93a0b1b3..8275b2c2c4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,16 +2,16 @@ ### Version 0.14.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.3%22)) ### -* [Pull 407](https://github.com/Netflix/RxJava/pull/407) Implemented RefCount Operator -* [Pull 410](https://github.com/Netflix/RxJava/pull/410) Implemented the 'Contains' operator +* [Pull 407](https://github.com/Netflix/RxJava/pull/407) Operator: RefCount +* [Pull 410](https://github.com/Netflix/RxJava/pull/410) Operator: Contains * [Pull 411](https://github.com/Netflix/RxJava/pull/411) Unit Test fix: update counter before triggering latch * [Pull 413](https://github.com/Netflix/RxJava/pull/413) Fixed the issues of takeLast(items, 0) and null values -* [Pull 414](https://github.com/Netflix/RxJava/pull/414) Implemented the 'SkipLast' operator -* [Pull 415](https://github.com/Netflix/RxJava/pull/415) Implemented the 'Empty' operator with scheduler -* [Pull 416](https://github.com/Netflix/RxJava/pull/416) Implemented the 'Throw' operator with scheduler +* [Pull 414](https://github.com/Netflix/RxJava/pull/414) Operator: SkipLast +* [Pull 415](https://github.com/Netflix/RxJava/pull/415) Operator: Empty with scheduler +* [Pull 416](https://github.com/Netflix/RxJava/pull/416) Operator: Throw with scheduler * [Pull 420](https://github.com/Netflix/RxJava/pull/420) Scala Adaptor Improvements * [Pull 422](https://github.com/Netflix/RxJava/pull/422) JRuby function wrapping support -* [Pull 424](https://github.com/Netflix/RxJava/pull/424) Implemented the 'IgnoreElements' operator +* [Pull 424](https://github.com/Netflix/RxJava/pull/424) Operator: IgnoreElements * [Pull 426](https://github.com/Netflix/RxJava/pull/426) PublishSubject ReSubscribe for publish().refCount() Behavior ### Version 0.14.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.2%22)) ### From 60b97853cc0510abafba64404b68eb5ae2de908d Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 11 Oct 2013 21:26:01 +0200 Subject: [PATCH 161/333] update TODOs according to meeting --- language-adaptors/rxjava-scala/TODO.md | 48 ++++++++++++++++++++------ 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/language-adaptors/rxjava-scala/TODO.md b/language-adaptors/rxjava-scala/TODO.md index a3f4b8fd53..f465415344 100644 --- a/language-adaptors/rxjava-scala/TODO.md +++ b/language-adaptors/rxjava-scala/TODO.md @@ -2,21 +2,49 @@ TODOs for Scala Adapter ----------------------- -This is a (probably incomplete) list of what still needs to be done in the Scala adaptor: +This is a (probably incomplete) list of what still needs to be done in the Scala adaptor. + +TODOs which came up at the meeting with Erik Meijer on 2013-10-11: + +* Rename the factory methods in `object Observable`, considering that the most important is the one taking an `Observer => Subscription` function (the "king" according to Erik). Thunk to Subscription conversion (?), also consider Jason's [comments](https://github.com/Netflix/RxJava/commit/c1596253fc5567b7cc37d20128374d189471ff79). A useful trick might also be to have `apply(T, T, T*)` instead of just `apply(T*)`. +* Factory methods for observables and instance methods should take implicit scheduler, default is different per method (Isn't this a contradiction? In other words: If I call a method without providing a scheduler, should the default scheduler be used or the implicit that I provided?) Find in .NET source the list of which schedulers goes with which operators by default. If no other default, use NewThreadScheduler. Note that there are methods in Scala Observable which should have an overload taking a Scheduler, but do not yet have it! Also remember Erik saying that he would like to "minimize magically injected concurrency". +* Bring `BooleanSubscription`, `CompositeSubscription`, `MultipleAssignmentSubscription` to Scala, `compositeSubscription.subscription = ...`instead of setter method, add on `CompositeSubscription` should be `+=` +* Convert executor to scheduler +* Java Scheduler methods take `state` arguments (they were needed to schedule work on a different machine, but are now considered a bad idea). Remove these `state` arguments from all Scala schedulers. +* Check if TestScheduler added in 0.14.3 is sufficient +* Infinite Iterables: the java Observable.from version unfolds an iterable, even it is infinite. Should be fixed in java. +* subscribe methods: There are many overloads, but still not all combinations one might need. Make this nicer and complete, maybe using default arguments. Also try to make sure that `subscribe(println)` works, not only `subscribe(println(_))`. `foreach(println)` works on collections, but not on `subscribe(println)`, because subscribe is overloaded. +* Currently all Swing examples use Swing directly, without using the Scala wrappers around Swing. Make sure that everything is nice if the Scala wrappers around Swing are used, eg `Reaction { case clicked: ButtonClicked => … }` -- avoid default case, check out the hooks for partial function applyOrElse to avoid double pattern matching +* There are no examples yet using `async`, but `async` will be used in the course. Write examples and check if everything works as expected when combined with `async`. +* Futures: For the moment, just add a Future->Observable converter method to `object Observable`. Later, think if `Future[T] extends Observable[T]`. +* Operator `delay`: Once Erik has commented on [this](https://github.com/Netflix/RxJava/pull/384), make sure this operator is added accordingly to RxJava and then to RxScala +* go through Erik's code that he showed at the meeting and check if everything can now be done nicely +* get Erik's slides from the course and check if they are compatible with the library + +Some more TODOs: * Integrating Scala Futures: Should there be a common base interface for Futures and Observables? And if all subscribers of an Observable wrapping a Future unsubscribe, the Future should be cancelled, but Futures do not support cancellation. * Add methods present in Scala collections library, but not in RxJava, e.g. aggregate à la Scala, collect, tails, ... * combineLatest with arities > 2 -* Implicit schedulers? * Avoid text duplication in scaladoc using templates, add examples, distinction between use case signature and full signature -* other small TODOs +* other small TODOs in source code +* always: keep Scala Observable in sync with Java Observable + + +### Appendix: + +`println` example: + + List(1, 2).foreach(println) + Observable(1, 2).toBlockingObservable.foreach(println) + + Observable(1, 2).subscribe(println) // does not work + + class Ob[+T] { + def subscribe(onNext: T => Unit) = ??? + } + val o2 = new Ob[Int] + o2.subscribe(println) // works -(Implicit) schedulers for interval: Options: -```scala -def interval(duration: Duration)(implicit scheduler: Scheduler): Observable[Long] -def interval(duration: Duration)(scheduler: Scheduler): Observable[Long] -def interval(scheduler: Scheduler)(duration: Duration): Observable[Long] -def interval(duration: Duration, scheduler: Scheduler): Observable[Long] && def interval(duration: Duration): Observable[Long] -```` From ede1288d35c2350a1fad1d4e9fbc610811dab953 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Sat, 12 Oct 2013 15:49:21 +0800 Subject: [PATCH 162/333] Fixed issue #428 --- .../java/rx/operators/ChunkedOperation.java | 5 ++- .../java/rx/operators/OperationBuffer.java | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java b/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java index 5cd0845ea5..d68501d122 100644 --- a/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java +++ b/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java @@ -187,8 +187,11 @@ public void emitChunk(Chunk chunk) { return; } - subscription.unsubscribe(); + // Fixed issue 428. + // As unsubscribe will cancel the Future, and the currrent thread's interrupt status + // will be set. So we need to emit the chunk before unsubscribe. super.emitChunk(chunk); + subscription.unsubscribe(); createChunk(); } diff --git a/rxjava-core/src/main/java/rx/operators/OperationBuffer.java b/rxjava-core/src/main/java/rx/operators/OperationBuffer.java index 1cc559fb57..898260d0ef 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationBuffer.java +++ b/rxjava-core/src/main/java/rx/operators/OperationBuffer.java @@ -15,8 +15,11 @@ */ package rx.operators; +import static org.junit.Assert.assertFalse; + import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.junit.Before; @@ -37,6 +40,7 @@ import rx.util.Opening; import rx.util.Openings; import rx.util.functions.Action0; +import rx.util.functions.Action1; import rx.util.functions.Func0; import rx.util.functions.Func1; @@ -631,6 +635,40 @@ public Subscription onSubscribe(Observer observer) { inOrder.verify(observer, Mockito.times(1)).onCompleted(); } + @Test + public void testLongTimeAction() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + LongTimeAction action = new LongTimeAction(latch); + Observable.from(1).buffer(10, TimeUnit.MILLISECONDS, 10) + .subscribe(action); + latch.await(); + assertFalse(action.fail); + } + + private static class LongTimeAction implements Action1> { + + CountDownLatch latch; + boolean fail = false; + + public LongTimeAction(CountDownLatch latch) { + this.latch = latch; + } + + @Override + public void call(List t1) { + try { + if (fail) { + return; + } + Thread.sleep(200); + } catch (InterruptedException e) { + fail = true; + } finally { + latch.countDown(); + } + } + } + private List list(String... args) { List list = new ArrayList(); for (String arg : args) { From 2998ac3278f7c52ed49befb84985335f37ad7084 Mon Sep 17 00:00:00 2001 From: Matthias Kaeppler Date: Mon, 14 Oct 2013 10:14:25 +0200 Subject: [PATCH 163/333] Drop volatile in favor of failing fast if not subscribed from UI thread --- .../OperationObserveFromAndroidComponent.java | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java index 73f0ef44e7..8d945fba51 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java @@ -24,9 +24,14 @@ import android.app.Activity; import android.app.Fragment; +import android.os.Looper; import android.util.Log; import java.lang.reflect.Field; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; public class OperationObserveFromAndroidComponent { @@ -47,8 +52,8 @@ private static abstract class OnSubscribeBase implements Ob private static final String LOG_TAG = OperationObserveFromAndroidComponent.class.getSimpleName(); private final Observable source; - private volatile AndroidComponent componentRef; - private volatile Observer observerRef; + private AndroidComponent componentRef; + private Observer observerRef; private OnSubscribeBase(Observable source, AndroidComponent component) { this.source = source; @@ -65,6 +70,7 @@ private void log(String message) { @Override public Subscription onSubscribe(Observer observer) { + assertUiThread(); observerRef = observer; final Subscription sourceSub = source.observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer() { @Override @@ -111,6 +117,12 @@ private void releaseReferences() { observerRef = null; componentRef = null; } + + private void assertUiThread() { + if (Looper.getMainLooper() != Looper.myLooper()) { + throw new IllegalStateException("Observers must subscribe from the main UI thread, but was " + Thread.currentThread()); + } + } } private static final class OnSubscribeFragment extends OnSubscribeBase { @@ -171,6 +183,21 @@ public void setupMocks() { when(mockFragment.isAdded()).thenReturn(true); } + @Test + public void itThrowsIfObserverSubscribesFromBackgroundThread() throws Exception { + final Future future = Executors.newSingleThreadExecutor().submit(new Callable() { + @Override + public Object call() throws Exception { + OperationObserveFromAndroidComponent.observeFromAndroidComponent( + mockObservable, mockFragment).subscribe(mockObserver); + return null; + } + }); + future.get(1, TimeUnit.SECONDS); + verify(mockObserver).onError(any(IllegalStateException.class)); + verifyNoMoreInteractions(mockObserver); + } + @Test public void itObservesTheSourceSequenceOnTheMainUIThread() { OperationObserveFromAndroidComponent.observeFromAndroidComponent(mockObservable, mockFragment).subscribe(mockObserver); From 8e0343e6cbdbb861b44b1982769145f3d9686d69 Mon Sep 17 00:00:00 2001 From: Matthias Kaeppler Date: Mon, 14 Oct 2013 10:22:26 +0200 Subject: [PATCH 164/333] Only release references in unsubscribe We decided it's better to leave it to the caller when it's appropriate to release, cf. #1 --- .../rx/operators/OperationObserveFromAndroidComponent.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java index 8d945fba51..c19cb1076b 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java @@ -79,7 +79,6 @@ public void onCompleted() { observerRef.onCompleted(); } else { log("onComplete: target component released or detached; dropping message"); - releaseReferences(); } } @@ -89,7 +88,6 @@ public void onError(Throwable e) { observerRef.onError(e); } else { log("onError: target component released or detached; dropping message"); - releaseReferences(); } } @@ -99,7 +97,6 @@ public void onNext(T args) { observerRef.onNext(args); } else { log("onNext: target component released or detached; dropping message"); - releaseReferences(); } } }); From 50d38b72bdb091aba97e030f6143ee56e49c9708 Mon Sep 17 00:00:00 2001 From: Matthias Kaeppler Date: Mon, 14 Oct 2013 10:23:18 +0200 Subject: [PATCH 165/333] Add guards around log statements --- .../operators/OperationObserveFromAndroidComponent.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java index c19cb1076b..b2ce5b0bf6 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java @@ -61,9 +61,11 @@ private OnSubscribeBase(Observable source, AndroidComponent component) { } private void log(String message) { - Log.d(LOG_TAG, "componentRef = " + componentRef); - Log.d(LOG_TAG, "observerRef = " + observerRef); - Log.d(LOG_TAG, message); + if (Log.isLoggable(LOG_TAG, Log.DEBUG)) { + Log.d(LOG_TAG, "componentRef = " + componentRef); + Log.d(LOG_TAG, "observerRef = " + observerRef); + Log.d(LOG_TAG, message); + } } protected abstract boolean isComponentValid(AndroidComponent component); From fa83439e60360033c69b5f8925487cec611f83ae Mon Sep 17 00:00:00 2001 From: Matthias Kaeppler Date: Mon, 14 Oct 2013 10:26:44 +0200 Subject: [PATCH 166/333] Remove isFinishing check when calling back to activities We decided it's sufficient for the callback to be safe as long as the caller unsubscribes in onDestroy --- .../OperationObserveFromAndroidComponent.java | 50 ++----------------- 1 file changed, 5 insertions(+), 45 deletions(-) diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java index b2ce5b0bf6..ad363f7912 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java @@ -44,10 +44,10 @@ public static Observable observeFromAndroidComponent(Observable source } public static Observable observeFromAndroidComponent(Observable source, Activity activity) { - return Observable.create(new OnSubscribeActivity(source, activity)); + return Observable.create(new OnSubscribeBase(source, activity)); } - private static abstract class OnSubscribeBase implements Observable.OnSubscribeFunc { + private static class OnSubscribeBase implements Observable.OnSubscribeFunc { private static final String LOG_TAG = OperationObserveFromAndroidComponent.class.getSimpleName(); @@ -68,7 +68,9 @@ private void log(String message) { } } - protected abstract boolean isComponentValid(AndroidComponent component); + protected boolean isComponentValid(AndroidComponent component) { + return true; + } @Override public Subscription onSubscribe(Observer observer) { @@ -148,18 +150,6 @@ protected boolean isComponentValid(android.support.v4.app.Fragment fragment) { } } - private static final class OnSubscribeActivity extends OnSubscribeBase { - - private OnSubscribeActivity(Observable source, Activity activity) { - super(source, activity); - } - - @Override - protected boolean isComponentValid(Activity activity) { - return !activity.isFinishing(); - } - } - @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) public static final class UnitTest { @@ -292,36 +282,6 @@ public void itDoesNotForwardOnErrorIfFragmentIsDetached() { verify(mockObserver, never()).onError(any(Exception.class)); } - @Test - public void isDoesNotForwardOnNextOnCompletedSequenceIfActivityIsFinishing() { - PublishSubject source = PublishSubject.create(); - OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockActivity).subscribe(mockObserver); - - source.onNext(1); - - when(mockActivity.isFinishing()).thenReturn(true); - source.onNext(2); - source.onNext(3); - source.onCompleted(); - - verify(mockObserver).onNext(1); - verify(mockObserver, never()).onCompleted(); - } - - @Test - public void itDoesNotForwardOnErrorIfActivityIsFinishing() { - PublishSubject source = PublishSubject.create(); - OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockActivity).subscribe(mockObserver); - - source.onNext(1); - - when(mockActivity.isFinishing()).thenReturn(true); - source.onError(new Exception()); - - verify(mockObserver).onNext(1); - verify(mockObserver, never()).onError(any(Exception.class)); - } - @Test public void itUnsubscribesFromTheSourceSequence() { Subscription underlying = mock(Subscription.class); From 669c7b6bb39a46f4509cb5000ae704ad70ccafd0 Mon Sep 17 00:00:00 2001 From: John Marks Date: Mon, 14 Oct 2013 23:23:20 +0100 Subject: [PATCH 167/333] SerialSubscription and Timeout operator --- rxjava-core/src/main/java/rx/Observable.java | 82 ++++------- .../java/rx/operators/OperationTimeout.java | 128 ++++++++++++++++++ .../rx/subscriptions/SerialSubscription.java | 62 +++++++++ .../SerialSubscriptionTests.java | 75 ++++++++++ 4 files changed, 292 insertions(+), 55 deletions(-) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationTimeout.java create mode 100644 rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java create mode 100644 rxjava-core/src/test/java/rx/subscriptions/SerialSubscriptionTests.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 5cd876adba..9e3724e830 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -28,61 +28,7 @@ import rx.observables.BlockingObservable; import rx.observables.ConnectableObservable; import rx.observables.GroupedObservable; -import rx.operators.OperationAll; -import rx.operators.OperationAny; -import rx.operators.OperationAverage; -import rx.operators.OperationBuffer; -import rx.operators.OperationCache; -import rx.operators.OperationCast; -import rx.operators.OperationCombineLatest; -import rx.operators.OperationConcat; -import rx.operators.OperationDebounce; -import rx.operators.OperationDefaultIfEmpty; -import rx.operators.OperationDefer; -import rx.operators.OperationDematerialize; -import rx.operators.OperationDistinct; -import rx.operators.OperationDistinctUntilChanged; -import rx.operators.OperationElementAt; -import rx.operators.OperationFilter; -import rx.operators.OperationFinally; -import rx.operators.OperationFirstOrDefault; -import rx.operators.OperationGroupBy; -import rx.operators.OperationInterval; -import rx.operators.OperationMap; -import rx.operators.OperationMaterialize; -import rx.operators.OperationMerge; -import rx.operators.OperationMergeDelayError; -import rx.operators.OperationMulticast; -import rx.operators.OperationObserveOn; -import rx.operators.OperationOnErrorResumeNextViaFunction; -import rx.operators.OperationOnErrorResumeNextViaObservable; -import rx.operators.OperationOnErrorReturn; -import rx.operators.OperationOnExceptionResumeNextViaObservable; -import rx.operators.OperationParallel; -import rx.operators.OperationRetry; -import rx.operators.OperationSample; -import rx.operators.OperationScan; -import rx.operators.OperationSkip; -import rx.operators.OperationSkipLast; -import rx.operators.OperationSkipWhile; -import rx.operators.OperationSubscribeOn; -import rx.operators.OperationSum; -import rx.operators.OperationSwitch; -import rx.operators.OperationSynchronize; -import rx.operators.OperationTake; -import rx.operators.OperationTakeLast; -import rx.operators.OperationTakeUntil; -import rx.operators.OperationTakeWhile; -import rx.operators.OperationThrottleFirst; -import rx.operators.OperationTimestamp; -import rx.operators.OperationToObservableFuture; -import rx.operators.OperationToObservableIterable; -import rx.operators.OperationToObservableList; -import rx.operators.OperationToObservableSortedList; -import rx.operators.OperationWindow; -import rx.operators.OperationZip; -import rx.operators.SafeObservableSubscription; -import rx.operators.SafeObserver; +import rx.operators.*; import rx.plugins.RxJavaErrorHandler; import rx.plugins.RxJavaObservableExecutionHook; import rx.plugins.RxJavaPlugins; @@ -4507,6 +4453,32 @@ public Observable ignoreElements() { return filter(alwaysFalse()); } + /** + * Returns either the observable sequence or an TimeoutException if timeout elapses. + * @param timeout + * The timeout duration + * @param timeUnit + * The time unit of the timeout + * @param scheduler + * The scheduler to run the timeout timers on. + * @return The source sequence with a TimeoutException in case of a timeout. + */ + public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { + return create(OperationTimeout.timeout(this, timeout, timeUnit, scheduler)); + } + + /** + * Returns either the observable sequence or an TimeoutException if timeout elapses. + * @param timeout + * The timeout duration + * @param timeUnit + * The time unit of the timeout + * @return The source sequence with a TimeoutException in case of a timeout. + */ + public Observable timeout(long timeout, TimeUnit timeUnit) { + return create(OperationTimeout.timeout(this, timeout, timeUnit, Schedulers.threadPoolForComputation())); + } + /** * Whether a given {@link Function} is an internal implementation inside rx.* packages or not. *

diff --git a/rxjava-core/src/main/java/rx/operators/OperationTimeout.java b/rxjava-core/src/main/java/rx/operators/OperationTimeout.java new file mode 100644 index 0000000000..99c8c167b6 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationTimeout.java @@ -0,0 +1,128 @@ +/** + * Copyright 2013 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.operators; + +import rx.Observable; +import rx.Observer; +import rx.Scheduler; +import rx.Subscription; +import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.SerialSubscription; +import rx.util.functions.Action0; +import rx.util.functions.Func0; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +public final class OperationTimeout { + public static Observable.OnSubscribeFunc timeout(Observable source, long timeout, TimeUnit timeUnit, Scheduler scheduler) { + return new Timeout(source, timeout, timeUnit, scheduler); + } + + private static class Timeout implements Observable.OnSubscribeFunc { + private final Observable source; + private final long timeout; + private final TimeUnit timeUnit; + private final Scheduler scheduler; + + @Override + public Subscription onSubscribe(final Observer observer) { + final AtomicBoolean terminated = new AtomicBoolean(false); + final AtomicLong actual = new AtomicLong(0L); // Required to handle race between onNext and timeout + final SerialSubscription serial = new SerialSubscription(); + final Object gate = new Object(); + CompositeSubscription composite = new CompositeSubscription(); + final Func0 schedule = new Func0() { + @Override + public Subscription call() { + final long expected = actual.get(); + return scheduler.schedule(new Action0() { + @Override + public void call() { + boolean timeoutWins = false; + synchronized (gate) { + if (expected == actual.get() && !terminated.getAndSet(true)) { + timeoutWins = true; + } + } + if (timeoutWins) { + observer.onError(new TimeoutException()); + } + + } + }, timeout, timeUnit); + } + }; + SafeObservableSubscription subscription = new SafeObservableSubscription(); + composite.add(subscription.wrap(source.subscribe(new Observer() { + @Override + public void onNext(T value) { + boolean onNextWins = false; + synchronized (gate) { + if (!terminated.get()) { + actual.incrementAndGet(); + onNextWins = true; + } + } + if (onNextWins) { + serial.setSubscription(schedule.call()); + observer.onNext(value); + } + } + + @Override + public void onError(Throwable error) { + boolean onErrorWins = false; + synchronized (gate) { + if (!terminated.getAndSet(true)) { + onErrorWins = true; + } + } + if (onErrorWins) { + serial.unsubscribe(); + observer.onError(error); + } + } + + @Override + public void onCompleted() { + boolean onCompletedWins = false; + synchronized (gate) { + if (!terminated.getAndSet(true)) { + onCompletedWins = true; + } + } + if (onCompletedWins) { + serial.unsubscribe(); + observer.onCompleted(); + } + } + }))); + composite.add(serial); + serial.setSubscription(schedule.call()); + return composite; + } + + private Timeout(Observable source, long timeout, TimeUnit timeUnit, Scheduler scheduler) { + this.source = source; + this.timeout = timeout; + this.timeUnit = timeUnit; + this.scheduler = scheduler; + } + } +} diff --git a/rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java b/rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java new file mode 100644 index 0000000000..a39ff45a10 --- /dev/null +++ b/rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java @@ -0,0 +1,62 @@ +/** + * Copyright 2013 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.subscriptions; + +import rx.Subscription; + +/** + * Represents a subscription whose underlying subscription can be swapped for another subscription + * which causes the previous underlying subscription to be unsubscribed. + * + * @see Rx.Net equivalent SerialDisposable + */ +public class SerialSubscription implements Subscription { + private boolean unsubscribed; + private Subscription subscription; + private final Object gate = new Object(); + + @Override + public void unsubscribe() { + synchronized (gate) { + if (!unsubscribed) { + if (subscription != null) { + subscription.unsubscribe(); + subscription = null; + } + unsubscribed = true; + } + } + } + + public Subscription getSubscription() { + synchronized (gate) { + return subscription; + } + } + + public void setSubscription(Subscription subscription) { + synchronized (gate) { + if (!unsubscribed) { + if (this.subscription != null) { + this.subscription.unsubscribe(); + } + this.subscription = subscription; + } else { + subscription.unsubscribe(); + } + } + } +} diff --git a/rxjava-core/src/test/java/rx/subscriptions/SerialSubscriptionTests.java b/rxjava-core/src/test/java/rx/subscriptions/SerialSubscriptionTests.java new file mode 100644 index 0000000000..4ffc5cef27 --- /dev/null +++ b/rxjava-core/src/test/java/rx/subscriptions/SerialSubscriptionTests.java @@ -0,0 +1,75 @@ +/** + * Copyright 2013 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.subscriptions; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; +import rx.Subscription; + +import static org.mockito.Mockito.*; + +public class SerialSubscriptionTests { + private SerialSubscription serialSubscription; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + serialSubscription = new SerialSubscription(); + } + + @Test + public void unsubscribingWithoutUnderlyingDoesNothing() { + serialSubscription.unsubscribe(); + } + + @Test + public void unsubscribingWithSingleUnderlyingUnsubscribes() { + Subscription underlying = mock(Subscription.class); + serialSubscription.setSubscription(underlying); + underlying.unsubscribe(); + verify(underlying).unsubscribe(); + } + + @Test + public void replacingFirstUnderlyingCausesUnsubscription() { + Subscription first = mock(Subscription.class); + serialSubscription.setSubscription(first); + Subscription second = mock(Subscription.class); + serialSubscription.setSubscription(second); + verify(first).unsubscribe(); + } + + @Test + public void whenUnsubscribingSecondUnderlyingUnsubscribed() { + Subscription first = mock(Subscription.class); + serialSubscription.setSubscription(first); + Subscription second = mock(Subscription.class); + serialSubscription.setSubscription(second); + serialSubscription.unsubscribe(); + verify(second).unsubscribe(); + } + + @Test + public void settingUnderlyingWhenUnsubscribedCausesImmediateUnsubscription() + { + serialSubscription.unsubscribe(); + Subscription underlying = mock(Subscription.class); + serialSubscription.setSubscription(underlying); + verify(underlying).unsubscribe(); + } +} From 22eaa5e4472d2fc89732039d98a33af1aa94cf9a Mon Sep 17 00:00:00 2001 From: John Marks Date: Mon, 14 Oct 2013 23:31:43 +0100 Subject: [PATCH 168/333] SerialSubscription and Timeout operator --- .../main/java/rx/operators/OperationTimeout.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationTimeout.java b/rxjava-core/src/main/java/rx/operators/OperationTimeout.java index 99c8c167b6..03f5e515ac 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTimeout.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTimeout.java @@ -40,6 +40,13 @@ private static class Timeout implements Observable.OnSubscribeFunc { private final TimeUnit timeUnit; private final Scheduler scheduler; + private Timeout(Observable source, long timeout, TimeUnit timeUnit, Scheduler scheduler) { + this.source = source; + this.timeout = timeout; + this.timeUnit = timeUnit; + this.scheduler = scheduler; + } + @Override public Subscription onSubscribe(final Observer observer) { final AtomicBoolean terminated = new AtomicBoolean(false); @@ -117,12 +124,5 @@ public void onCompleted() { serial.setSubscription(schedule.call()); return composite; } - - private Timeout(Observable source, long timeout, TimeUnit timeUnit, Scheduler scheduler) { - this.source = source; - this.timeout = timeout; - this.timeUnit = timeUnit; - this.scheduler = scheduler; - } } } From facf9fca700659160909c6435103d8626d87e97f Mon Sep 17 00:00:00 2001 From: codecurve Date: Tue, 15 Oct 2013 17:10:12 +1300 Subject: [PATCH 169/333] Fixed path to RxScalaDemo.scala --- language-adaptors/rxjava-scala/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/README.md b/language-adaptors/rxjava-scala/README.md index 05160e8ad9..d198abcdb8 100644 --- a/language-adaptors/rxjava-scala/README.md +++ b/language-adaptors/rxjava-scala/README.md @@ -58,7 +58,7 @@ def never: Observable[Nothing] Also, the Scala Observable is fully covariant in its type parameter, whereas the Java Observable only achieves partial covariance due to limitations of Java's type system (or if you can fix this, your suggestions are very welcome). -For more examples, see [RxScalaDemo.scala](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala). +For more examples, see [RxScalaDemo.scala](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala). Scala code using Rx should only import members from `rx.lang.scala` and below. From 10da2ae1c826c688af3d1236eb8aac237f1db397 Mon Sep 17 00:00:00 2001 From: John Marks Date: Tue, 15 Oct 2013 11:18:14 +0100 Subject: [PATCH 170/333] Improved efficiency of SerialSubscription and unit tested Timeout --- .../rx/subscriptions/SerialSubscription.java | 14 ++- .../src/test/java/rx/TimeoutTests.java | 117 ++++++++++++++++++ 2 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 rxjava-core/src/test/java/rx/TimeoutTests.java diff --git a/rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java b/rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java index a39ff45a10..c1235afda6 100644 --- a/rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java +++ b/rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java @@ -30,15 +30,19 @@ public class SerialSubscription implements Subscription { @Override public void unsubscribe() { + Subscription toUnsubscribe = null; synchronized (gate) { if (!unsubscribed) { if (subscription != null) { - subscription.unsubscribe(); + toUnsubscribe = subscription; subscription = null; } unsubscribed = true; } } + if (toUnsubscribe != null) { + toUnsubscribe.unsubscribe(); + } } public Subscription getSubscription() { @@ -48,15 +52,19 @@ public Subscription getSubscription() { } public void setSubscription(Subscription subscription) { + Subscription toUnsubscribe = null; synchronized (gate) { if (!unsubscribed) { if (this.subscription != null) { - this.subscription.unsubscribe(); + toUnsubscribe = this.subscription; } this.subscription = subscription; } else { - subscription.unsubscribe(); + toUnsubscribe = subscription; } } + if (toUnsubscribe != null) { + toUnsubscribe.unsubscribe(); + } } } diff --git a/rxjava-core/src/test/java/rx/TimeoutTests.java b/rxjava-core/src/test/java/rx/TimeoutTests.java new file mode 100644 index 0000000000..532ae42eba --- /dev/null +++ b/rxjava-core/src/test/java/rx/TimeoutTests.java @@ -0,0 +1,117 @@ +/** + * Copyright 2013 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 org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; +import rx.concurrency.TestScheduler; +import rx.subjects.PublishSubject; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +public class TimeoutTests { + private PublishSubject underlyingSubject; + private TestScheduler testScheduler; + private Observable withTimeout; + private static final long TIMEOUT = 3; + private static final TimeUnit TIME_UNIT = TimeUnit.SECONDS; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + underlyingSubject = PublishSubject.create(); + testScheduler = new TestScheduler(); + withTimeout = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, testScheduler); + } + + @Test + public void shouldNotTimeoutIfOnNextWithinTimeout() { + Observer observer = mock(Observer.class); + Subscription subscription = withTimeout.subscribe(observer); + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + underlyingSubject.onNext("One"); + verify(observer).onNext("One"); + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + verify(observer, never()).onError(any(Throwable.class)); + subscription.unsubscribe(); + } + + @Test + public void shouldNotTimeoutIfSecondOnNextWithinTimeout() { + Observer observer = mock(Observer.class); + Subscription subscription = withTimeout.subscribe(observer); + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + underlyingSubject.onNext("One"); + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + underlyingSubject.onNext("Two"); + verify(observer).onNext("Two"); + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + verify(observer, never()).onError(any(Throwable.class)); + subscription.unsubscribe(); + } + + @Test + public void shouldTimeoutIfOnNextNotWithinTimeout() { + Observer observer = mock(Observer.class); + Subscription subscription = withTimeout.subscribe(observer); + testScheduler.advanceTimeBy(TIMEOUT + 1, TimeUnit.SECONDS); + verify(observer).onError(any(TimeoutException.class)); + subscription.unsubscribe(); + } + + @Test + public void shouldTimeoutIfSecondOnNextNotWithinTimeout() { + Observer observer = mock(Observer.class); + Subscription subscription = withTimeout.subscribe(observer); + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + underlyingSubject.onNext("One"); + verify(observer).onNext("One"); + testScheduler.advanceTimeBy(TIMEOUT + 1, TimeUnit.SECONDS); + verify(observer).onError(any(TimeoutException.class)); + subscription.unsubscribe(); + } + + @Test + public void shouldCompleteIfUnderlyingComletes() { + Observer observer = mock(Observer.class); + Subscription subscription = withTimeout.subscribe(observer); + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + underlyingSubject.onCompleted(); + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + verify(observer).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + subscription.unsubscribe(); + } + + @Test + public void shouldErrorIfUnderlyingErrors() { + Observer observer = mock(Observer.class); + Subscription subscription = withTimeout.subscribe(observer); + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + underlyingSubject.onError(new UnsupportedOperationException()); + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + verify(observer).onError(any(UnsupportedOperationException.class)); + subscription.unsubscribe(); + } +} From b58bc454dd6b64c39c22974d818c21d09518749d Mon Sep 17 00:00:00 2001 From: John Marks Date: Tue, 15 Oct 2013 11:37:23 +0100 Subject: [PATCH 171/333] Fixed a couple of warnings and reverted change to imports --- rxjava-core/src/main/java/rx/Observable.java | 63 ++++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 9e3724e830..8c8a0c4288 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -28,7 +28,62 @@ import rx.observables.BlockingObservable; import rx.observables.ConnectableObservable; import rx.observables.GroupedObservable; -import rx.operators.*; +import rx.operators.OperationAll; +import rx.operators.OperationAny; +import rx.operators.OperationAverage; +import rx.operators.OperationBuffer; +import rx.operators.OperationCache; +import rx.operators.OperationCast; +import rx.operators.OperationCombineLatest; +import rx.operators.OperationConcat; +import rx.operators.OperationDebounce; +import rx.operators.OperationDefaultIfEmpty; +import rx.operators.OperationDefer; +import rx.operators.OperationDematerialize; +import rx.operators.OperationDistinct; +import rx.operators.OperationDistinctUntilChanged; +import rx.operators.OperationElementAt; +import rx.operators.OperationFilter; +import rx.operators.OperationFinally; +import rx.operators.OperationFirstOrDefault; +import rx.operators.OperationGroupBy; +import rx.operators.OperationInterval; +import rx.operators.OperationMap; +import rx.operators.OperationMaterialize; +import rx.operators.OperationMerge; +import rx.operators.OperationMergeDelayError; +import rx.operators.OperationMulticast; +import rx.operators.OperationObserveOn; +import rx.operators.OperationOnErrorResumeNextViaFunction; +import rx.operators.OperationOnErrorResumeNextViaObservable; +import rx.operators.OperationOnErrorReturn; +import rx.operators.OperationOnExceptionResumeNextViaObservable; +import rx.operators.OperationParallel; +import rx.operators.OperationRetry; +import rx.operators.OperationSample; +import rx.operators.OperationScan; +import rx.operators.OperationSkip; +import rx.operators.OperationSkipLast; +import rx.operators.OperationSkipWhile; +import rx.operators.OperationSubscribeOn; +import rx.operators.OperationSum; +import rx.operators.OperationSwitch; +import rx.operators.OperationSynchronize; +import rx.operators.OperationTake; +import rx.operators.OperationTakeLast; +import rx.operators.OperationTakeUntil; +import rx.operators.OperationTakeWhile; +import rx.operators.OperationThrottleFirst; +import rx.operators.OperationTimeout; +import rx.operators.OperationTimestamp; +import rx.operators.OperationToObservableFuture; +import rx.operators.OperationToObservableIterable; +import rx.operators.OperationToObservableList; +import rx.operators.OperationToObservableSortedList; +import rx.operators.OperationWindow; +import rx.operators.OperationZip; +import rx.operators.SafeObservableSubscription; +import rx.operators.SafeObserver; import rx.plugins.RxJavaErrorHandler; import rx.plugins.RxJavaObservableExecutionHook; import rx.plugins.RxJavaPlugins; @@ -1801,8 +1856,6 @@ public static Observable switchOnNext(Observable - * the type of item emitted by the source Observable * @return an Observable that is a chronologically well-behaved version of the source * Observable, and that synchronously notifies its {@link Observer}s */ @@ -1822,8 +1875,6 @@ public Observable synchronize() { * * @param lock * The lock object to synchronize each observer call on - * @param - * the type of item emitted by the source Observable * @return an Observable that is a chronologically well-behaved version of the source * Observable, and that synchronously notifies its {@link Observer}s */ @@ -3140,7 +3191,7 @@ public Observable exists(Func1 predicate) { /** * Determines whether an observable sequence contains a specified element. * - * @param value + * @param element * The element to search in the sequence. * @return an Observable that emits if the element is in the source sequence. * @see MSDN: Observable.Contains From 18eed1a94e4e8f3911aa85c15b716271438924e8 Mon Sep 17 00:00:00 2001 From: Matt Jacobs Date: Tue, 15 Oct 2013 20:25:13 -0700 Subject: [PATCH 172/333] rx-apache-http now recognizes "Transfer-Encoding: chunked" as an HTTP stream --- .../consumers/ResponseConsumerDelegate.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java index 7eab30b441..b352ecb61a 100644 --- a/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java +++ b/rxjava-contrib/rxjava-apache-http/src/main/java/rx/apache/http/consumers/ResponseConsumerDelegate.java @@ -17,6 +17,7 @@ import java.io.IOException; +import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpException; import org.apache.http.HttpResponse; @@ -52,9 +53,7 @@ public ResponseConsumerDelegate(final Observer o @Override protected void onResponseReceived(HttpResponse response) throws HttpException, IOException { // when we receive the response with headers we evaluate what type of consumer we want - if (response.getFirstHeader("Content-Type").getValue().contains("text/event-stream")) { - // use 'contains' instead of equals since Content-Type can contain additional information - // such as charset ... see here: http://www.w3.org/International/O-HTTP-charset + if (responseIsStreamLike(response)) { consumer = new ResponseConsumerEventStream(observer, subscription); } else { consumer = new ResponseConsumerBasic(observer, subscription); @@ -63,6 +62,20 @@ protected void onResponseReceived(HttpResponse response) throws HttpException, I consumer._onResponseReceived(response); } + private boolean responseIsStreamLike(HttpResponse response) { + final Header contentType = response.getFirstHeader("Content-Type"); + // use 'contains' instead of equals since Content-Type can contain additional information + // such as charset ... see here: http://www.w3.org/International/O-HTTP-charset + if (contentType != null && contentType.getValue().contains("text/event-stream")) { + return true; + } + final Header transferEncoding = response.getFirstHeader("Transfer-Encoding"); + if (transferEncoding != null && transferEncoding.getValue().equals("chunked")) { + return true; + } + return false; + } + @Override protected void onContentReceived(ContentDecoder decoder, IOControl ioctrl) throws IOException { consumer._onContentReceived(decoder, ioctrl); From dc7a3f8f575edc28d86daa2039715866059a574d Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 15 Oct 2013 10:46:13 -0700 Subject: [PATCH 173/333] Scheduler overload with recursive support --- rxjava-core/src/main/java/rx/Scheduler.java | 82 ++++++++++++++----- .../java/rx/concurrency/TestSchedulers.java | 22 +++++ 2 files changed, 82 insertions(+), 22 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Scheduler.java b/rxjava-core/src/main/java/rx/Scheduler.java index 6fdfc286c0..cb3430c1f3 100644 --- a/rxjava-core/src/main/java/rx/Scheduler.java +++ b/rxjava-core/src/main/java/rx/Scheduler.java @@ -27,8 +27,11 @@ import org.mockito.Mockito; import rx.concurrency.TestScheduler; +import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.MultipleAssignmentSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import rx.util.functions.Action1; import rx.util.functions.Func1; import rx.util.functions.Func2; @@ -83,23 +86,23 @@ public abstract class Scheduler { * Schedules a cancelable action to be executed periodically. * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. - * - * @param state + * + * @param state * State to pass into the action. - * @param action + * @param action * The action to execute periodically. - * @param initialDelay + * @param initialDelay * Time to wait before executing the action for the first time. - * @param period + * @param period * The time interval to wait each time in between executing the action. - * @param unit + * @param unit * The time unit the interval above is given in. * @return A subscription to be able to unsubscribe from action. */ public Subscription schedulePeriodically(T state, final Func2 action, long initialDelay, long period, TimeUnit unit) { final long periodInNanos = unit.toNanos(period); final AtomicBoolean complete = new AtomicBoolean(); - + final Func2 recursiveAction = new Func2() { @Override public Subscription call(Scheduler scheduler, T state0) { @@ -128,7 +131,7 @@ public void call() { } }); } - + /** * Schedules a cancelable action to be executed at dueTime. * @@ -150,6 +153,40 @@ public Subscription schedule(T state, Func2 action) { + final CompositeSubscription parentSubscription = new CompositeSubscription(); + final MultipleAssignmentSubscription childSubscription = new MultipleAssignmentSubscription(); + parentSubscription.add(childSubscription); + + final Func2 parentAction = new Func2() { + + @Override + public Subscription call(final Scheduler scheduler, final Func2 parentAction) { + action.call(new Action0() { + + @Override + public void call() { + if (!parentSubscription.isUnsubscribed()) { + childSubscription.setSubscription(scheduler.schedule(parentAction, parentAction)); + } + } + + }); + return childSubscription; + } + }; + + parentSubscription.add(schedule(parentAction, parentAction)); + + return parentSubscription; + } /** * Schedules an action to be executed. @@ -187,17 +224,16 @@ public Subscription call(Scheduler scheduler, Void state) { }, delayTime, unit); } - /** * Schedules an action to be executed periodically. * - * @param action + * @param action * The action to execute periodically. - * @param initialDelay + * @param initialDelay * Time to wait before executing the action for the first time. - * @param period + * @param period * The time interval to wait each time in between executing the action. - * @param unit + * @param unit * The time unit the interval above is given in. * @return A subscription to be able to unsubscribe from action. */ @@ -230,39 +266,41 @@ public int degreeOfParallelism() { } public static class UnitTest { - @SuppressWarnings("unchecked") // mocking is unchecked, unfortunately + @SuppressWarnings("unchecked") + // mocking is unchecked, unfortunately @Test public void testPeriodicScheduling() { final Func1 calledOp = mock(Func1.class); - + final TestScheduler scheduler = new TestScheduler(); Subscription subscription = scheduler.schedulePeriodically(new Action0() { - @Override public void call() { + @Override + public void call() { System.out.println(scheduler.now()); calledOp.call(scheduler.now()); } }, 1, 2, TimeUnit.SECONDS); - + verify(calledOp, never()).call(anyLong()); InOrder inOrder = Mockito.inOrder(calledOp); - + scheduler.advanceTimeBy(999L, TimeUnit.MILLISECONDS); inOrder.verify(calledOp, never()).call(anyLong()); scheduler.advanceTimeBy(1L, TimeUnit.MILLISECONDS); inOrder.verify(calledOp, times(1)).call(1000L); - + scheduler.advanceTimeBy(1999L, TimeUnit.MILLISECONDS); inOrder.verify(calledOp, never()).call(3000L); - + scheduler.advanceTimeBy(1L, TimeUnit.MILLISECONDS); inOrder.verify(calledOp, times(1)).call(3000L); - + scheduler.advanceTimeBy(5L, TimeUnit.SECONDS); inOrder.verify(calledOp, times(1)).call(5000L); inOrder.verify(calledOp, times(1)).call(7000L); - + subscription.unsubscribe(); scheduler.advanceTimeBy(11L, TimeUnit.SECONDS); inOrder.verify(calledOp, never()).call(anyLong()); diff --git a/rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java b/rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java index 7d2851abf9..e1df828237 100644 --- a/rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java +++ b/rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java @@ -33,6 +33,7 @@ import rx.Subscription; import rx.subscriptions.BooleanSubscription; import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; import rx.util.functions.Action1; import rx.util.functions.Func1; import rx.util.functions.Func2; @@ -473,6 +474,27 @@ public Subscription onSubscribe(final Observer observer) { fail("Error: " + observer.error.get().getMessage()); } } + + @Test + public void testRecursion() { + TestScheduler s = new TestScheduler(); + + final AtomicInteger counter = new AtomicInteger(0); + + Subscription subscription = s.schedule(new Action1() { + + @Override + public void call(Action0 self) { + counter.incrementAndGet(); + System.out.println("counter: " + counter.get()); + self.call(); + } + + }); + subscription.unsubscribe(); + assertEquals(0, counter.get()); + } + /** * Used to determine if onNext is being invoked concurrently. From 6ef2530749fe3ee3dac36ffca218e884b988721f Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 15 Oct 2013 14:39:10 -0700 Subject: [PATCH 174/333] BugFix: unsubscribe was not propagating to parent Observable on merge(Observable>) --- .../java/rx/operators/OperationMerge.java | 80 ++++++++++++++++++- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationMerge.java b/rxjava-core/src/main/java/rx/operators/OperationMerge.java index 1152ee5637..2e5655b38f 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMerge.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMerge.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -36,6 +37,10 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; +import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; +import rx.util.functions.Action1; /** * Flattens a list of Observables into one Observable sequence, without any transformation. @@ -93,6 +98,7 @@ public Subscription onSubscribe(Observer> observ @Override public void unsubscribe() { + System.out.println("unsubscribe from merge"); unsubscribed = true; } @@ -125,6 +131,7 @@ private MergeObservable(Observable> sequences) } public Subscription onSubscribe(Observer actualObserver) { + CompositeSubscription completeSubscription = new CompositeSubscription(); /** * We must synchronize a merge because we subscribe to multiple sequences in parallel that will each be emitting. @@ -134,15 +141,16 @@ public Subscription onSubscribe(Observer actualObserver) { * Bug report: https://github.com/Netflix/RxJava/issues/200 */ SafeObservableSubscription subscription = new SafeObservableSubscription(ourSubscription); + completeSubscription.add(subscription); SynchronizedObserver synchronizedObserver = new SynchronizedObserver(actualObserver, subscription); /** * Subscribe to the parent Observable to get to the children Observables */ - sequences.subscribe(new ParentObserver(synchronizedObserver)); + completeSubscription.add(sequences.subscribe(new ParentObserver(synchronizedObserver))); /* return our subscription to allow unsubscribing */ - return subscription; + return completeSubscription; } /** @@ -380,6 +388,70 @@ public void testUnSubscribe() { verify(stringObserver, never()).onCompleted(); } + @Test + public void testUnSubscribeObservableOfObservables() throws InterruptedException { + + final AtomicBoolean unsubscribed = new AtomicBoolean(); + final CountDownLatch latch = new CountDownLatch(1); + + Observable> source = Observable.create(new OnSubscribeFunc>() { + + @Override + public Subscription onSubscribe(final Observer> observer) { + // verbose on purpose so I can track the inside of it + final Subscription s = Subscriptions.create(new Action0() { + + @Override + public void call() { + System.out.println("*** unsubscribed"); + unsubscribed.set(true); + } + + }); + + new Thread(new Runnable() { + + @Override + public void run() { + + while (!unsubscribed.get()) { + observer.onNext(Observable.from(1L, 2L)); + } + System.out.println("Done looping after unsubscribe: " + unsubscribed.get()); + observer.onCompleted(); + + // mark that the thread is finished + latch.countDown(); + } + }).start(); + + return s; + }; + + }); + + final AtomicInteger count = new AtomicInteger(); + Observable.create(merge(source)).take(6).toBlockingObservable().forEach(new Action1() { + + @Override + public void call(Long v) { + System.out.println("Value: " + v); + int c = count.incrementAndGet(); + if (c > 6) { + fail("Should be only 6"); + } + + } + }); + + latch.await(1000, TimeUnit.MILLISECONDS); + + System.out.println("unsubscribed: " + unsubscribed.get()); + + assertTrue(unsubscribed.get()); + + } + @Test public void testMergeArrayWithThreading() { final TestASynchronousObservable o1 = new TestASynchronousObservable(); @@ -453,9 +525,9 @@ public void onNext(String v) { // so I'm unfortunately reverting to using a Thread.sleep to allow the process scheduler time // to make sure after o1.onNextBeingSent and o2.onNextBeingSent are hit that the following // onNext is invoked. - + Thread.sleep(300); - + try { // in try/finally so threads are released via latch countDown even if assertion fails assertEquals(1, concurrentCounter.get()); } finally { From 183d6b1fcb7b23118d3769954b5b5329054253dc Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Wed, 16 Oct 2013 06:33:04 +0000 Subject: [PATCH 175/333] [Gradle Release Plugin] - pre tag commit: '0.14.4'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 4b2501ab94..8d56e0125b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.4-SNAPSHOT +version=0.14.4 From 791df188362e1ff85c4a5f62964335142ad0cacb Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Wed, 16 Oct 2013 06:33:07 +0000 Subject: [PATCH 176/333] [Gradle Release Plugin] - new version commit: '0.14.5-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8d56e0125b..bc744cf239 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.4 +version=0.14.5-SNAPSHOT From eee232436cb40fc04a03340906cba253ba17ffaa Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 15 Oct 2013 23:38:06 -0700 Subject: [PATCH 177/333] Version 0.14.4 --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 8275b2c2c4..16a6935d9a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # RxJava Releases # +### Version 0.14.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.4%22)) ### + +* [Issue 428](https://github.com/Netflix/RxJava/issues/428) Fix: buffer() using TimeAndSizeBasedChunks incorrectly forces thread into interrupted state +* [Pull 435](https://github.com/Netflix/RxJava/pull/435) rx-apache-http recognizes "Transfer-Encoding: chunked" as an HTTP stream +* [Pull 437](https://github.com/Netflix/RxJava/pull/437) Fixes: Scheduler and Merge + + ### Version 0.14.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.3%22)) ### * [Pull 407](https://github.com/Netflix/RxJava/pull/407) Operator: RefCount From 5657920300684c5dca108af0dd1b5efcbc84163b Mon Sep 17 00:00:00 2001 From: MarioAriasC Date: Wed, 16 Oct 2013 06:37:12 +0100 Subject: [PATCH 178/333] merge --- gradle/buildscript.gradle | 1 + language-adaptors/rxjava-kotlin/README.md | 0 language-adaptors/rxjava-kotlin/build.gradle | 19 +++++++++++++++++++ settings.gradle | 1 + 4 files changed, 21 insertions(+) create mode 100644 language-adaptors/rxjava-kotlin/README.md create mode 100644 language-adaptors/rxjava-kotlin/build.gradle diff --git a/gradle/buildscript.gradle b/gradle/buildscript.gradle index 0b6da7ce84..5697e0e04d 100644 --- a/gradle/buildscript.gradle +++ b/gradle/buildscript.gradle @@ -8,4 +8,5 @@ dependencies { classpath 'com.mapvine:gradle-cobertura-plugin:0.1' classpath 'gradle-release:gradle-release:1.1.5' classpath 'org.ajoberstar:gradle-git:0.5.0' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.6.+' } diff --git a/language-adaptors/rxjava-kotlin/README.md b/language-adaptors/rxjava-kotlin/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/language-adaptors/rxjava-kotlin/build.gradle b/language-adaptors/rxjava-kotlin/build.gradle new file mode 100644 index 0000000000..35858fc4ba --- /dev/null +++ b/language-adaptors/rxjava-kotlin/build.gradle @@ -0,0 +1,19 @@ +apply plugin: 'kotlin' +apply plugin: 'osgi' + +dependencies { + compile project(':rxjava-core') + compile 'org.jetbrains.kotlin:kotlin-stdlib:0.6.+' + provided 'junit:junit-dep:4.10' + provided 'org.mockito:mockito-core:1.8.5' +} + +jar { + manifest { + name = 'rxjava-kotlin' + instruction 'Bundle-Vendor', 'Netflix' + instruction 'Bundle-DocURL', 'https://github.com/Netflix/RxJava' + instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*' + instruction 'Fragment-Host', 'com.netflix.rxjava.core' + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 766bb264f6..01cf356da0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,6 +4,7 @@ include 'rxjava-core', \ 'language-adaptors:rxjava-clojure', \ 'language-adaptors:rxjava-jruby', \ 'language-adaptors:rxjava-scala', \ +'language-adaptors:rxjava-kotlin', \ 'rxjava-contrib:rxjava-swing', \ 'rxjava-contrib:rxjava-android', \ 'rxjava-contrib:rxjava-apache-http' From 78845850c1c523b28abfc9af07899aee5e3c01af Mon Sep 17 00:00:00 2001 From: MarioAriasC Date: Mon, 23 Sep 2013 07:59:54 +0100 Subject: [PATCH 179/333] Initial Kotlin support --- gradle/buildscript.gradle | 1 - language-adaptors/rxjava-kotlin/build.gradle | 10 ++ .../main/kotlin/rx/lang/kotlin/namespace.kt | 17 +++ .../kotlin/rx/lang/kotlin/BasicKotlinTests.kt | 138 ++++++++++++++++++ .../kotlin/rx/lang/kotlin/SchedulersTests.kt | 11 ++ 5 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 language-adaptors/rxjava-kotlin/src/main/kotlin/rx/lang/kotlin/namespace.kt create mode 100644 language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt create mode 100644 language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/SchedulersTests.kt diff --git a/gradle/buildscript.gradle b/gradle/buildscript.gradle index 5697e0e04d..0b6da7ce84 100644 --- a/gradle/buildscript.gradle +++ b/gradle/buildscript.gradle @@ -8,5 +8,4 @@ dependencies { classpath 'com.mapvine:gradle-cobertura-plugin:0.1' classpath 'gradle-release:gradle-release:1.1.5' classpath 'org.ajoberstar:gradle-git:0.5.0' - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.6.+' } diff --git a/language-adaptors/rxjava-kotlin/build.gradle b/language-adaptors/rxjava-kotlin/build.gradle index 35858fc4ba..f3e9f725a3 100644 --- a/language-adaptors/rxjava-kotlin/build.gradle +++ b/language-adaptors/rxjava-kotlin/build.gradle @@ -1,3 +1,13 @@ +buildscript { + repositories() { + mavenCentral() + } + + dependencies { + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.6.+' + } +} + apply plugin: 'kotlin' apply plugin: 'osgi' diff --git a/language-adaptors/rxjava-kotlin/src/main/kotlin/rx/lang/kotlin/namespace.kt b/language-adaptors/rxjava-kotlin/src/main/kotlin/rx/lang/kotlin/namespace.kt new file mode 100644 index 0000000000..26db46bfb1 --- /dev/null +++ b/language-adaptors/rxjava-kotlin/src/main/kotlin/rx/lang/kotlin/namespace.kt @@ -0,0 +1,17 @@ +package rx.lang.kotlin + +import rx.Subscription +import rx.Observer +import rx.Observable + +public fun Function1, Subscription>.asObservable(): Observable { + return Observable.create{ this(it!!) }!! +} + +public fun Iterable.asObservable(): Observable { + return Observable.from(this)!! +} + +public fun T.asObservable(): Observable { + return Observable.from(this)!! +} diff --git a/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt b/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt new file mode 100644 index 0000000000..a881d84f11 --- /dev/null +++ b/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt @@ -0,0 +1,138 @@ +package rx.lang.kotlin + +import org.mockito.Mock +import rx.Observable +import org.junit.Before +import org.mockito.MockitoAnnotations +import org.junit.Test +import rx.subscriptions.Subscriptions +import org.mockito.Mockito.* +import org.mockito.Matchers.* +import rx.Observer +import org.junit.Assert.* +import rx.Notification + +public class BasicKotlinTests { + + [Mock] var a: ScriptAssertion? = null + [Mock] var w: Observable? = null + + [Before] + public fun before() { + MockitoAnnotations.initMocks(this) + } + + [Test] + public fun testCreate() { + Observable.create{ + it!!.onNext("Hello") + it.onCompleted() + Subscriptions.empty() + }!!.subscribe { result -> + a!!.received(result) + } + + verify(a, times(1))!!.received("Hello") + } + [Test] + public fun testCreateEx() { + + {(observer: Observer) -> + observer.onNext("Hello") + observer.onCompleted() + Subscriptions.empty()!! + }.asObservable().subscribe { result -> + a!!.received(result) + } + + verify(a, times(1))!!.received("Hello") + } + + [Test] + public fun testFilter() { + Observable.from(1, 2, 3)!!.filter { it!! >= 2 }!!.subscribe { result -> + a!!.received(result) + } + verify(a, times(0))!!.received(1); + verify(a, times(1))!!.received(2); + verify(a, times(1))!!.received(3); + } + + [Test] + public fun testFilterEx() { + listOf(1, 2, 3).asObservable().filter { it!! >= 2 }!!.subscribe { result -> + a!!.received(result) + } + verify(a, times(0))!!.received(1); + verify(a, times(1))!!.received(2); + verify(a, times(1))!!.received(3); + } + + [Test] + public fun testLast() { + assertEquals("three", Observable.from("one", "two", "three")!!.toBlockingObservable()!!.last()) + } + + [Test] + public fun testLastEx() { + assertEquals("three", listOf("one", "two", "three").asObservable().toBlockingObservable()!!.last()) + } + + [Test] + public fun testLastWithPredicate() { + assertEquals("two", Observable.from("one", "two", "three")!!.toBlockingObservable()!!.last { x -> x!!.length == 3 }) + } + + [Test] + public fun testLastWithPredicateEx() { + assertEquals("two", listOf("one", "two", "three").asObservable().toBlockingObservable()!!.last { x -> x!!.length == 3 }) + } + + [Test] + public fun testMap1() { + Observable.from(1)!!.map { v -> "hello_$v" }!!.subscribe { result -> a!!.received(result) } + verify(a, times(1))!!.received("hello_1") + } + + [Test] + public fun testMap1Ex() { + 1.asObservable().map { v -> "hello_$v" }!!.subscribe { result -> a!!.received(result) } + verify(a, times(1))!!.received("hello_1") + } + + [Test] + public fun testMap2() { + Observable.from(1, 2, 3)!!.map { v -> "hello_$v" }!!.subscribe { result -> a!!.received(result) } + verify(a, times(1))!!.received("hello_1") + verify(a, times(1))!!.received("hello_2") + verify(a, times(1))!!.received("hello_3") + } + + [Test] + public fun testMap2Ex() { + listOf(1, 2, 3).asObservable().map { v -> "hello_$v" }!!.subscribe { result -> a!!.received(result) } + verify(a, times(1))!!.received("hello_1") + verify(a, times(1))!!.received("hello_2") + verify(a, times(1))!!.received("hello_3") + } + + [Test] + public fun testMaterialize() { + Observable.from(1, 2, 3)!!.materialize()!!.subscribe { result -> a!!.received(result) } + verify(a, times(4))!!.received(any(javaClass>())) + verify(a, times(0))!!.error(any(javaClass())) + } + + [Test] + public fun testMaterializeEx() { + listOf(1, 2, 3).asObservable().materialize()!!.subscribe { result -> a!!.received(result) } + verify(a, times(4))!!.received(any(javaClass>())) + verify(a, times(0))!!.error(any(javaClass())) + } + + public trait ScriptAssertion{ + fun error(e: Exception?) + + fun received(e: Any?) + } +} \ No newline at end of file diff --git a/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/SchedulersTests.kt b/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/SchedulersTests.kt new file mode 100644 index 0000000000..7a71f89e58 --- /dev/null +++ b/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/SchedulersTests.kt @@ -0,0 +1,11 @@ +package rx.lang.kotlin + +import org.junit.Test + + +public class SchedulersTests { + [Test] + public fun testComputationThreadPool(){ + + } +} \ No newline at end of file From 571f8286310d5ccd7572a97ad557dc0222b9f025 Mon Sep 17 00:00:00 2001 From: MarioAriasC Date: Fri, 20 Sep 2013 23:38:49 +0100 Subject: [PATCH 180/333] Gradle changes for Kotlin Support --- gradle/buildscript.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle/buildscript.gradle b/gradle/buildscript.gradle index 0b6da7ce84..5697e0e04d 100644 --- a/gradle/buildscript.gradle +++ b/gradle/buildscript.gradle @@ -8,4 +8,5 @@ dependencies { classpath 'com.mapvine:gradle-cobertura-plugin:0.1' classpath 'gradle-release:gradle-release:1.1.5' classpath 'org.ajoberstar:gradle-git:0.5.0' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.6.+' } From 7123ac561310861bfd1e37576c000d3fc7924f5e Mon Sep 17 00:00:00 2001 From: MarioAriasC Date: Mon, 23 Sep 2013 07:59:54 +0100 Subject: [PATCH 181/333] Initial Kotlin support --- gradle/buildscript.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/gradle/buildscript.gradle b/gradle/buildscript.gradle index 5697e0e04d..0b6da7ce84 100644 --- a/gradle/buildscript.gradle +++ b/gradle/buildscript.gradle @@ -8,5 +8,4 @@ dependencies { classpath 'com.mapvine:gradle-cobertura-plugin:0.1' classpath 'gradle-release:gradle-release:1.1.5' classpath 'org.ajoberstar:gradle-git:0.5.0' - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.6.+' } From 201f0537b7b75468142f0dbbec13226eb9909241 Mon Sep 17 00:00:00 2001 From: MarioAriasC Date: Sat, 28 Sep 2013 02:15:41 +0100 Subject: [PATCH 182/333] Basic Kotlin tests completed --- .../main/kotlin/rx/lang/kotlin/namespace.kt | 34 +- .../kotlin/rx/lang/kotlin/BasicKotlinTests.kt | 409 +++++++++++++++++- 2 files changed, 429 insertions(+), 14 deletions(-) diff --git a/language-adaptors/rxjava-kotlin/src/main/kotlin/rx/lang/kotlin/namespace.kt b/language-adaptors/rxjava-kotlin/src/main/kotlin/rx/lang/kotlin/namespace.kt index 26db46bfb1..a71aa680d9 100644 --- a/language-adaptors/rxjava-kotlin/src/main/kotlin/rx/lang/kotlin/namespace.kt +++ b/language-adaptors/rxjava-kotlin/src/main/kotlin/rx/lang/kotlin/namespace.kt @@ -5,7 +5,11 @@ import rx.Observer import rx.Observable public fun Function1, Subscription>.asObservable(): Observable { - return Observable.create{ this(it!!) }!! + return Observable.create { this(it!!) }!! +} + +public fun Function0>.defer(): Observable { + return Observable.defer(this)!! } public fun Iterable.asObservable(): Observable { @@ -15,3 +19,31 @@ public fun Iterable.asObservable(): Observable { public fun T.asObservable(): Observable { return Observable.from(this)!! } + +public fun Throwable.asObservable(): Observable { + return Observable.error(this)!! +} + +public fun Pair.asObservable(): Observable { + return Observable.from(this.component1(), this.component2())!! +} + +public fun Triple.asObservable(): Observable { + return Observable.from(this.component1(), this.component2(), this.component3())!! +} + +public fun Pair, Observable>.merge(): Observable { + return Observable.merge(this.component1(), this.component2())!! +} + +public fun Triple, Observable, Observable>.merge(): Observable { + return Observable.merge(this.component1(), this.component2(), this.component3())!! +} + +public fun Pair, Observable>.mergeDelayError(): Observable { + return Observable.mergeDelayError(this.component1(), this.component2())!! +} + +public fun Triple, Observable, Observable>.mergeDelayError(): Observable { + return Observable.mergeDelayError(this.component1(), this.component2(), this.component3())!! +} diff --git a/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt b/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt index a881d84f11..1c1515d714 100644 --- a/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt +++ b/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt @@ -11,6 +11,8 @@ import org.mockito.Matchers.* import rx.Observer import org.junit.Assert.* import rx.Notification +import rx.Subscription +import kotlin.concurrent.thread public class BasicKotlinTests { @@ -22,6 +24,10 @@ public class BasicKotlinTests { MockitoAnnotations.initMocks(this) } + fun received(): (T?) -> Unit { + return {(result: T?) -> a!!.received(result) } + } + [Test] public fun testCreate() { Observable.create{ @@ -34,6 +40,7 @@ public class BasicKotlinTests { verify(a, times(1))!!.received("Hello") } + [Test] public fun testCreateEx() { @@ -50,9 +57,7 @@ public class BasicKotlinTests { [Test] public fun testFilter() { - Observable.from(1, 2, 3)!!.filter { it!! >= 2 }!!.subscribe { result -> - a!!.received(result) - } + Observable.from(1, 2, 3)!!.filter { it!! >= 2 }!!.subscribe(received()) verify(a, times(0))!!.received(1); verify(a, times(1))!!.received(2); verify(a, times(1))!!.received(3); @@ -60,9 +65,7 @@ public class BasicKotlinTests { [Test] public fun testFilterEx() { - listOf(1, 2, 3).asObservable().filter { it!! >= 2 }!!.subscribe { result -> - a!!.received(result) - } + listOf(1, 2, 3).asObservable().filter { it!! >= 2 }!!.subscribe(received()) verify(a, times(0))!!.received(1); verify(a, times(1))!!.received(2); verify(a, times(1))!!.received(3); @@ -90,19 +93,19 @@ public class BasicKotlinTests { [Test] public fun testMap1() { - Observable.from(1)!!.map { v -> "hello_$v" }!!.subscribe { result -> a!!.received(result) } + Observable.from(1)!!.map { v -> "hello_$v" }!!.subscribe(received()) verify(a, times(1))!!.received("hello_1") } [Test] public fun testMap1Ex() { - 1.asObservable().map { v -> "hello_$v" }!!.subscribe { result -> a!!.received(result) } + 1.asObservable().map { v -> "hello_$v" }!!.subscribe((received())) verify(a, times(1))!!.received("hello_1") } [Test] public fun testMap2() { - Observable.from(1, 2, 3)!!.map { v -> "hello_$v" }!!.subscribe { result -> a!!.received(result) } + Observable.from(1, 2, 3)!!.map { v -> "hello_$v" }!!.subscribe((received())) verify(a, times(1))!!.received("hello_1") verify(a, times(1))!!.received("hello_2") verify(a, times(1))!!.received("hello_3") @@ -110,7 +113,7 @@ public class BasicKotlinTests { [Test] public fun testMap2Ex() { - listOf(1, 2, 3).asObservable().map { v -> "hello_$v" }!!.subscribe { result -> a!!.received(result) } + listOf(1, 2, 3).asObservable().map { v -> "hello_$v" }!!.subscribe((received())) verify(a, times(1))!!.received("hello_1") verify(a, times(1))!!.received("hello_2") verify(a, times(1))!!.received("hello_3") @@ -118,21 +121,401 @@ public class BasicKotlinTests { [Test] public fun testMaterialize() { - Observable.from(1, 2, 3)!!.materialize()!!.subscribe { result -> a!!.received(result) } + Observable.from(1, 2, 3)!!.materialize()!!.subscribe((received())) verify(a, times(4))!!.received(any(javaClass>())) verify(a, times(0))!!.error(any(javaClass())) } [Test] public fun testMaterializeEx() { - listOf(1, 2, 3).asObservable().materialize()!!.subscribe { result -> a!!.received(result) } + listOf(1, 2, 3).asObservable().materialize()!!.subscribe((received())) verify(a, times(4))!!.received(any(javaClass>())) verify(a, times(0))!!.error(any(javaClass())) } + [Test] + public fun testMergeDelayError() { + Observable.mergeDelayError( + Observable.from(1, 2, 3), + Observable.merge( + Observable.from(6), + Observable.error(NullPointerException()), + Observable.from(7) + ), + Observable.from(4, 5) + )!!.subscribe(received(), { e -> a!!.error(e) }) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(1))!!.received(3) + verify(a, times(1))!!.received(4) + verify(a, times(1))!!.received(5) + verify(a, times(1))!!.received(6) + verify(a, times(0))!!.received(7) + verify(a, times(1))!!.error(any(javaClass())) + } + + [Test] + public fun testMergeDelayErrorEx() { + + Triple(listOf(1, 2, 3).asObservable(), + Triple(6.asObservable(), + NullPointerException().asObservable(), + 7.asObservable() + ).merge(), + listOf(4, 5).asObservable() + ).mergeDelayError().subscribe(received(), { e -> a!!.error(e) }) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(1))!!.received(3) + verify(a, times(1))!!.received(4) + verify(a, times(1))!!.received(5) + verify(a, times(1))!!.received(6) + verify(a, times(0))!!.received(7) + verify(a, times(1))!!.error(any(javaClass())) + } + + [Test] + public fun testMerge() { + Observable.merge( + Observable.from(1, 2, 3), + Observable.merge( + Observable.from(6), + Observable.error(NullPointerException()), + Observable.from(7) + ), + Observable.from(4, 5) + )!!.subscribe(received(), { e -> a!!.error(e) }) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(1))!!.received(3) + verify(a, times(0))!!.received(4) + verify(a, times(0))!!.received(5) + verify(a, times(1))!!.received(6) + verify(a, times(0))!!.received(7) + verify(a, times(1))!!.error(any(javaClass())) + } + + [Test] + public fun testMergeEx() { + Triple(listOf(1, 2, 3).asObservable(), + Triple(6.asObservable(), + NullPointerException().asObservable(), + 7.asObservable() + ).merge(), + listOf(4, 5).asObservable() + ).merge().subscribe(received(), { e -> a!!.error(e) }) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(1))!!.received(3) + verify(a, times(0))!!.received(4) + verify(a, times(0))!!.received(5) + verify(a, times(1))!!.received(6) + verify(a, times(0))!!.received(7) + verify(a, times(1))!!.error(any(javaClass())) + } + + [Test] + public fun testScriptWithMaterialize() { + TestFactory().observable.materialize()!!.subscribe((received())) + verify(a, times(2))!!.received(any(javaClass>())) + } + + [Test] + public fun testScriptWithMerge() { + val factory = TestFactory() + Observable.merge(factory.observable, factory.observable)!!.subscribe((received())) + verify(a, times(1))!!.received("hello_1") + verify(a, times(1))!!.received("hello_2") + } + + [Test] + public fun testScriptWithMergeEx() { + val factory = TestFactory() + (factory.observable to factory.observable).merge().subscribe((received())) + verify(a, times(1))!!.received("hello_1") + verify(a, times(1))!!.received("hello_2") + } + + [Test] + public fun testFromWithIterable() { + val list = listOf(1, 2, 3, 4, 5) + assertEquals(5, Observable.from(list)!!.count()!!.toBlockingObservable()!!.single()) + } + + [Test] + public fun testFromWithIterableEx() { + assertEquals(5, listOf(1, 2, 3, 4, 5).asObservable().count()!!.toBlockingObservable()!!.single()) + } + + [Test] + public fun testFromWithObjects() { + val list = listOf(1, 2, 3, 4, 5) + assertEquals(2, Observable.from(list, 6)!!.count()!!.toBlockingObservable()!!.single()) + } + + [Test] + public fun testStartWith() { + val list = listOf(10, 11, 12, 13, 14) + val startList = listOf(1, 2, 3, 4, 5) + assertEquals(6, Observable.from(list)!!.startWith(0)!!.count()!!.toBlockingObservable()!!.single()) + assertEquals(10, Observable.from(list)!!.startWith(startList)!!.count()!!.toBlockingObservable()!!.single()) + } + + [Test] + public fun testStartWithEx() { + val list = listOf(10, 11, 12, 13, 14) + val startList = listOf(1, 2, 3, 4, 5) + assertEquals(6, list.asObservable().startWith(0)!!.count()!!.toBlockingObservable()!!.single()) + assertEquals(10, list.asObservable().startWith(startList)!!.count()!!.toBlockingObservable()!!.single()) + } + + [Test] + public fun testScriptWithOnNext() { + TestFactory().observable.subscribe((received())) + verify(a, times(1))!!.received("hello_1") + } + + [Test] + public fun testSkipTake() { + Observable.from(1, 2, 3)!!.skip(1)!!.take(1)!!.subscribe(received()) + verify(a, times(0))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(0))!!.received(3) + } + + [Test] + public fun testSkipTakeEx() { + Triple(1, 2, 3).asObservable().skip(1)!!.take(1)!!.subscribe(received()) + verify(a, times(0))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(0))!!.received(3) + } + + [Test] + public fun testSkip() { + Observable.from(1, 2, 3)!!.skip(2)!!.subscribe(received()) + verify(a, times(0))!!.received(1) + verify(a, times(0))!!.received(2) + verify(a, times(1))!!.received(3) + } + + [Test] + public fun testSkipEx() { + Triple(1, 2, 3).asObservable().skip(2)!!.subscribe(received()) + verify(a, times(0))!!.received(1) + verify(a, times(0))!!.received(2) + verify(a, times(1))!!.received(3) + } + + [Test] + public fun testTake() { + Observable.from(1, 2, 3)!!.take(2)!!.subscribe(received()) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(0))!!.received(3) + } + + [Test] + public fun testTakeEx() { + Triple(1, 2, 3).asObservable().take(2)!!.subscribe(received()) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(0))!!.received(3) + } + + [Test] + public fun testTakeLast() { + TestFactory().observable.takeLast(1)!!.subscribe((received())) + verify(a, times(1))!!.received("hello_1") + } + + [Test] + public fun testTakeWhile() { + Observable.from(1, 2, 3)!!.takeWhile { x -> x!! < 3 }!!.subscribe(received()) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(0))!!.received(3) + } + + [Test] + public fun testTakeWhileEx() { + Triple(1, 2, 3).asObservable().takeWhile { x -> x!! < 3 }!!.subscribe(received()) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(0))!!.received(3) + } + + [Test] + public fun testTakeWhileWithIndex() { + Observable.from(1, 2, 3)!!.takeWhileWithIndex { x, i -> i!! < 2 }!!.subscribe(received()) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(0))!!.received(3) + } + + [Test] + public fun testTakeWhileWithIndexEx() { + Triple(1, 2, 3).asObservable().takeWhileWithIndex { x, i -> i!! < 2 }!!.subscribe(received()) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(0))!!.received(3) + } + + [Test] + public fun testToSortedList() { + TestFactory().numbers.toSortedList()!!.subscribe(received()) + verify(a, times(1))!!.received(listOf(1, 2, 3, 4, 5)) + } + + [Test] + public fun testForEach() { + asyncObservable.asObservable().toBlockingObservable()!!.forEach(received()) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + verify(a, times(1))!!.received(3) + } + + [Test(expected = javaClass())] + public fun testForEachWithError() { + asyncObservable.asObservable().toBlockingObservable()!!.forEach { throw RuntimeException("err") } + fail("we expect an exception to be thrown") + } + + [Test] + public fun testLastOrDefault() { + assertEquals("two", Observable.from("one", "two")!!.toBlockingObservable()!!.lastOrDefault("default") { x -> x!!.length == 3 }) + assertEquals("default", Observable.from("one", "two")!!.toBlockingObservable()!!.lastOrDefault("default") { x -> x!!.length > 3 }) + } + + [Test] + public fun testLastOrDefaultEx() { + assertEquals("two", ("one" to"two").asObservable().toBlockingObservable()!!.lastOrDefault("default") { x -> x!!.length == 3 }) + assertEquals("default", ("one" to"two").asObservable().toBlockingObservable()!!.lastOrDefault("default") { x -> x!!.length > 3 }) + } + + [Test(expected = javaClass())] + public fun testSingle() { + assertEquals("one", Observable.from("one")!!.toBlockingObservable()!!.single { x -> x!!.length == 3 }) + Observable.from("one", "two")!!.toBlockingObservable()!!.single { x -> x!!.length == 3 } + fail() + } + + [Test] + public fun testDefer() { + Observable.defer { Observable.from(1, 2) }!!.subscribe(received()) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + } + + [Test] + public fun testDeferEx() { + { (1 to 2).asObservable() }.defer().subscribe(received()) + verify(a, times(1))!!.received(1) + verify(a, times(1))!!.received(2) + } + + [Test] + public fun testAll() { + Observable.from(1, 2, 3)!!.all { x -> x!! > 0 }!!.subscribe(received()) + verify(a, times(1))!!.received(true) + } + + [Test] + public fun testAllEx() { + Triple(1, 2, 3).asObservable().all { x -> x!! > 0 }!!.subscribe(received()) + verify(a, times(1))!!.received(true) + } + + [Test] + public fun testZip() { + val o1 = Observable.from(1, 2, 3)!! + val o2 = Observable.from(4, 5, 6)!! + val o3 = Observable.from(7, 8, 9)!! + + val values = Observable.zip(o1, o2, o3) { a, b, c -> listOf(a, b, c) }!!.toList()!!.toBlockingObservable()!!.single()!! + assertEquals(listOf(1, 4, 7), values[0]) + assertEquals(listOf(2, 5, 8), values[1]) + assertEquals(listOf(3, 6, 9), values[2]) + } + + [Test] + public fun testZipWithIterable() { + val o1 = Observable.from(1, 2, 3)!! + val o2 = Observable.from(4, 5, 6)!! + val o3 = Observable.from(7, 8, 9)!! + + val values = Observable.zip(listOf(o1, o2, o3)) { args -> listOf(*args) }!!.toList()!!.toBlockingObservable()!!.single()!! + assertEquals(listOf(1, 4, 7), values[0]) + assertEquals(listOf(2, 5, 8), values[1]) + assertEquals(listOf(3, 6, 9), values[2]) + } + + [Test] + public fun testGroupBy() { + var count = 0 + + Observable.from("one", "two", "three", "four", "five", "six")!! + .groupBy { s -> s!!.length }!! + .mapMany { groupObervable -> + groupObervable!!.map { s -> + "Value: $s Group ${groupObervable.getKey()}" + } + }!! + .toBlockingObservable()!!.forEach { s -> + println(s) + count++ + } + + assertEquals(6, count) + } + public trait ScriptAssertion{ - fun error(e: Exception?) + fun error(e: Throwable?) fun received(e: Any?) } + + val funOnSubscribe: (Int, Observer) -> Subscription = { counter, observer -> + observer.onNext("hello_$counter") + observer.onCompleted() + Subscription { } + } + + val asyncObservable: (Observer) -> Subscription = { observer -> + thread { + Thread.sleep(50) + observer.onNext(1) + observer.onNext(2) + observer.onNext(3) + observer.onCompleted() + } + Subscriptions.empty()!! + } + + /** + * Copied from (funKTionale)[https://github.com/MarioAriasC/funKTionale/blob/master/src/main/kotlin/org/funktionale/partials/namespace.kt] + */ + public fun Function2.partially1(p1: P1): (P2) -> R { + return {(p2: P2) -> this(p1, p2) } + } + + inner public class TestFactory(){ + var counter = 1 + + val numbers: Observable + get(){ + return listOf(1, 3, 2, 5, 4).asObservable() + } + + val onSubscribe: (Observer) -> Subscription + get(){ + return funOnSubscribe.partially1(counter++) + } + + val observable: Observable + get(){ + return onSubscribe.asObservable() + } + + } } \ No newline at end of file From 0f785d7c3d1f7bfb9d07a91ecfac0212e0904a46 Mon Sep 17 00:00:00 2001 From: MarioAriasC Date: Sat, 5 Oct 2013 08:15:32 +0100 Subject: [PATCH 183/333] Kotlin Examples --- language-adaptors/rxjava-kotlin/build.gradle | 4 +- .../rx/lang/kotlin/examples/namespace.kt | 114 +++++++ .../lang/kotlin/examples/video/namespace.kt | 152 +++++++++ .../kotlin/rx/lang/kotlin/BasicKotlinTests.kt | 234 ++----------- .../kotlin/rx/lang/kotlin/ExtensionTests.kt | 311 ++++++++++++++++++ .../kotlin/rx/lang/kotlin/SchedulersTests.kt | 11 - 6 files changed, 610 insertions(+), 216 deletions(-) create mode 100644 language-adaptors/rxjava-kotlin/src/examples/kotlin/rx/lang/kotlin/examples/namespace.kt create mode 100644 language-adaptors/rxjava-kotlin/src/examples/kotlin/rx/lang/kotlin/examples/video/namespace.kt create mode 100644 language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt delete mode 100644 language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/SchedulersTests.kt diff --git a/language-adaptors/rxjava-kotlin/build.gradle b/language-adaptors/rxjava-kotlin/build.gradle index f3e9f725a3..ca409031cc 100644 --- a/language-adaptors/rxjava-kotlin/build.gradle +++ b/language-adaptors/rxjava-kotlin/build.gradle @@ -4,7 +4,7 @@ buildscript { } dependencies { - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.6.+' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.6.602' } } @@ -13,7 +13,7 @@ apply plugin: 'osgi' dependencies { compile project(':rxjava-core') - compile 'org.jetbrains.kotlin:kotlin-stdlib:0.6.+' + compile 'org.jetbrains.kotlin:kotlin-stdlib:0.6.602' provided 'junit:junit-dep:4.10' provided 'org.mockito:mockito-core:1.8.5' } diff --git a/language-adaptors/rxjava-kotlin/src/examples/kotlin/rx/lang/kotlin/examples/namespace.kt b/language-adaptors/rxjava-kotlin/src/examples/kotlin/rx/lang/kotlin/examples/namespace.kt new file mode 100644 index 0000000000..5d433c74ed --- /dev/null +++ b/language-adaptors/rxjava-kotlin/src/examples/kotlin/rx/lang/kotlin/examples/namespace.kt @@ -0,0 +1,114 @@ +package rx.lang.kotlin.examples + +import rx.Observable +import rx.Observer +import rx.subscriptions.Subscriptions +import rx.lang.kotlin.asObservable +import kotlin.concurrent.thread +import rx.Subscription +import java.net.URL +import java.util.Scanner + +/** + * Created by IntelliJ IDEA. + * @author Mario Arias + * Date: 28/09/13 + * Time: 3:00 + */ + +fun main(args: Array) { + hello(array("Ben", "George")) + customObservableNonBlocking().subscribe { println(it) } + customObservableBlocking().subscribe { println(it) } + val printArticle: (String?) -> Unit = { + println("""--- Article --- + ${it!!.substring(0, 125)} + """) + } + fetchWikipediaArticleAsynchronously("Tiger", "Elephant").subscribe(printArticle) + simpleComposition() + + fetchWikipediaArticleAsynchronouslyWithErrorHandling("Tiger", "NonExistentTitle", "Elephant").subscribe (printArticle) { + println("""--- Error --- + ${it!!.getMessage()} + """) + } +} + +fun hello(names: Array) { + Observable.from(names)!!.subscribe { s -> println("Hello $s!") } +} + +fun customObservableBlocking(): Observable { + return {(observer: Observer) -> + (0..50).forEach { i -> + observer.onNext("value_$i") + } + observer.onCompleted() + Subscriptions.empty()!! + }.asObservable() +} + +fun customObservableNonBlocking(): Observable { + return {(observer: Observer) -> + val t = thread { + (0..50).forEach { i -> + observer.onNext("anotherValue_$i") + } + observer.onCompleted() + } + Subscription { + t.interrupt() + } + }.asObservable() +} + +fun fetchWikipediaArticleAsynchronously(vararg wikipediaArticleNames: String): Observable { + return {(observer: Observer) -> + thread { + wikipediaArticleNames.forEach { article -> + observer.onNext(URL("http://en.wikipedia.org/wiki/$article").getText()) + } + observer.onCompleted() + } + Subscriptions.empty()!! + }.asObservable() +} + +fun simpleComposition() { + customObservableNonBlocking() + .skip(10)!! + .take(5)!! + .map { s -> "${s}_transformed" }!! + .subscribe { println("onNext => $it") } +} + +fun fetchWikipediaArticleAsynchronouslyWithErrorHandling(vararg wikipediaArticleNames: String): Observable { + return {(observer: Observer) -> + thread { + try { + wikipediaArticleNames.forEach { article -> + observer.onNext(URL("http://en.wikipedia.org/wiki/$article").getText()) + } + observer.onCompleted() + } catch(e: Exception) { + observer.onError(e) + } + } + Subscriptions.empty()!! + }.asObservable() +} + + +//Extensions +fun URL.getText(): String { + val scanner = Scanner(this.openStream()!!) + val sb = StringBuilder(1024) + while(scanner.hasNextLine()){ + sb.append(scanner.nextLine()) + sb.append('\n') + } + return sb.toString() +} + + diff --git a/language-adaptors/rxjava-kotlin/src/examples/kotlin/rx/lang/kotlin/examples/video/namespace.kt b/language-adaptors/rxjava-kotlin/src/examples/kotlin/rx/lang/kotlin/examples/video/namespace.kt new file mode 100644 index 0000000000..faf160b299 --- /dev/null +++ b/language-adaptors/rxjava-kotlin/src/examples/kotlin/rx/lang/kotlin/examples/video/namespace.kt @@ -0,0 +1,152 @@ +package rx.lang.kotlin.examples.video + +import rx.Observable + +import rx.Observer +import rx.Subscription +import rx.lang.kotlin.asObservable +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.TimeUnit +import java.util.concurrent.ThreadPoolExecutor +import rx.subscriptions.BooleanSubscription +import java.util.HashMap + +fun main(args: Array) { + getVideoGridForDisplay(1).subscribe({ videoDictionary -> + println(videoDictionary) + }, { exception -> + println("Error:$exception") + }) { + executor.shutdownNow() + } +} + +val executor = ThreadPoolExecutor(4, 4, 1, TimeUnit.MINUTES, LinkedBlockingQueue()) + +fun getListOfLists(userId: Int): Observable { + return {(observer: Observer) -> + val subscription = BooleanSubscription() + try{ + executor.execute { + Thread.sleep(180) + (0..15).forEach {(i: Int):Unit -> + if (subscription.isUnsubscribed()) { + return@forEach + } + try{ + observer.onNext(VideoList(i)) + } catch (e: Exception){ + observer.onError(e) + } + } + } + } catch (e: Exception){ + observer.onError(e) + } + subscription + }.asObservable() +} + +fun getVideoGridForDisplay(userId: Int): Observable> { + return getListOfLists(userId).take(5)!!.mapMany { list -> + list!!.videos.take(10)!!.mapMany { video -> + val m = video!!.metadata.map { md -> + mapOf("title" to md!!["title"], "lenght" to md["duration"]) + } + val b = video.getBookmark(userId).map { position -> + mapOf("bookmark" to position) + } + val r = video.getRating(userId).map { rating -> + mapOf("rating" to mapOf( + "actual" to rating!!.actualStarRating, + "average" to rating.averageStarRating, + "predicted" to rating.predictedStarRating + )) + } + + Observable.zip(m, b, r) { metadata, bookmark, rating -> + val map: HashMap = hashMapOf("id" to video.videoId) + map.putAll(metadata!!) + map.putAll(bookmark!!) + map.putAll(rating!!) + map as Map + } + } + }!! +} + +class Video(val videoId: Int){ + val metadata: Observable> + get(){ + return {(observer: Observer>) -> + observer.onNext(mapOf("title" to "video-$videoId-title", "duration" to "5428")) + Subscription { } + }.asObservable() + } + + fun getBookmark(userId: Int): Observable { + return {(observer: Observer) -> + executor.execute { + Thread.sleep(4) + if(randInt(6) > 1){ + observer.onNext(randInt(0)) + } else { + observer.onNext(randInt(4000)) + } + observer.onCompleted() + } + Subscription { } + }.asObservable() + } + + fun getRating(userId: Int): Observable { + return {(observer: Observer) -> + executor.execute { + Thread.sleep(10) + observer.onNext(VideoRating(videoId, userId)) + observer.onCompleted() + } + Subscription { } + }.asObservable() + } +} + +class VideoRating(val videoId: Int, val userId: Int){ + val predictedStarRating: Int + get(){ + return randInt(5) + } + + val averageStarRating: Int + get(){ + return randInt(4) + } + + val actualStarRating: Int + get(){ + return randInt(5) + } +} + +class VideoList(val position: Int){ + val listName: String + get(){ + return "ListName-$position" + } + + val videos: Observable

- * + * */ public final class OperationSwitch { /** - * This function transforms an {@link Observable} sequence of {@link Observable} sequences into a single {@link Observable} sequence which produces values from the most recently published - * {@link Observable}. + * This function transforms an {@link Observable} sequence of + * {@link Observable} sequences into a single {@link Observable} sequence + * which produces values from the most recently published {@link Observable} + * . * * @param sequences - * The {@link Observable} sequence consisting of {@link Observable} sequences. + * The {@link Observable} sequence consisting of + * {@link Observable} sequences. * @return A {@link Func1} which does this transformation. */ public static OnSubscribeFunc switchDo(final Observable> sequences) { @@ -77,61 +86,99 @@ public Subscription onSubscribe(Observer observer) { private static class SwitchObserver implements Observer> { - private final Observer observer; - private final SafeObservableSubscription parent; - private final AtomicReference subsequence = new AtomicReference(); + private final Object gate; + private final Observer observer; + private final SafeObservableSubscription parent; + private final MultipleAssignmentSubscription innerSubscription; + private long latest; + private boolean stopped; + private boolean hasLatest; public SwitchObserver(Observer observer, SafeObservableSubscription parent) { this.observer = observer; this.parent = parent; - } - - @Override - public void onCompleted() { - unsubscribeFromSubSequence(); - observer.onCompleted(); - } - - @Override - public void onError(Throwable e) { - unsubscribeFromSubSequence(); - observer.onError(e); + this.gate = new Object(); + this.innerSubscription = new MultipleAssignmentSubscription(); } @Override public void onNext(Observable args) { - unsubscribeFromSubSequence(); + final long id; + synchronized (gate) { + id = ++latest; + hasLatest = true; + } - subsequence.set(args.subscribe(new Observer() { + final SafeObservableSubscription sub; + sub = new SafeObservableSubscription(); + sub.wrap(args.subscribe(new Observer() { @Override - public void onCompleted() { - // Do nothing. + public void onNext(T args) { + synchronized (gate) { + if (latest == id) { + observer.onNext(args); + } + } } @Override public void onError(Throwable e) { - parent.unsubscribe(); - observer.onError(e); + synchronized (gate) { + sub.unsubscribe(); + if (latest == id) { + observer.onError(e); + parent.unsubscribe(); + } + } } @Override - public void onNext(T args) { - observer.onNext(args); + public void onCompleted() { + synchronized (gate) { + sub.unsubscribe(); + if (latest == id) { + hasLatest = false; + } + + if (stopped) { + observer.onCompleted(); + parent.unsubscribe(); + } + + } } + })); + + innerSubscription.setSubscription(sub); + } + + @Override + public void onError(Throwable e) { + synchronized (gate) { + observer.onError(e); + } + + parent.unsubscribe(); } - private void unsubscribeFromSubSequence() { - Subscription previousSubscription = subsequence.get(); - if (previousSubscription != null) { - previousSubscription.unsubscribe(); + @Override + public void onCompleted() { + synchronized (gate) { + innerSubscription.unsubscribe(); + stopped = true; + if (!hasLatest) { + observer.onCompleted(); + parent.unsubscribe(); + } } } + } public static class UnitTest { - private TestScheduler scheduler; + private TestScheduler scheduler; private Observer observer; @Before @@ -141,6 +188,83 @@ public void before() { observer = mock(Observer.class); } + @Test + public void testSwitchWhenOuterCompleteBeforeInner() { + Observable> source = Observable.create(new OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 50, Observable.create(new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 70, "one"); + publishNext(observer, 100, "two"); + publishCompleted(observer, 200); + return Subscriptions.empty(); + } + })); + publishCompleted(observer, 60); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(2)).onNext(anyString()); + inOrder.verify(observer, times(1)).onCompleted(); + } + + @Test + public void testSwitchWhenInnerCompleteBeforeOuter() { + Observable> source = Observable.create(new OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 10, Observable.create(new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 0, "one"); + publishNext(observer, 10, "two"); + publishCompleted(observer, 20); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 100, Observable.create(new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 0, "three"); + publishNext(observer, 10, "four"); + publishCompleted(observer, 20); + return Subscriptions.empty(); + } + })); + publishCompleted(observer, 200); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(150, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onCompleted(); + 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"); + + scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + inOrder.verify(observer, times(1)).onCompleted(); + } + @Test public void testSwitchWithComplete() { Observable> source = Observable.create(new OnSubscribeFunc>() { @@ -149,7 +273,7 @@ public Subscription onSubscribe(Observer> observer) { publishNext(observer, 50, Observable.create(new OnSubscribeFunc() { @Override public Subscription onSubscribe(Observer observer) { - publishNext(observer, 50, "one"); + publishNext(observer, 60, "one"); publishNext(observer, 100, "two"); return Subscriptions.empty(); } @@ -196,8 +320,8 @@ public Subscription onSubscribe(Observer observer) { verify(observer, never()).onError(any(Throwable.class)); scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - verify(observer, times(1)).onCompleted(); + inOrder.verify(observer, times(1)).onNext("four"); + verify(observer, never()).onCompleted(); verify(observer, never()).onError(any(Throwable.class)); } From 2de214cec5e44dc8af134ccb08a5da9df7c87278 Mon Sep 17 00:00:00 2001 From: ylecaillez Date: Mon, 21 Oct 2013 12:15:22 -0700 Subject: [PATCH 195/333] Fix OperationSwitch so that it does not onComplete() before inner and outer subscription completes. --- .../java/rx/operators/OperationSwitch.java | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationSwitch.java b/rxjava-core/src/main/java/rx/operators/OperationSwitch.java index fe90b58280..b59a35d88b 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSwitch.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSwitch.java @@ -34,6 +34,7 @@ import rx.Observer; import rx.Subscription; import rx.concurrency.TestScheduler; +import rx.subscriptions.CompositeSubscription; import rx.subscriptions.MultipleAssignmentSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; @@ -78,9 +79,15 @@ public Switch(Observable> sequences) { @Override public Subscription onSubscribe(Observer observer) { - SafeObservableSubscription subscription = new SafeObservableSubscription(); - subscription.wrap(sequences.subscribe(new SwitchObserver(observer, subscription))); - return subscription; + SafeObservableSubscription parent; + parent = new SafeObservableSubscription(); + + MultipleAssignmentSubscription child; + child = new MultipleAssignmentSubscription(); + + parent.wrap(sequences.subscribe(new SwitchObserver(observer, parent, child))); + + return new CompositeSubscription(parent, child); } } @@ -89,16 +96,17 @@ private static class SwitchObserver implements Observer observer; private final SafeObservableSubscription parent; - private final MultipleAssignmentSubscription innerSubscription; + private final MultipleAssignmentSubscription child; private long latest; private boolean stopped; private boolean hasLatest; - public SwitchObserver(Observer observer, SafeObservableSubscription parent) { + public SwitchObserver(Observer observer, SafeObservableSubscription parent, + MultipleAssignmentSubscription child) { this.observer = observer; this.parent = parent; + this.child = child; this.gate = new Object(); - this.innerSubscription = new MultipleAssignmentSubscription(); } @Override @@ -106,7 +114,7 @@ public void onNext(Observable args) { final long id; synchronized (gate) { id = ++latest; - hasLatest = true; + this.hasLatest = true; } final SafeObservableSubscription sub; @@ -116,7 +124,7 @@ public void onNext(Observable args) { public void onNext(T args) { synchronized (gate) { if (latest == id) { - observer.onNext(args); + SwitchObserver.this.observer.onNext(args); } } } @@ -126,8 +134,8 @@ public void onError(Throwable e) { synchronized (gate) { sub.unsubscribe(); if (latest == id) { - observer.onError(e); - parent.unsubscribe(); + SwitchObserver.this.observer.onError(e); + SwitchObserver.this.parent.unsubscribe(); } } } @@ -137,12 +145,12 @@ public void onCompleted() { synchronized (gate) { sub.unsubscribe(); if (latest == id) { - hasLatest = false; + SwitchObserver.this.hasLatest = false; } if (stopped) { - observer.onCompleted(); - parent.unsubscribe(); + SwitchObserver.this.observer.onCompleted(); + SwitchObserver.this.parent.unsubscribe(); } } @@ -150,26 +158,26 @@ public void onCompleted() { })); - innerSubscription.setSubscription(sub); + this.child.setSubscription(sub); } @Override public void onError(Throwable e) { synchronized (gate) { - observer.onError(e); + this.observer.onError(e); } - parent.unsubscribe(); + this.parent.unsubscribe(); } @Override public void onCompleted() { synchronized (gate) { - innerSubscription.unsubscribe(); - stopped = true; - if (!hasLatest) { - observer.onCompleted(); - parent.unsubscribe(); + this.child.unsubscribe(); + this.stopped = true; + if (!this.hasLatest) { + this.observer.onCompleted(); + this.parent.unsubscribe(); } } } From d48e92f13858baa9cc5185661dc8090da60f0be9 Mon Sep 17 00:00:00 2001 From: Yannick Lecaillez Date: Mon, 21 Oct 2013 14:12:55 -0700 Subject: [PATCH 196/333] Do not unsubscribe from child once parent has been completed. Child should not be unsubscribed when the parent complete because it may not be completed. --- rxjava-core/src/main/java/rx/operators/OperationSwitch.java | 1 - 1 file changed, 1 deletion(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationSwitch.java b/rxjava-core/src/main/java/rx/operators/OperationSwitch.java index b59a35d88b..c40864de84 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSwitch.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSwitch.java @@ -173,7 +173,6 @@ public void onError(Throwable e) { @Override public void onCompleted() { synchronized (gate) { - this.child.unsubscribe(); this.stopped = true; if (!this.hasLatest) { this.observer.onCompleted(); From f2db8b4bda96ad108b38b0aab3875319d796e211 Mon Sep 17 00:00:00 2001 From: Matt Jacobs Date: Tue, 22 Oct 2013 10:55:24 -0700 Subject: [PATCH 197/333] Caching the result of 'isInternalImplementation' so that reflection doesn't get invoked on every subscribe --- rxjava-core/src/main/java/rx/Observable.java | 21 ++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 5cd876adba..b961126e91 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -128,6 +129,8 @@ */ public class Observable { + private final static ConcurrentHashMap internalClassMap = new ConcurrentHashMap(); + /** * Executed when 'subscribe' is invoked. */ @@ -4522,11 +4525,21 @@ private boolean isInternalImplementation(Object o) { return true; } // prevent double-wrapping (yeah it happens) - if (o instanceof SafeObserver) + if (o instanceof SafeObserver) { return true; - // we treat the following package as "internal" and don't wrap it - Package p = o.getClass().getPackage(); // it can be null - return p != null && p.getName().startsWith("rx.operators"); + } + + Class clazz = o.getClass(); + if (internalClassMap.containsKey(clazz)) { + //don't need to do reflection + return internalClassMap.get(clazz); + } else { + // we treat the following package as "internal" and don't wrap it + Package p = o.getClass().getPackage(); // it can be null + Boolean isInternal = (p != null && p.getName().startsWith("rx.operators")); + internalClassMap.put(clazz, isInternal); + return isInternal; + } } } From 7a1de0af2cd2489f3aa69fcd803d322d0295e141 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Tue, 22 Oct 2013 20:16:31 +0000 Subject: [PATCH 198/333] [Gradle Release Plugin] - pre tag commit: '0.14.6'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 466e742580..b7af8192d7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.6-SNAPSHOT +version=0.14.6 From a352fb8aaec71dfd7fe046e8b34d6c5e175b3265 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Tue, 22 Oct 2013 20:16:35 +0000 Subject: [PATCH 199/333] [Gradle Release Plugin] - new version commit: '0.14.7-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b7af8192d7..1e13b14016 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.6 +version=0.14.7-SNAPSHOT From 767557df37852771c76409e7264a66c315f86462 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 22 Oct 2013 16:22:29 -0700 Subject: [PATCH 200/333] Update CHANGES.md --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index c1d0c97753..37fb3b7235 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # RxJava Releases # +### Version 0.14.6 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.6%22)) ### + +* [Pull 441](https://github.com/Netflix/RxJava/pull/441) Fixed the issue that 'take' does not call 'onError' +* [Pull 443](https://github.com/Netflix/RxJava/pull/443) OperationSwitch notify onComplete() too early. +* [Pull 434](https://github.com/Netflix/RxJava/pull/434) Timeout operator and SerialSubscription +* [Pull 447](https://github.com/Netflix/RxJava/pull/447) Caching the result of 'isInternalImplementation' + ### Version 0.14.5 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.5%22)) ### * [Pull 438](https://github.com/Netflix/RxJava/pull/438) Kotlin Language Adaptor From b05df24282badba9dd7b493e7f79093cf19f9b36 Mon Sep 17 00:00:00 2001 From: Matt Jacobs Date: Wed, 23 Oct 2013 16:35:01 -0700 Subject: [PATCH 201/333] Removing println from OperationMerge --- rxjava-core/src/main/java/rx/operators/OperationMerge.java | 1 - 1 file changed, 1 deletion(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationMerge.java b/rxjava-core/src/main/java/rx/operators/OperationMerge.java index 2e5655b38f..ab226c1eee 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMerge.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMerge.java @@ -98,7 +98,6 @@ public Subscription onSubscribe(Observer> observ @Override public void unsubscribe() { - System.out.println("unsubscribe from merge"); unsubscribed = true; } From a39f9f8a038138ee6d820197828154ae67f167f5 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Thu, 24 Oct 2013 14:20:57 +0800 Subject: [PATCH 202/333] Implemented the 'TimeInterval' operator --- rxjava-core/src/main/java/rx/Observable.java | 26 ++++ .../rx/operators/OperationTimeInterval.java | 140 ++++++++++++++++++ .../src/main/java/rx/util/TimeInterval.java | 81 ++++++++++ 3 files changed, 247 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java create mode 100644 rxjava-core/src/main/java/rx/util/TimeInterval.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 0fbb967ae1..20ea2113b3 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -75,6 +75,7 @@ import rx.operators.OperationTakeUntil; import rx.operators.OperationTakeWhile; import rx.operators.OperationThrottleFirst; +import rx.operators.OperationTimeInterval; import rx.operators.OperationTimeout; import rx.operators.OperationTimestamp; import rx.operators.OperationToObservableFuture; @@ -97,6 +98,7 @@ import rx.util.OnErrorNotImplementedException; import rx.util.Opening; import rx.util.Range; +import rx.util.TimeInterval; import rx.util.Timestamped; import rx.util.functions.Action0; import rx.util.functions.Action1; @@ -4533,6 +4535,30 @@ public Observable timeout(long timeout, TimeUnit timeUnit) { return create(OperationTimeout.timeout(this, timeout, timeUnit, Schedulers.threadPoolForComputation())); } + /** + * Records the time interval between consecutive elements in an observable sequence. + * + * @return An observable sequence with time interval information on elements. + * @see MSDN: Observable.TimeInterval + */ + public Observable> timeInterval() { + return create(OperationTimeInterval.timeInterval(this)); + } + + /** + * Records the time interval between consecutive elements in an observable + * sequence, using the specified scheduler to compute time intervals. + * + * @param scheduler + * Scheduler used to compute time intervals. + * + * @return An observable sequence with time interval information on elements. + * @see MSDN: Observable.TimeInterval + */ + public Observable> timeInterval(Scheduler scheduler) { + return create(OperationTimeInterval.timeInterval(this, scheduler)); + } + /** * Whether a given {@link Function} is an internal implementation inside rx.* packages or not. *

diff --git a/rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java b/rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java new file mode 100644 index 0000000000..7b70818bc1 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java @@ -0,0 +1,140 @@ +/** + * Copyright 2013 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.operators; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.times; + +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Scheduler; +import rx.Subscription; +import rx.concurrency.Schedulers; +import rx.concurrency.TestScheduler; +import rx.subjects.PublishSubject; +import rx.util.TimeInterval; + +/** + * Records the time interval between consecutive elements in an observable sequence. + */ +public class OperationTimeInterval { + + public static OnSubscribeFunc> timeInterval( + Observable source) { + return timeInterval(source, Schedulers.immediate()); + } + + public static OnSubscribeFunc> timeInterval( + final Observable source, final Scheduler scheduler) { + return new OnSubscribeFunc>() { + @Override + public Subscription onSubscribe( + Observer> observer) { + return source.subscribe(new TimeIntervalObserver(observer, + scheduler)); + } + }; + } + + private static class TimeIntervalObserver implements Observer { + + private final Observer> observer; + /** + * Only used to compute time intervals. + */ + private final Scheduler scheduler; + private long lastTimestamp; + + public TimeIntervalObserver(Observer> observer, + Scheduler scheduler) { + this.observer = observer; + this.scheduler = scheduler; + // The beginning time is the time when the observer subscribes. + lastTimestamp = scheduler.now(); + } + + @Override + public void onNext(T args) { + long nowTimestamp = scheduler.now(); + observer.onNext(new TimeInterval(nowTimestamp - lastTimestamp, + args)); + lastTimestamp = nowTimestamp; + } + + @Override + public void onCompleted() { + observer.onCompleted(); + } + + @Override + public void onError(Throwable e) { + observer.onCompleted(); + } + } + + public static class UnitTest { + + private static final TimeUnit TIME_UNIT = TimeUnit.MILLISECONDS; + + @Mock + private Observer> observer; + + private TestScheduler testScheduler; + private PublishSubject subject; + private Observable> observable; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + testScheduler = new TestScheduler(); + subject = PublishSubject.create(); + observable = subject.timeInterval(testScheduler); + } + + @Test + public void testTimeInterval() { + InOrder inOrder = inOrder(observer); + observable.subscribe(observer); + + testScheduler.advanceTimeBy(1000, TIME_UNIT); + subject.onNext(1); + testScheduler.advanceTimeBy(2000, TIME_UNIT); + subject.onNext(2); + testScheduler.advanceTimeBy(3000, TIME_UNIT); + subject.onNext(3); + subject.onCompleted(); + + inOrder.verify(observer, times(1)).onNext( + new TimeInterval(1000, 1)); + inOrder.verify(observer, times(1)).onNext( + new TimeInterval(2000, 2)); + inOrder.verify(observer, times(1)).onNext( + new TimeInterval(3000, 3)); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + } + +} diff --git a/rxjava-core/src/main/java/rx/util/TimeInterval.java b/rxjava-core/src/main/java/rx/util/TimeInterval.java new file mode 100644 index 0000000000..7bf1383688 --- /dev/null +++ b/rxjava-core/src/main/java/rx/util/TimeInterval.java @@ -0,0 +1,81 @@ +/** + * Copyright 2013 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.util; + +public class TimeInterval { + private final long intervalInMilliseconds; + private final T value; + + public TimeInterval(long intervalInMilliseconds, T value) { + this.value = value; + this.intervalInMilliseconds = intervalInMilliseconds; + } + + /** + * Returns the interval in milliseconds. + * + * @return interval in milliseconds + */ + public long getIntervalInMilliseconds() { + return intervalInMilliseconds; + } + + /** + * Returns the value. + * + * @return the value + */ + public T getValue() { + return value; + } + + // The following methods are generated by eclipse automatically. + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime + * result + + (int) (intervalInMilliseconds ^ (intervalInMilliseconds >>> 32)); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TimeInterval other = (TimeInterval) obj; + if (intervalInMilliseconds != other.intervalInMilliseconds) + return false; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + + @Override + public String toString() { + return "TimeInterval [intervalInMilliseconds=" + intervalInMilliseconds + + ", value=" + value + "]"; + } +} From 7ef16c9da4ace88b260cb908884b3df8a799490b Mon Sep 17 00:00:00 2001 From: zsxwing Date: Fri, 25 Oct 2013 13:56:27 +0800 Subject: [PATCH 203/333] Implemented the scheduler version of the 'Return' operator --- rxjava-core/src/main/java/rx/Observable.java | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 0fbb967ae1..75950e3173 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -1012,6 +1012,31 @@ public static Observable just(T value) { return from(list); } + /** + * Returns an Observable that emits a single item and then completes in a specified scheduler. + *

+ * + *

+ * To convert any object into an Observable that emits that object, pass that object into the + * just method. + *

+ * This is similar to the {@link #from(java.lang.Object[])} method, except that + * from() will convert an {@link Iterable} object into an Observable that emits + * each of the items in the Iterable, one at a time, while the just() method + * converts an Iterable into an Observable that emits the entire Iterable as a single item. + * + * @param value + * the item to pass to the {@link Observer}'s {@link Observer#onNext onNext} method + * @param scheduler + * the scheduler to send the single element on + * @param + * the type of that item + * @return an Observable that emits a single item and then completes + */ + public static Observable just(T value, Scheduler scheduler) { + return just(value).observeOn(scheduler); + } + /** * Flattens a sequence of Observables emitted by an Observable into one Observable, without any * transformation. From dcb5d79b833fe7169421c6fbdef483527037103d Mon Sep 17 00:00:00 2001 From: zsxwing Date: Fri, 25 Oct 2013 14:00:13 +0800 Subject: [PATCH 204/333] Updated the comments --- rxjava-core/src/main/java/rx/Observable.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 75950e3173..15ba94120b 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -1013,17 +1013,9 @@ public static Observable just(T value) { } /** - * Returns an Observable that emits a single item and then completes in a specified scheduler. + * Returns an Observable that emits a single item and then completes on a specified scheduler. *

- * - *

- * To convert any object into an Observable that emits that object, pass that object into the - * just method. - *

- * This is similar to the {@link #from(java.lang.Object[])} method, except that - * from() will convert an {@link Iterable} object into an Observable that emits - * each of the items in the Iterable, one at a time, while the just() method - * converts an Iterable into an Observable that emits the entire Iterable as a single item. + * This is a scheduler version of {@link Observable#just(Object)}. * * @param value * the item to pass to the {@link Observer}'s {@link Observer#onNext onNext} method @@ -1031,7 +1023,7 @@ public static Observable just(T value) { * the scheduler to send the single element on * @param * the type of that item - * @return an Observable that emits a single item and then completes + * @return an Observable that emits a single item and then completes on a specified scheduler. */ public static Observable just(T value, Scheduler scheduler) { return just(value).observeOn(scheduler); From 646aa624eaddb4f543a1019f2d5ef49795cf66ce Mon Sep 17 00:00:00 2001 From: zsxwing Date: Fri, 25 Oct 2013 18:42:19 +0800 Subject: [PATCH 205/333] Fixed issue #417 --- .../main/java/rx/operators/OperationMap.java | 60 +++++++++++++++---- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationMap.java b/rxjava-core/src/main/java/rx/operators/OperationMap.java index 9eb2520420..940147b0b8 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMap.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMap.java @@ -15,12 +15,16 @@ */ package rx.operators; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Before; @@ -33,6 +37,7 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; +import rx.concurrency.Schedulers; import rx.util.functions.Func1; import rx.util.functions.Func2; @@ -59,17 +64,12 @@ public final class OperationMap { * @return a sequence that is the result of applying the transformation function to each item in the input sequence. */ public static OnSubscribeFunc map(final Observable sequence, final Func1 func) { - return new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - return new MapObservable(sequence, new Func2() { + return mapWithIndex(sequence, new Func2() { @Override public R call(T value, @SuppressWarnings("unused") Integer unused) { return func.call(value); } - }).onSubscribe(observer); - } - }; + }); } /** @@ -136,7 +136,8 @@ public MapObservable(Observable sequence, Func2 observer) { - return sequence.subscribe(new Observer() { + final SafeObservableSubscription subscription = new SafeObservableSubscription(); + return subscription.wrap(sequence.subscribe(new SafeObserver(subscription, new Observer() { @Override public void onNext(T value) { observer.onNext(func.call(value, index)); @@ -152,7 +153,7 @@ public void onError(Throwable ex) { public void onCompleted() { observer.onCompleted(); } - }); + }))); } } @@ -366,6 +367,41 @@ public String call(String s) { assertEquals(1, c2.get()); } + @Test(expected = IllegalArgumentException.class) + public void testMapWithIssue417() { + Observable.from(1).observeOn(Schedulers.threadPoolForComputation()) + .map(new Func1() { + public Integer call(Integer arg0) { + throw new IllegalArgumentException("any error"); + } + }).toBlockingObservable().single(); + } + + @Test + public void testMapWithErrorInFuncAndThreadPoolScheduler() throws InterruptedException { + // The error will throw in one of threads in the thread pool. + // If map does not handle it, the error will disappear. + // so map needs to handle the error by itself. + final CountDownLatch latch = new CountDownLatch(1); + Observable m = Observable.from("one") + .observeOn(Schedulers.threadPoolForComputation()) + .map(new Func1() { + public String call(String arg0) { + try { + throw new IllegalArgumentException("any error"); + } finally { + latch.countDown(); + } + } + }); + + m.subscribe(stringObserver); + latch.await(); + InOrder inorder = inOrder(stringObserver); + inorder.verify(stringObserver, times(1)).onError(any(IllegalArgumentException.class)); + inorder.verifyNoMoreInteractions(); + } + private static Map getMap(String prefix) { Map m = new HashMap(); m.put("firstName", prefix + "First"); From 42674efbb59916a90a745b8f8dd23e6bb7b63df5 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Sun, 27 Oct 2013 14:42:46 +0100 Subject: [PATCH 206/333] add one TODO --- language-adaptors/rxjava-scala/TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/language-adaptors/rxjava-scala/TODO.md b/language-adaptors/rxjava-scala/TODO.md index f465415344..60169c9a81 100644 --- a/language-adaptors/rxjava-scala/TODO.md +++ b/language-adaptors/rxjava-scala/TODO.md @@ -18,6 +18,7 @@ TODOs which came up at the meeting with Erik Meijer on 2013-10-11: * There are no examples yet using `async`, but `async` will be used in the course. Write examples and check if everything works as expected when combined with `async`. * Futures: For the moment, just add a Future->Observable converter method to `object Observable`. Later, think if `Future[T] extends Observable[T]`. * Operator `delay`: Once Erik has commented on [this](https://github.com/Netflix/RxJava/pull/384), make sure this operator is added accordingly to RxJava and then to RxScala +* add wrappers or aliases for `AsyncSubject`, `BehaviorSubject`, `PublishSubject`, and `ReplaySubject` * go through Erik's code that he showed at the meeting and check if everything can now be done nicely * get Erik's slides from the course and check if they are compatible with the library From 782909f7b8063403a0783f1ca213d5a2909bf2ba Mon Sep 17 00:00:00 2001 From: zsxwing Date: Tue, 15 Oct 2013 14:11:31 +0800 Subject: [PATCH 207/333] Fixed testOnErrorViaHasNext in issue #383 --- .../src/main/java/rx/operators/OperationNext.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationNext.java b/rxjava-core/src/main/java/rx/operators/OperationNext.java index 73a3b3dc13..e04e41dd14 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationNext.java +++ b/rxjava-core/src/main/java/rx/operators/OperationNext.java @@ -104,6 +104,7 @@ public void remove() { private static class NextObserver implements Observer> { private final BlockingQueue> buf = new ArrayBlockingQueue>(1); private final AtomicBoolean waiting = new AtomicBoolean(false); + private volatile boolean completed = false; @Override public void onCompleted() { @@ -139,7 +140,11 @@ public void await() { public boolean isCompleted(boolean rethrowExceptionIfExists) { Notification lastItem = buf.peek(); if (lastItem == null) { - return false; + // Fixed issue #383 testOnErrorViaHasNext fails sometimes. + // If the buf is empty, there are two cases: + // 1. The next item has not been emitted yet. + // 2. The error or completed notification is removed in takeNext method. + return completed; } if (lastItem.isOnError()) { @@ -157,10 +162,12 @@ public T takeNext() throws InterruptedException { Notification next = buf.take(); if (next.isOnError()) { + completed = true; throw Exceptions.propagate(next.getThrowable()); } if (next.isOnCompleted()) { + completed = true; throw new IllegalStateException("Observable is completed"); } From f67efa7857f23836a4151b59dfdacfc342578649 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Wed, 16 Oct 2013 09:25:51 +0800 Subject: [PATCH 208/333] Removed the unnecessary 'catch' and 'fail' --- .../src/main/java/rx/operators/OperationNext.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationNext.java b/rxjava-core/src/main/java/rx/operators/OperationNext.java index e04e41dd14..34d00954e9 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationNext.java +++ b/rxjava-core/src/main/java/rx/operators/OperationNext.java @@ -234,7 +234,7 @@ public void testOnError() throws Throwable { } @Test - public void testOnErrorViaHasNext() throws Throwable { + public void testOnErrorViaHasNext() throws InterruptedException, ExecutionException { Subject obs = PublishSubject.create(); Iterator it = next(obs).iterator(); @@ -253,15 +253,10 @@ public void testOnErrorViaHasNext() throws Throwable { obs.onError(new TestException()); // this should not throw an exception but instead just return false - try { - assertFalse(it.hasNext()); - } catch (Throwable e) { - fail("should not have received exception"); - e.printStackTrace(); - } + assertFalse(it.hasNext()); } - private Future nextAsync(final Iterator it) throws Throwable { + private Future nextAsync(final Iterator it) { return executor.submit(new Callable() { From c4e3a6c61cc62c09588437426812187395f6ecd4 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Sun, 20 Oct 2013 00:58:09 +0800 Subject: [PATCH 209/333] Blocked 'hasNext' instead of 'next' until any notification arrives --- .../main/java/rx/operators/OperationNext.java | 229 +++++++++--------- 1 file changed, 120 insertions(+), 109 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationNext.java b/rxjava-core/src/main/java/rx/operators/OperationNext.java index 34d00954e9..1d2fc9f0b0 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationNext.java +++ b/rxjava-core/src/main/java/rx/operators/OperationNext.java @@ -15,17 +15,16 @@ */ package rx.operators; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.Iterator; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -37,6 +36,7 @@ import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; +import rx.concurrency.Schedulers; import rx.subjects.PublishSubject; import rx.subjects.Subject; import rx.subscriptions.Subscriptions; @@ -68,6 +68,8 @@ public Iterator iterator() { private static class NextIterator implements Iterator { private final NextObserver observer; + private T next; + private boolean hasNext = true; private NextIterator(NextObserver observer) { this.observer = observer; @@ -75,24 +77,35 @@ private NextIterator(NextObserver observer) { @Override public boolean hasNext() { - return !observer.isCompleted(false); - } - - @Override - public T next() { - if (observer.isCompleted(true)) { - throw new IllegalStateException("Observable is completed"); + // Since an iterator should not be used in different thread, + // so we do not need any synchronization. + if(hasNext == false) { + return false; } - - observer.await(); - try { - return observer.takeNext(); + Notification nextNotification = observer.takeNext(); + if(nextNotification.isOnNext()) { + next = nextNotification.getValue(); + return true; + } + // If an observable is completed or fails, + // next always return null and hasNext always return false. + next = null; + hasNext = false; + if(nextNotification.isOnCompleted()) { + return false; + } + // onError + throw Exceptions.propagate(nextNotification.getThrowable()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw Exceptions.propagate(e); } + } + @Override + public T next() { + return next; } @Override @@ -104,7 +117,6 @@ public void remove() { private static class NextObserver implements Observer> { private final BlockingQueue> buf = new ArrayBlockingQueue>(1); private final AtomicBoolean waiting = new AtomicBoolean(false); - private volatile boolean completed = false; @Override public void onCompleted() { @@ -125,7 +137,7 @@ public void onNext(Notification args) { Notification concurrentItem = buf.poll(); // in case if we won race condition with onComplete/onError method - if (!concurrentItem.isOnNext()) { + if (concurrentItem != null && !concurrentItem.isOnNext()) { toOffer = concurrentItem; } } @@ -133,138 +145,137 @@ public void onNext(Notification args) { } - public void await() { + public Notification takeNext() throws InterruptedException { waiting.set(true); + return buf.take(); } - public boolean isCompleted(boolean rethrowExceptionIfExists) { - Notification lastItem = buf.peek(); - if (lastItem == null) { - // Fixed issue #383 testOnErrorViaHasNext fails sometimes. - // If the buf is empty, there are two cases: - // 1. The next item has not been emitted yet. - // 2. The error or completed notification is removed in takeNext method. - return completed; - } + } - if (lastItem.isOnError()) { - if (rethrowExceptionIfExists) { - throw Exceptions.propagate(lastItem.getThrowable()); - } else { - return true; - } - } + public static class UnitTest { - return lastItem.isOnCompleted(); + private void fireOnNextInNewThread(final Subject o, final String value) { + new Thread() { + @Override + public void run() { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // ignore + } + o.onNext(value); + } + }.start(); } - public T takeNext() throws InterruptedException { - Notification next = buf.take(); - - if (next.isOnError()) { - completed = true; - throw Exceptions.propagate(next.getThrowable()); - } - - if (next.isOnCompleted()) { - completed = true; - throw new IllegalStateException("Observable is completed"); - } - - return next.getValue(); - + private void fireOnErrorInNewThread(final Subject o) { + new Thread() { + @Override + public void run() { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // ignore + } + o.onError(new TestException()); + } + }.start(); } - } - - public static class UnitTest { - private final ExecutorService executor = Executors.newSingleThreadExecutor(); @Test - public void testNext() throws Throwable { + public void testNext() { Subject obs = PublishSubject.create(); - Iterator it = next(obs).iterator(); - - assertTrue(it.hasNext()); - - Future next = nextAsync(it); - Thread.sleep(100); - obs.onNext("one"); - assertEquals("one", next.get()); - + fireOnNextInNewThread(obs, "one"); assertTrue(it.hasNext()); + assertEquals("one", it.next()); - next = nextAsync(it); - Thread.sleep(100); - obs.onNext("two"); - assertEquals("two", next.get()); - + fireOnNextInNewThread(obs, "two"); assertTrue(it.hasNext()); + assertEquals("two", it.next()); obs.onCompleted(); + assertFalse(it.hasNext()); + assertNull(it.next()); + // If the observable is completed, hasNext always returns false and next always returns null. assertFalse(it.hasNext()); + assertNull(it.next()); } - @Test(expected = TestException.class) - public void testOnError() throws Throwable { + @Test + public void testNextWithError() { Subject obs = PublishSubject.create(); - Iterator it = next(obs).iterator(); - + fireOnNextInNewThread(obs, "one"); assertTrue(it.hasNext()); + assertEquals("one", it.next()); - Future next = nextAsync(it); - Thread.sleep(100); - obs.onNext("one"); - assertEquals("one", next.get()); - - assertTrue(it.hasNext()); - - next = nextAsync(it); - Thread.sleep(100); - obs.onError(new TestException()); - + fireOnErrorInNewThread(obs); try { - next.get(); - } catch (ExecutionException e) { - throw e.getCause(); + it.hasNext(); + fail("Expected an TestException"); + } + catch(TestException e) { + // successful } + + // After the observable fails, hasNext always returns false and next always returns null. + assertFalse(it.hasNext()); + assertNull(it.next()); } @Test - public void testOnErrorViaHasNext() throws InterruptedException, ExecutionException { - Subject obs = PublishSubject.create(); + public void testNextWithEmpty() { + Observable obs = Observable.empty().observeOn(Schedulers.newThread()); + Iterator it = next(obs).iterator(); - Iterator it = next(obs).iterator(); - - assertTrue(it.hasNext()); + assertFalse(it.hasNext()); + assertNull(it.next()); - Future next = nextAsync(it); - Thread.sleep(100); - obs.onNext("one"); - assertEquals("one", next.get()); + // If the observable is completed, hasNext always returns false and next always returns null. + assertFalse(it.hasNext()); + assertNull(it.next()); + } - assertTrue(it.hasNext()); + @Test + public void testOnError() throws Throwable { + Subject obs = PublishSubject.create(); + Iterator it = next(obs).iterator(); - next = nextAsync(it); - Thread.sleep(100); obs.onError(new TestException()); + try { + it.hasNext(); + fail("Expected an TestException"); + } + catch(TestException e) { + // successful + } - // this should not throw an exception but instead just return false + // After the observable fails, hasNext always returns false and next always returns null. assertFalse(it.hasNext()); + assertNull(it.next()); } - private Future nextAsync(final Iterator it) { + @Test + public void testOnErrorInNewThread() { + Subject obs = PublishSubject.create(); + Iterator it = next(obs).iterator(); - return executor.submit(new Callable() { + fireOnErrorInNewThread(obs); - @Override - public String call() throws Exception { - return it.next(); - } - }); + try { + it.hasNext(); + fail("Expected an TestException"); + } + catch(TestException e) { + // successful + } + + // After the observable fails, hasNext always returns false and next always returns null. + assertFalse(it.hasNext()); + assertNull(it.next()); } @SuppressWarnings("serial") From e6a2951c78d1e0a2365ebbd4e5d956ba7ecd8d40 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Wed, 23 Oct 2013 11:05:08 +0800 Subject: [PATCH 210/333] Followed the iterator contract --- .../main/java/rx/operators/OperationNext.java | 132 +++++++++++++++--- 1 file changed, 113 insertions(+), 19 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationNext.java b/rxjava-core/src/main/java/rx/operators/OperationNext.java index 1d2fc9f0b0..69ed2df778 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationNext.java +++ b/rxjava-core/src/main/java/rx/operators/OperationNext.java @@ -17,11 +17,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Iterator; +import java.util.NoSuchElementException; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; @@ -70,6 +70,7 @@ private static class NextIterator implements Iterator { private final NextObserver observer; private T next; private boolean hasNext = true; + private boolean isNextConsumed = true; private NextIterator(NextObserver observer) { this.observer = observer; @@ -80,23 +81,34 @@ public boolean hasNext() { // Since an iterator should not be used in different thread, // so we do not need any synchronization. if(hasNext == false) { + // the iterator has reached the end. return false; } + if(isNextConsumed == false) { + // next has not been used yet. + return true; + } + return moveToNext(); + } + + private boolean moveToNext() { try { Notification nextNotification = observer.takeNext(); if(nextNotification.isOnNext()) { + isNextConsumed = false; next = nextNotification.getValue(); return true; } // If an observable is completed or fails, - // next always return null and hasNext always return false. - next = null; + // hasNext() always return false. hasNext = false; if(nextNotification.isOnCompleted()) { return false; } - // onError - throw Exceptions.propagate(nextNotification.getThrowable()); + if(nextNotification.isOnError()) { + throw Exceptions.propagate(nextNotification.getThrowable()); + } + throw new IllegalStateException("Should not reach here"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw Exceptions.propagate(e); @@ -105,7 +117,13 @@ public boolean hasNext() { @Override public T next() { - return next; + if(hasNext()) { + isNextConsumed = true; + return next; + } + else { + throw new NoSuchElementException("No more elements"); + } } @Override @@ -197,11 +215,21 @@ public void testNext() { obs.onCompleted(); assertFalse(it.hasNext()); - assertNull(it.next()); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } + catch(NoSuchElementException e){ + } - // If the observable is completed, hasNext always returns false and next always returns null. + // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException. assertFalse(it.hasNext()); - assertNull(it.next()); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } + catch(NoSuchElementException e){ + } } @Test @@ -221,9 +249,14 @@ public void testNextWithError() { // successful } - // After the observable fails, hasNext always returns false and next always returns null. + // After the observable fails, hasNext always returns false and next always throw a NoSuchElementException. assertFalse(it.hasNext()); - assertNull(it.next()); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } + catch(NoSuchElementException e){ + } } @Test @@ -232,11 +265,21 @@ public void testNextWithEmpty() { Iterator it = next(obs).iterator(); assertFalse(it.hasNext()); - assertNull(it.next()); - - // If the observable is completed, hasNext always returns false and next always returns null. + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } + catch(NoSuchElementException e){ + } + + // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException. assertFalse(it.hasNext()); - assertNull(it.next()); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } + catch(NoSuchElementException e){ + } } @Test @@ -253,9 +296,14 @@ public void testOnError() throws Throwable { // successful } - // After the observable fails, hasNext always returns false and next always returns null. + // After the observable fails, hasNext always returns false and next always throw a NoSuchElementException. assertFalse(it.hasNext()); - assertNull(it.next()); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } + catch(NoSuchElementException e){ + } } @Test @@ -273,9 +321,53 @@ public void testOnErrorInNewThread() { // successful } - // After the observable fails, hasNext always returns false and next always returns null. + // After the observable fails, hasNext always returns false and next always throw a NoSuchElementException. assertFalse(it.hasNext()); - assertNull(it.next()); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } + catch(NoSuchElementException e){ + } + } + + @Test + public void testNextWithOnlyUsingNextMethod() { + Subject obs = PublishSubject.create(); + Iterator it = next(obs).iterator(); + fireOnNextInNewThread(obs, "one"); + assertEquals("one", it.next()); + + fireOnNextInNewThread(obs, "two"); + assertEquals("two", it.next()); + + obs.onCompleted(); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } + catch(NoSuchElementException e){ + } + } + + @Test + public void testNextWithCallingHasNextMultipleTimes() { + Subject obs = PublishSubject.create(); + Iterator it = next(obs).iterator(); + fireOnNextInNewThread(obs, "one"); + assertTrue(it.hasNext()); + assertTrue(it.hasNext()); + assertTrue(it.hasNext()); + assertTrue(it.hasNext()); + assertEquals("one", it.next()); + + obs.onCompleted(); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } + catch(NoSuchElementException e){ + } } @SuppressWarnings("serial") @@ -342,6 +434,8 @@ public void run() { assertTrue("expected that c [" + c + "] is higher than or equal to " + COUNT, c >= COUNT); assertTrue(it.hasNext()); + int d = it.next(); + assertTrue(d > c); // shut down the thread running.set(false); From 0659539b518d672c90925916069d722ab86b6376 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Sun, 27 Oct 2013 22:39:50 +0800 Subject: [PATCH 211/333] Force 'hasNext' and 'next' throw the error once they have already thrown it before --- .../main/java/rx/operators/OperationNext.java | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationNext.java b/rxjava-core/src/main/java/rx/operators/OperationNext.java index 69ed2df778..43100e1096 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationNext.java +++ b/rxjava-core/src/main/java/rx/operators/OperationNext.java @@ -71,6 +71,7 @@ private static class NextIterator implements Iterator { private T next; private boolean hasNext = true; private boolean isNextConsumed = true; + private Throwable error = null; private NextIterator(NextObserver observer) { this.observer = observer; @@ -78,6 +79,10 @@ private NextIterator(NextObserver observer) { @Override public boolean hasNext() { + if(error != null) { + // If any error has already been thrown, throw it again. + throw Exceptions.propagate(error); + } // Since an iterator should not be used in different thread, // so we do not need any synchronization. if(hasNext == false) { @@ -106,17 +111,23 @@ private boolean moveToNext() { return false; } if(nextNotification.isOnError()) { - throw Exceptions.propagate(nextNotification.getThrowable()); + error = nextNotification.getThrowable(); + throw Exceptions.propagate(error); } throw new IllegalStateException("Should not reach here"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw Exceptions.propagate(e); + error = e; + throw Exceptions.propagate(error); } } @Override public T next() { + if(error != null) { + // If any error has already been thrown, throw it again. + throw Exceptions.propagate(error); + } if(hasNext()) { isNextConsumed = true; return next; @@ -246,17 +257,9 @@ public void testNextWithError() { fail("Expected an TestException"); } catch(TestException e) { - // successful } - // After the observable fails, hasNext always returns false and next always throw a NoSuchElementException. - assertFalse(it.hasNext()); - try { - it.next(); - fail("At the end of an iterator should throw a NoSuchElementException"); - } - catch(NoSuchElementException e){ - } + assertErrorAfterObservableFail(it); } @Test @@ -296,14 +299,7 @@ public void testOnError() throws Throwable { // successful } - // After the observable fails, hasNext always returns false and next always throw a NoSuchElementException. - assertFalse(it.hasNext()); - try { - it.next(); - fail("At the end of an iterator should throw a NoSuchElementException"); - } - catch(NoSuchElementException e){ - } + assertErrorAfterObservableFail(it); } @Test @@ -321,13 +317,22 @@ public void testOnErrorInNewThread() { // successful } - // After the observable fails, hasNext always returns false and next always throw a NoSuchElementException. - assertFalse(it.hasNext()); + assertErrorAfterObservableFail(it); + } + + private void assertErrorAfterObservableFail(Iterator it) { + // After the observable fails, hasNext and next always throw the exception. + try { + it.hasNext(); + fail("hasNext should throw a TestException"); + } + catch(TestException e){ + } try { it.next(); - fail("At the end of an iterator should throw a NoSuchElementException"); + fail("next should throw a TestException"); } - catch(NoSuchElementException e){ + catch(TestException e){ } } From 9d38fdeaf9ef2c1c6e30710887056b11bc02160d Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Sun, 27 Oct 2013 21:17:46 +0100 Subject: [PATCH 212/333] some documentation for CompletenessTest --- .../rx/lang/scala/CompletenessTest.scala | 58 ++++++++++++------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index d9c26c54c0..f38ac0d521 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -1,32 +1,55 @@ package rx.lang.scala -import scala.reflect.runtime.universe._ -import org.scalatest.junit.JUnitSuite -import org.junit.Test -import rx.util.functions._ -import scala.collection.SortedSet +import java.util.Calendar + import scala.collection.SortedMap +import scala.reflect.runtime.universe +import scala.reflect.runtime.universe.Symbol +import scala.reflect.runtime.universe.Type +import scala.reflect.runtime.universe.typeOf + import org.junit.Ignore -import java.lang.reflect.Modifier -import java.util.Date -import java.util.Calendar +import org.junit.Test +import org.scalatest.junit.JUnitSuite +/** + * These tests can be used to check if all methods of the Java Observable have a corresponding + * method in the Scala Observable. + * + * These tests don't contain any assertions, so they will always succeed, but they print their + * results to stdout. + */ class CompletenessTest extends JUnitSuite { + // some frequently used comments: val unnecessary = "[considered unnecessary in Scala land]" - val deprecated = "[deprecated in RxJava]" - val averageProblem = "[We can't have a general average method because Scala's `Numeric` does not have " + "scalar multiplication (we would need to calculate `(1.0/numberOfElements)*sum`). " + "You can use `fold` instead to accumulate `sum` and `numberOfElements` and divide at the end.]" - val commentForFirstWithPredicate = "[use `.filter(condition).first`]" - val fromFuture = "[TODO: Decide how Scala Futures should relate to Observables. Should there be a " + "common base interface for Future and Observable? And should Futures also have an unsubscribe method?]" - val correspondence = defaultMethodCorrespondence ++ Map( + /** + * Maps each method from the Java Observable to its corresponding method in the Scala Observable + */ + val correspondence = defaultMethodCorrespondence ++ correspondenceChanges // ++ overrides LHS with RHS + + /** + * Creates default method correspondence mappings, assuming that Scala methods have the same + * name and the same argument types as in Java + */ + def defaultMethodCorrespondence: Map[String, String] = { + val allMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.Observable[_]]) + val tuples = for (javaM <- allMethods) yield (javaM, javaMethodSignatureToScala(javaM)) + tuples.toMap + } + + /** + * Manually added mappings from Java Observable methods to Scala Observable methods + */ + def correspondenceChanges = Map( // manually added entries for Java instance methods "aggregate(Func2[T, T, T])" -> "reduce((U, U) => U)", "aggregate(R, Func2[R, _ >: T, R])" -> "foldLeft(R)((R, T) => R)", @@ -158,8 +181,7 @@ class CompletenessTest extends JUnitSuite { def getPublicInstanceAndCompanionMethods(tp: Type): Iterable[String] = getPublicInstanceMethods(tp) ++ getPublicInstanceMethods(tp.typeSymbol.companionSymbol.typeSignature) - - + def printMethodSet(title: String, tp: Type) { println("\n" + title) println(title.map(_ => '-') + "\n") @@ -204,12 +226,6 @@ class CompletenessTest extends JUnitSuite { .replaceAll("(\\w+)\\(\\)", "$1") } - def defaultMethodCorrespondence: Map[String, String] = { - val allMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.Observable[_]]) - val tuples = for (javaM <- allMethods) yield (javaM, javaMethodSignatureToScala(javaM)) - tuples.toMap - } - @Ignore // because spams output @Test def printDefaultMethodCorrespondence: Unit = { println("\nDefault Method Correspondence") From e71110ea156b6f7822b948f042d93bbfffdd5525 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Tue, 29 Oct 2013 17:09:00 +0800 Subject: [PATCH 213/333] Fixed issue #454 --- .../java/rx/subscriptions/Subscriptions.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java b/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java index 032a0eaece..5febe4f46a 100644 --- a/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java +++ b/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java @@ -15,9 +15,16 @@ */ package rx.subscriptions; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + import java.util.concurrent.Future; +import org.junit.Test; + import rx.Subscription; +import rx.operators.SafeObservableSubscription; import rx.util.functions.Action0; /** @@ -41,14 +48,14 @@ public static Subscription empty() { * @return {@link Subscription} */ public static Subscription create(final Action0 unsubscribe) { - return new Subscription() { + return new SafeObservableSubscription(new Subscription() { @Override public void unsubscribe() { unsubscribe.call(); } - }; + }); } /** @@ -122,4 +129,15 @@ public static CompositeSubscription create(Subscription... subscriptions) { public void unsubscribe() { } }; + + public static class UnitTest { + @Test + public void testUnsubscribeOnlyOnce() { + Action0 unsubscribe = mock(Action0.class); + Subscription subscription = create(unsubscribe); + subscription.unsubscribe(); + subscription.unsubscribe(); + verify(unsubscribe, times(1)).call(); + } + } } From b032731cf03105d2666f63416b98c23beefb2eb3 Mon Sep 17 00:00:00 2001 From: Mustafa Sezgin Date: Tue, 15 Oct 2013 15:50:42 +0200 Subject: [PATCH 214/333] Renamed the log tag as it was too long --- .../java/rx/operators/OperationObserveFromAndroidComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java index ad363f7912..a1aaff5354 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java @@ -49,7 +49,7 @@ public static Observable observeFromAndroidComponent(Observable source private static class OnSubscribeBase implements Observable.OnSubscribeFunc { - private static final String LOG_TAG = OperationObserveFromAndroidComponent.class.getSimpleName(); + private static final String LOG_TAG = "AndroidObserver"; private final Observable source; private AndroidComponent componentRef; From 5417b596b59f001b1b04f6680349a33c38180c34 Mon Sep 17 00:00:00 2001 From: Matthias Kaeppler Date: Tue, 29 Oct 2013 11:37:06 +0100 Subject: [PATCH 215/333] Singularize AndroidObservables, move to observables package --- .../AndroidObservable.java} | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename rxjava-contrib/rxjava-android/src/main/java/rx/android/{AndroidObservables.java => observables/AndroidObservable.java} (88%) diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/android/AndroidObservables.java b/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java similarity index 88% rename from rxjava-contrib/rxjava-android/src/main/java/rx/android/AndroidObservables.java rename to rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java index db5768d43b..e411074be3 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/android/AndroidObservables.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java @@ -1,4 +1,4 @@ -package rx.android; +package rx.android.observables; import rx.Observable; import rx.operators.OperationObserveFromAndroidComponent; @@ -6,7 +6,9 @@ import android.app.Activity; import android.app.Fragment; -public class AndroidObservables { +public final class AndroidObservable { + + private AndroidObservable() {} public static Observable fromActivity(Activity activity, Observable sourceObservable) { return OperationObserveFromAndroidComponent.observeFromAndroidComponent(sourceObservable, activity); From d64a8c5f73d8d1a5de1861e0d20f12609b408880 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 31 Oct 2013 13:34:20 -0700 Subject: [PATCH 216/333] Update httpasyncclient to 4.0 from 4.0-beta4 --- rxjava-contrib/rxjava-apache-http/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rxjava-contrib/rxjava-apache-http/build.gradle b/rxjava-contrib/rxjava-apache-http/build.gradle index b51cb6c213..81d150ccc3 100644 --- a/rxjava-contrib/rxjava-apache-http/build.gradle +++ b/rxjava-contrib/rxjava-apache-http/build.gradle @@ -7,7 +7,7 @@ dependencies { compile project(':rxjava-core') compile 'org.apache.httpcomponents:httpclient:4.3' compile 'org.apache.httpcomponents:httpcore-nio:4.3' - compile 'org.apache.httpcomponents:httpasyncclient:4.0-beta4' + compile 'org.apache.httpcomponents:httpasyncclient:4.0' } jar { From 7cdbc94276bb8596d2257857f11af892c56896a2 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Thu, 31 Oct 2013 20:43:30 +0000 Subject: [PATCH 217/333] [Gradle Release Plugin] - pre tag commit: '0.14.7'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1e13b14016..22c647b075 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.7-SNAPSHOT +version=0.14.7 From e54943d387ff7dd36602cff56e034bbcb2d2eb9d Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Thu, 31 Oct 2013 20:43:34 +0000 Subject: [PATCH 218/333] [Gradle Release Plugin] - new version commit: '0.14.8-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 22c647b075..70d33a25c8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.7 +version=0.14.8-SNAPSHOT From bcf6eb288e966e132502f7abe12a47ec2b843eea Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 31 Oct 2013 13:52:41 -0700 Subject: [PATCH 219/333] Version 0.14.7 --- CHANGES.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 37fb3b7235..0b3262002a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,15 @@ # RxJava Releases # +### Version 0.14.7 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.7%22)) ### + +* [Pull 459](https://github.com/Netflix/RxJava/pull/459) Fix multiple unsubscribe behavior +* [Pull 458](https://github.com/Netflix/RxJava/pull/458) rxjava-android: OperationObserveFromAndroidComponent +* [Pull 453](https://github.com/Netflix/RxJava/pull/453) Fix error handling in map operator +* [Pull 450](https://github.com/Netflix/RxJava/pull/450) Operator: TimeInterval +* [Pull 452](https://github.com/Netflix/RxJava/pull/451) Scheduler Overload of Just/Return Operator +* [Pull 433](https://github.com/Netflix/RxJava/pull/433) Fixes: Next Operator +* [Commit d64a8c5](https://github.com/Netflix/RxJava/commit/d64a8c5f73d8d1a5de1861e0d20f12609b408880) Update rxjava-apache-http to Apache HttpAsyncClient 4.0 GA + ### Version 0.14.6 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.6%22)) ### * [Pull 441](https://github.com/Netflix/RxJava/pull/441) Fixed the issue that 'take' does not call 'onError' From 24619f055076a5b5d0ee8e582fd6325b146f352a Mon Sep 17 00:00:00 2001 From: zsxwing Date: Fri, 1 Nov 2013 20:33:43 +0800 Subject: [PATCH 220/333] Implemented the 'Amb' operator --- rxjava-core/src/main/java/rx/Observable.java | 29 +++ .../main/java/rx/operators/OperationAmb.java | 245 ++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationAmb.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 8cf4c951ed..76171ac761 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -30,6 +30,7 @@ import rx.observables.ConnectableObservable; import rx.observables.GroupedObservable; import rx.operators.OperationAll; +import rx.operators.OperationAmb; import rx.operators.OperationAny; import rx.operators.OperationAverage; import rx.operators.OperationBuffer; @@ -4576,6 +4577,34 @@ public Observable> timeInterval(Scheduler scheduler) { return create(OperationTimeInterval.timeInterval(this, scheduler)); } + /** + * Propagates the observable sequence that reacts first. + * + * @param sources + * observable sources competing to react first. + * + * @return + * an observable sequence that surfaces any of the given sequences, whichever reacted first. + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable... sources) { + return create(OperationAmb.amb(sources)); + } + + /** + * Propagates the observable sequence that reacts first. + * + * @param sources + * observable sources competing to react first. + * + * @return + * an observable sequence that surfaces any of the given sequences, whichever reacted first. + * @see MSDN: Observable.Amb + */ + public static Observable amb(Iterable> sources) { + return create(OperationAmb.amb(sources)); + } + /** * Whether a given {@link Function} is an internal implementation inside rx.* packages or not. *

diff --git a/rxjava-core/src/main/java/rx/operators/OperationAmb.java b/rxjava-core/src/main/java/rx/operators/OperationAmb.java new file mode 100644 index 0000000000..505814acf3 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationAmb.java @@ -0,0 +1,245 @@ +/** + * Copyright 2013 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.operators; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.concurrency.TestScheduler; +import rx.subscriptions.CompositeSubscription; +import rx.util.functions.Action0; + +/** + * Propagates the observable sequence that reacts first. + */ +public class OperationAmb { + + public static OnSubscribeFunc amb(Observable... sources) { + return amb(Arrays.asList(sources)); + } + + public static OnSubscribeFunc amb( + final Iterable> sources) { + return new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(final Observer observer) { + AtomicInteger choice = new AtomicInteger(AmbObserver.NONE); + int index = 0; + CompositeSubscription parentSubscription = new CompositeSubscription(); + for (Observable source : sources) { + SafeObservableSubscription subscription = new SafeObservableSubscription(); + AmbObserver ambObserver = new AmbObserver( + subscription, observer, index, choice); + parentSubscription.add(subscription.wrap(source + .subscribe(ambObserver))); + index++; + } + return parentSubscription; + } + }; + } + + private static class AmbObserver implements Observer { + + private static final int NONE = -1; + + private Subscription subscription; + private Observer observer; + private int index; + private AtomicInteger choice; + + private AmbObserver(Subscription subscription, + Observer observer, int index, AtomicInteger choice) { + this.subscription = subscription; + this.observer = observer; + this.choice = choice; + this.index = index; + } + + @Override + public void onNext(T args) { + if (!isSelected()) { + subscription.unsubscribe(); + return; + } + observer.onNext(args); + } + + @Override + public void onCompleted() { + if (!isSelected()) { + subscription.unsubscribe(); + return; + } + observer.onCompleted(); + } + + @Override + public void onError(Throwable e) { + if (!isSelected()) { + subscription.unsubscribe(); + return; + } + observer.onError(e); + } + + private boolean isSelected() { + if (choice.get() == NONE) { + return choice.compareAndSet(NONE, index); + } + return choice.get() == index; + } + } + + public static class UnitTest { + private TestScheduler scheduler; + + @Before + public void setUp() { + scheduler = new TestScheduler(); + } + + private Observable createObservable(final String[] values, + final long interval, final Throwable e) { + return Observable.create(new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe( + final Observer observer) { + CompositeSubscription parentSubscription = new CompositeSubscription(); + long delay = interval; + for (final String value : values) { + parentSubscription.add(scheduler.schedule( + new Action0() { + @Override + public void call() { + observer.onNext(value); + } + }, delay, TimeUnit.MILLISECONDS)); + delay += interval; + } + parentSubscription.add(scheduler.schedule(new Action0() { + @Override + public void call() { + if (e == null) { + observer.onCompleted(); + } else { + observer.onError(e); + } + } + }, delay, TimeUnit.MILLISECONDS)); + return parentSubscription; + } + }); + } + + @Test + public void testAmb() { + Observable observable1 = createObservable(new String[] { + "1", "11", "111", "1111" }, 2000, null); + Observable observable2 = createObservable(new String[] { + "2", "22", "222", "2222" }, 1000, null); + Observable observable3 = createObservable(new String[] { + "3", "33", "333", "3333" }, 3000, null); + + @SuppressWarnings("unchecked") + Observable o = Observable.create(amb(observable1, + observable2, observable3)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + o.subscribe(observer); + + scheduler.advanceTimeBy(100000, TimeUnit.MILLISECONDS); + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext("2"); + inOrder.verify(observer, times(1)).onNext("22"); + inOrder.verify(observer, times(1)).onNext("222"); + inOrder.verify(observer, times(1)).onNext("2222"); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testAmb2() { + IOException needHappenedException = new IOException( + "fake exception"); + Observable observable1 = createObservable(new String[] {}, + 2000, new IOException("fake exception")); + Observable observable2 = createObservable(new String[] { + "2", "22", "222", "2222" }, 1000, needHappenedException); + Observable observable3 = createObservable(new String[] {}, + 3000, new IOException("fake exception")); + + @SuppressWarnings("unchecked") + Observable o = Observable.create(amb(observable1, + observable2, observable3)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + o.subscribe(observer); + + scheduler.advanceTimeBy(100000, TimeUnit.MILLISECONDS); + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext("2"); + inOrder.verify(observer, times(1)).onNext("22"); + inOrder.verify(observer, times(1)).onNext("222"); + inOrder.verify(observer, times(1)).onNext("2222"); + inOrder.verify(observer, times(1)).onError(needHappenedException); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testAmb3() { + Observable observable1 = createObservable(new String[] { + "1" }, 2000, null); + Observable observable2 = createObservable(new String[] {}, + 1000, null); + Observable observable3 = createObservable(new String[] { + "3" }, 3000, null); + + @SuppressWarnings("unchecked") + Observable o = Observable.create(amb(observable1, + observable2, observable3)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + o.subscribe(observer); + + scheduler.advanceTimeBy(100000, TimeUnit.MILLISECONDS); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + } +} From 50b04ec8301b11f321e7d9547ba076f6b365bcf4 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Sun, 3 Nov 2013 16:39:08 +0800 Subject: [PATCH 221/333] Removed the 'vararg' overload and added 2-9 args overloads --- rxjava-core/src/main/java/rx/Observable.java | 170 +++++++++++++++++- .../main/java/rx/operators/OperationAmb.java | 90 +++++++++- 2 files changed, 250 insertions(+), 10 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 76171ac761..a6f5f0f7ce 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4580,15 +4580,177 @@ public Observable> timeInterval(Scheduler scheduler) { /** * Propagates the observable sequence that reacts first. * - * @param sources - * observable sources competing to react first. + * @param o1 + * an observable competing to react first. + * @param o2 + * an observable competing to react first. + * @return + * an observable sequence that surfaces any of the given sequences, whichever reacted first. + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2) { + return create(OperationAmb.amb(o1, o2)); + } + + /** + * Propagates the observable sequence that reacts first. * + * @param o1 + * an observable competing to react first. + * @param o2 + * an observable competing to react first. + * @param o3 + * an observable competing to react first. * @return * an observable sequence that surfaces any of the given sequences, whichever reacted first. * @see MSDN: Observable.Amb */ - public static Observable amb(Observable... sources) { - return create(OperationAmb.amb(sources)); + public static Observable amb(Observable o1, Observable o2, Observable o3) { + return create(OperationAmb.amb(o1, o2, o3)); + } + + /** + * Propagates the observable sequence that reacts first. + * + * @param o1 + * an observable competing to react first. + * @param o2 + * an observable competing to react first. + * @param o3 + * an observable competing to react first. + * @param o4 + * an observable competing to react first. + * @return + * an observable sequence that surfaces any of the given sequences, whichever reacted first. + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4) { + return create(OperationAmb.amb(o1, o2, o3, o4)); + } + + /** + * Propagates the observable sequence that reacts first. + * + * @param o1 + * an observable competing to react first. + * @param o2 + * an observable competing to react first. + * @param o3 + * an observable competing to react first. + * @param o4 + * an observable competing to react first. + * @param o5 + * an observable competing to react first. + * @return + * an observable sequence that surfaces any of the given sequences, whichever reacted first. + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5) { + return create(OperationAmb.amb(o1, o2, o3, o4, o5)); + } + + /** + * Propagates the observable sequence that reacts first. + * + * @param o1 + * an observable competing to react first. + * @param o2 + * an observable competing to react first. + * @param o3 + * an observable competing to react first. + * @param o4 + * an observable competing to react first. + * @param o5 + * an observable competing to react first. + * @param o6 + * an observable competing to react first. + * @return + * an observable sequence that surfaces any of the given sequences, whichever reacted first. + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6) { + return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6)); + } + + /** + * Propagates the observable sequence that reacts first. + * + * @param o1 + * an observable competing to react first. + * @param o2 + * an observable competing to react first. + * @param o3 + * an observable competing to react first. + * @param o4 + * an observable competing to react first. + * @param o5 + * an observable competing to react first. + * @param o6 + * an observable competing to react first. + * @param o7 + * an observable competing to react first. + * @return + * an observable sequence that surfaces any of the given sequences, whichever reacted first. + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7) { + return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7)); + } + + /** + * Propagates the observable sequence that reacts first. + * + * @param o1 + * an observable competing to react first. + * @param o2 + * an observable competing to react first. + * @param o3 + * an observable competing to react first. + * @param o4 + * an observable competing to react first. + * @param o5 + * an observable competing to react first. + * @param o6 + * an observable competing to react first. + * @param o7 + * an observable competing to react first. + * @param o8 + * an observable competing to react first. + * @return + * an observable sequence that surfaces any of the given sequences, whichever reacted first. + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8) { + return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8)); + } + + /** + * Propagates the observable sequence that reacts first. + * + * @param o1 + * an observable competing to react first. + * @param o2 + * an observable competing to react first. + * @param o3 + * an observable competing to react first. + * @param o4 + * an observable competing to react first. + * @param o5 + * an observable competing to react first. + * @param o6 + * an observable competing to react first. + * @param o7 + * an observable competing to react first. + * @param o8 + * an observable competing to react first. + * @param o9 + * an observable competing to react first. + * @return + * an observable sequence that surfaces any of the given sequences, whichever reacted first. + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9) { + return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8, o9)); } /** diff --git a/rxjava-core/src/main/java/rx/operators/OperationAmb.java b/rxjava-core/src/main/java/rx/operators/OperationAmb.java index 505814acf3..3074b4d3e9 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAmb.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAmb.java @@ -20,7 +20,8 @@ import static org.mockito.Mockito.times; import java.io.IOException; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -41,8 +42,88 @@ */ public class OperationAmb { - public static OnSubscribeFunc amb(Observable... sources) { - return amb(Arrays.asList(sources)); + public static OnSubscribeFunc amb(Observable o1, Observable o2) { + List> sources = new ArrayList>(); + sources.add(o1); + sources.add(o2); + return amb(sources); + } + + public static OnSubscribeFunc amb(Observable o1, Observable o2, Observable o3) { + List> sources = new ArrayList>(); + sources.add(o1); + sources.add(o2); + sources.add(o3); + return amb(sources); + } + + public static OnSubscribeFunc amb(Observable o1, Observable o2, Observable o3, Observable o4) { + List> sources = new ArrayList>(); + sources.add(o1); + sources.add(o2); + sources.add(o3); + sources.add(o4); + return amb(sources); + } + + public static OnSubscribeFunc amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5) { + List> sources = new ArrayList>(); + sources.add(o1); + sources.add(o2); + sources.add(o3); + sources.add(o4); + sources.add(o5); + return amb(sources); + } + + public static OnSubscribeFunc amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6) { + List> sources = new ArrayList>(); + sources.add(o1); + sources.add(o2); + sources.add(o3); + sources.add(o4); + sources.add(o5); + sources.add(o6); + return amb(sources); + } + + public static OnSubscribeFunc amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7) { + List> sources = new ArrayList>(); + sources.add(o1); + sources.add(o2); + sources.add(o3); + sources.add(o4); + sources.add(o5); + sources.add(o6); + sources.add(o7); + return amb(sources); + } + + public static OnSubscribeFunc amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8) { + List> sources = new ArrayList>(); + sources.add(o1); + sources.add(o2); + sources.add(o3); + sources.add(o4); + sources.add(o5); + sources.add(o6); + sources.add(o7); + sources.add(o8); + return amb(sources); + } + + public static OnSubscribeFunc amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9) { + List> sources = new ArrayList>(); + sources.add(o1); + sources.add(o2); + sources.add(o3); + sources.add(o4); + sources.add(o5); + sources.add(o6); + sources.add(o7); + sources.add(o8); + sources.add(o9); + return amb(sources); } public static OnSubscribeFunc amb( @@ -170,7 +251,6 @@ public void testAmb() { Observable observable3 = createObservable(new String[] { "3", "33", "333", "3333" }, 3000, null); - @SuppressWarnings("unchecked") Observable o = Observable.create(amb(observable1, observable2, observable3)); @@ -200,7 +280,6 @@ public void testAmb2() { Observable observable3 = createObservable(new String[] {}, 3000, new IOException("fake exception")); - @SuppressWarnings("unchecked") Observable o = Observable.create(amb(observable1, observable2, observable3)); @@ -228,7 +307,6 @@ public void testAmb3() { Observable observable3 = createObservable(new String[] { "3" }, 3000, null); - @SuppressWarnings("unchecked") Observable o = Observable.create(amb(observable1, observable2, observable3)); From 1c66113ece291932235ef2ec2b0c4a3ae1dd58bc Mon Sep 17 00:00:00 2001 From: Pedro Viegas Date: Mon, 4 Nov 2013 10:43:51 +0530 Subject: [PATCH 222/333] creating test classes on test source folder --- ...estSchedulers.java => SchedulersTest.java} | 65 +++++++++---------- .../CurrentThreadSchedulerTest.java | 7 ++ .../concurrency/ImmediateSchedulerTest.java | 7 ++ .../observables/BlockingObservableTest.java | 7 ++ .../java/rx/operators/OperationAllTest.java | 7 ++ .../java/rx/operators/OperationAnyTest.java | 7 ++ .../rx/operators/OperationAverageTest.java | 7 ++ .../rx/operators/OperationBufferTest.java | 7 ++ .../java/rx/operators/OperationCacheTest.java | 7 ++ .../java/rx/operators/OperationCastTest.java | 7 ++ .../operators/OperationCombineLatestTest.java | 7 ++ .../rx/operators/OperationConcatTest.java | 7 ++ .../rx/operators/OperationDebounceTest.java | 7 ++ .../OperationDefaultIfEmptyTest.java | 7 ++ .../java/rx/operators/OperationDeferTest.java | 7 ++ .../operators/OperationDematerializeTest.java | 7 ++ .../rx/operators/OperationDistinctTest.java | 7 ++ .../OperationDistinctUntilChangedTest.java | 7 ++ .../rx/operators/OperationElementAtTest.java | 7 ++ .../rx/operators/OperationFilterTest.java | 7 ++ .../rx/operators/OperationFinallyTest.java | 7 ++ .../OperationFirstOrDefaultTest.java | 7 ++ .../rx/operators/OperationGroupByTest.java | 7 ++ .../rx/operators/OperationIntervalTest.java | 7 ++ .../java/rx/operators/OperationMapTest.java | 7 ++ .../operators/OperationMaterializeTest.java | 7 ++ .../OperationMergeDelayErrorTest.java | 7 ++ .../java/rx/operators/OperationMergeTest.java | 7 ++ .../rx/operators/OperationMostRecentTest.java | 7 ++ .../rx/operators/OperationMulticastTest.java | 7 ++ .../java/rx/operators/OperationNextTest.java | 7 ++ .../rx/operators/OperationObserveOnTest.java | 7 ++ ...ationOnErrorResumeNextViaFunctionTest.java | 7 ++ ...ionOnErrorResumeNextViaObservableTest.java | 7 ++ .../operators/OperationOnErrorReturnTest.java | 7 ++ ...nExceptionResumeNextViaObservableTest.java | 7 ++ .../rx/operators/OperationParallelTest.java | 7 ++ .../java/rx/operators/OperationRetryTest.java | 7 ++ .../rx/operators/OperationSampleTest.java | 7 ++ .../java/rx/operators/OperationScanTest.java | 7 ++ .../rx/operators/OperationSkipLastTest.java | 7 ++ .../java/rx/operators/OperationSkipTest.java | 7 ++ .../rx/operators/OperationSkipWhileTest.java | 7 ++ .../operators/OperationSubscribeOnTest.java | 7 ++ .../java/rx/operators/OperationSumTest.java | 7 ++ .../rx/operators/OperationSwitchTest.java | 7 ++ .../operators/OperationSynchronizeTest.java | 7 ++ .../rx/operators/OperationTakeLastTest.java | 7 ++ .../java/rx/operators/OperationTakeTest.java | 7 ++ .../rx/operators/OperationTakeUntilTest.java | 7 ++ .../operators/OperationThrottleFirstTest.java | 7 ++ .../OperationToObservableFutureTest.java | 7 ++ .../OperationToObservableIterableTest.java | 7 ++ .../OperationToObservableListTest.java | 7 ++ .../OperationToObservableSortedListTest.java | 7 ++ .../rx/operators/OperationWindowTest.java | 7 ++ .../java/rx/operators/OperationZipTest.java | 7 ++ .../java/rx/operators/OperatorTesterTest.java | 7 ++ .../SafeObservableSubscriptionTest.java | 7 ++ .../operators/SynchronizedObserverTest.java | 7 ++ .../test/java/rx/operators/TakeWhileTest.java | 7 ++ .../operators/TimeIntervalObserverTest.java | 7 ++ .../java/rx/plugins/RxJavaPluginsTest.java | 7 ++ .../java/rx/subjects/AsyncSubjectTest.java | 7 ++ .../java/rx/subjects/BehaviorSubjectTest.java | 7 ++ .../java/rx/subjects/PublishSubjectTest.java | 7 ++ .../java/rx/subjects/ReplaySubjectTest.java | 7 ++ .../CompositeSubscriptionTest.java | 7 ++ .../rx/subscriptions/SubscriptionsTest.java | 7 ++ .../src/test/java/rx/util/RangeTest.java | 7 ++ 70 files changed, 514 insertions(+), 34 deletions(-) rename rxjava-core/src/test/java/rx/{concurrency/TestSchedulers.java => SchedulersTest.java} (95%) create mode 100644 rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java create mode 100644 rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java create mode 100644 rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationAllTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationAnyTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationAverageTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationBufferTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationCacheTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationCastTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationConcatTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationDeferTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationFilterTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationMapTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationMergeTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationNextTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationParallelTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationRetryTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationSampleTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationScanTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationSkipTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationSumTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationTakeTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationWindowTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationZipTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperatorTesterTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/TakeWhileTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/TimeIntervalObserverTest.java create mode 100644 rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java create mode 100644 rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java create mode 100644 rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java create mode 100644 rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java create mode 100644 rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java create mode 100644 rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java create mode 100644 rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java create mode 100644 rxjava-core/src/test/java/rx/util/RangeTest.java diff --git a/rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java b/rxjava-core/src/test/java/rx/SchedulersTest.java similarity index 95% rename from rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java rename to rxjava-core/src/test/java/rx/SchedulersTest.java index e1df828237..d2ac58e61f 100644 --- a/rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java +++ b/rxjava-core/src/test/java/rx/SchedulersTest.java @@ -1,36 +1,24 @@ /** * Copyright 2013 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.concurrency; - -import static org.junit.Assert.*; - -import java.util.Date; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; +package rx; import org.junit.Test; - -import rx.Observable; import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Scheduler; -import rx.Subscription; +import rx.concurrency.Schedulers; +import rx.concurrency.TestScheduler; import rx.subscriptions.BooleanSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; @@ -38,7 +26,16 @@ import rx.util.functions.Func1; import rx.util.functions.Func2; -public class TestSchedulers { +import java.util.Date; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.*; + +public class SchedulersTest { @Test public void testComputationThreadPool1() { @@ -447,18 +444,18 @@ public void testSubscribeOnNestedConcurrency() throws InterruptedException { Observable o = Observable.from("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten") .mapMany(new Func1>() { - @Override - public Observable call(final String v) { - return Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(final Observer observer) { - observer.onNext("value_after_map-" + v); - observer.onCompleted(); - return Subscriptions.empty(); - } - }).subscribeOn(Schedulers.newThread()); // subscribe on a new thread - } + @Override + public Observable call(final String v) { + return Observable.create(new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(final Observer observer) { + observer.onNext("value_after_map-" + v); + observer.onCompleted(); + return Subscriptions.empty(); + } + }).subscribeOn(Schedulers.newThread()); // subscribe on a new thread + } }); ConcurrentObserverValidator observer = new ConcurrentObserverValidator(); @@ -474,7 +471,7 @@ public Subscription onSubscribe(final Observer observer) { fail("Error: " + observer.error.get().getMessage()); } } - + @Test public void testRecursion() { TestScheduler s = new TestScheduler(); @@ -494,11 +491,11 @@ public void call(Action0 self) { subscription.unsubscribe(); assertEquals(0, counter.get()); } - + /** * Used to determine if onNext is being invoked concurrently. - * + * * @param */ private static class ConcurrentObserverValidator implements Observer { diff --git a/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java b/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java new file mode 100644 index 0000000000..b9a9b1a1c2 --- /dev/null +++ b/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java @@ -0,0 +1,7 @@ +package rx.concurrency; + +import org.junit.Ignore; + +@Ignore("WIP") +public class CurrentThreadSchedulerTest { +} diff --git a/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java b/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java new file mode 100644 index 0000000000..036103e540 --- /dev/null +++ b/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java @@ -0,0 +1,7 @@ +package rx.concurrency; + +import org.junit.Ignore; + +@Ignore("WIP") +public class ImmediateSchedulerTest { +} diff --git a/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java b/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java new file mode 100644 index 0000000000..aa38f711ad --- /dev/null +++ b/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java @@ -0,0 +1,7 @@ +package rx.observables; + +import org.junit.Ignore; + +@Ignore("WIP") +public class BlockingObservableTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationAllTest.java b/rxjava-core/src/test/java/rx/operators/OperationAllTest.java new file mode 100644 index 0000000000..ffb0b56798 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationAllTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationAllTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java b/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java new file mode 100644 index 0000000000..98d6e72fa5 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationAnyTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java b/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java new file mode 100644 index 0000000000..6fa01dfc92 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationAverageTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java b/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java new file mode 100644 index 0000000000..d096cd666a --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationBufferTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java b/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java new file mode 100644 index 0000000000..4a754528e5 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationCacheTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationCastTest.java b/rxjava-core/src/test/java/rx/operators/OperationCastTest.java new file mode 100644 index 0000000000..36bab268ff --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationCastTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationCastTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java b/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java new file mode 100644 index 0000000000..b154ce1223 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationCombineLatestTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java b/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java new file mode 100644 index 0000000000..1380ffcaef --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationConcatTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java b/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java new file mode 100644 index 0000000000..a1f6d4c237 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationDebounceTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java b/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java new file mode 100644 index 0000000000..241c8790c3 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationDefaultIfEmptyTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java b/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java new file mode 100644 index 0000000000..344cbee450 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationDeferTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java b/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java new file mode 100644 index 0000000000..f0433032fb --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationDematerializeTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java b/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java new file mode 100644 index 0000000000..1704b644fc --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationDistinctTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java b/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java new file mode 100644 index 0000000000..cdb4eb7203 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationDistinctUntilChangedTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java b/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java new file mode 100644 index 0000000000..b17efa3cd3 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationElementAtTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java b/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java new file mode 100644 index 0000000000..19e15ea81a --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationFilterTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java b/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java new file mode 100644 index 0000000000..0e46ce1cb2 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationFinallyTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java b/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java new file mode 100644 index 0000000000..39a60a03c5 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationFirstOrDefaultTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java b/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java new file mode 100644 index 0000000000..222085a96f --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationGroupByTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java b/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java new file mode 100644 index 0000000000..60b2ad7789 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationIntervalTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java new file mode 100644 index 0000000000..3c3ee5813c --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationMapTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java b/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java new file mode 100644 index 0000000000..24f67b432c --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationMaterializeTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java b/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java new file mode 100644 index 0000000000..1e3c763573 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationMergeDelayErrorTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java b/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java new file mode 100644 index 0000000000..ef50b179e6 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationMergeTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java b/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java new file mode 100644 index 0000000000..1f5806b310 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationMostRecentTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java b/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java new file mode 100644 index 0000000000..dd2ba06360 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationMulticastTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationNextTest.java b/rxjava-core/src/test/java/rx/operators/OperationNextTest.java new file mode 100644 index 0000000000..bad26bbcb1 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationNextTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationNextTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java b/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java new file mode 100644 index 0000000000..9f439949a8 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationObserveOnTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java new file mode 100644 index 0000000000..8a98b35dd2 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationOnErrorResumeNextViaFunctionTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java new file mode 100644 index 0000000000..b0ec9f3211 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationOnErrorResumeNextViaObservableTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java new file mode 100644 index 0000000000..0fe50a7f04 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationOnErrorReturnTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java new file mode 100644 index 0000000000..4e8a7c1586 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationOnExceptionResumeNextViaObservableTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java b/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java new file mode 100644 index 0000000000..fee3816e98 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationParallelTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java b/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java new file mode 100644 index 0000000000..768d1f5f9c --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationRetryTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java b/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java new file mode 100644 index 0000000000..a718adbc0a --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationSampleTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationScanTest.java b/rxjava-core/src/test/java/rx/operators/OperationScanTest.java new file mode 100644 index 0000000000..aa3d7d6f9e --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationScanTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationScanTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java new file mode 100644 index 0000000000..9604c718c7 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationSkipLastTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java new file mode 100644 index 0000000000..1d74ed6302 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationSkipTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java new file mode 100644 index 0000000000..d9347d907e --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationSkipWhileTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java b/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java new file mode 100644 index 0000000000..495cbc6713 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationSubscribeOnTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationSumTest.java b/rxjava-core/src/test/java/rx/operators/OperationSumTest.java new file mode 100644 index 0000000000..c89121e207 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSumTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationSumTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java b/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java new file mode 100644 index 0000000000..d91bb804bf --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationSwitchTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java b/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java new file mode 100644 index 0000000000..2dce4a7d32 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationSynchronizeTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java new file mode 100644 index 0000000000..234e70d543 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationTakeLastTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java new file mode 100644 index 0000000000..eaa1bdf1d4 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationTakeTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java new file mode 100644 index 0000000000..22cd5eb078 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationTakeUntilTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java b/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java new file mode 100644 index 0000000000..66301abfca --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationThrottleFirstTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java new file mode 100644 index 0000000000..3f58b63911 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationToObservableFutureTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java new file mode 100644 index 0000000000..1805e1cbcd --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationToObservableIterableTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java new file mode 100644 index 0000000000..49c7409e86 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationToObservableListTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java new file mode 100644 index 0000000000..62efef680d --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationToObservableSortedListTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java b/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java new file mode 100644 index 0000000000..0e602254f5 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationWindowTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationZipTest.java b/rxjava-core/src/test/java/rx/operators/OperationZipTest.java new file mode 100644 index 0000000000..3e027193ab --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationZipTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperationZipTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/OperatorTesterTest.java b/rxjava-core/src/test/java/rx/operators/OperatorTesterTest.java new file mode 100644 index 0000000000..f5a4161708 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperatorTesterTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class OperatorTesterTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java b/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java new file mode 100644 index 0000000000..c5b7e6d590 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class SafeObservableSubscriptionTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java b/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java new file mode 100644 index 0000000000..5b4b301590 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class SynchronizedObserverTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/TakeWhileTest.java b/rxjava-core/src/test/java/rx/operators/TakeWhileTest.java new file mode 100644 index 0000000000..c81169eac2 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/TakeWhileTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class TakeWhileTest { +} diff --git a/rxjava-core/src/test/java/rx/operators/TimeIntervalObserverTest.java b/rxjava-core/src/test/java/rx/operators/TimeIntervalObserverTest.java new file mode 100644 index 0000000000..6f073696f0 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/TimeIntervalObserverTest.java @@ -0,0 +1,7 @@ +package rx.operators; + +import org.junit.Ignore; + +@Ignore("WIP") +public class TimeIntervalObserverTest { +} diff --git a/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java b/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java new file mode 100644 index 0000000000..1def236246 --- /dev/null +++ b/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java @@ -0,0 +1,7 @@ +package rx.plugins; + +import org.junit.Ignore; + +@Ignore("WIP") +public class RxJavaPluginsTest { +} diff --git a/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java new file mode 100644 index 0000000000..bfe0aa0bb0 --- /dev/null +++ b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java @@ -0,0 +1,7 @@ +package rx.subjects; + +import org.junit.Ignore; + +@Ignore("WIP") +public class AsyncSubjectTest { +} diff --git a/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java new file mode 100644 index 0000000000..ffee087158 --- /dev/null +++ b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java @@ -0,0 +1,7 @@ +package rx.subjects; + +import org.junit.Ignore; + +@Ignore("WIP") +public class BehaviorSubjectTest { +} diff --git a/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java new file mode 100644 index 0000000000..25cebbf510 --- /dev/null +++ b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java @@ -0,0 +1,7 @@ +package rx.subjects; + +import org.junit.Ignore; + +@Ignore("WIP") +public class PublishSubjectTest { +} diff --git a/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java b/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java new file mode 100644 index 0000000000..1e722e5e5e --- /dev/null +++ b/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java @@ -0,0 +1,7 @@ +package rx.subjects; + +import org.junit.Ignore; + +@Ignore("WIP") +public class ReplaySubjectTest { +} diff --git a/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java new file mode 100644 index 0000000000..43830564ce --- /dev/null +++ b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java @@ -0,0 +1,7 @@ +package rx.subscriptions; + +import org.junit.Ignore; + +@Ignore("WIP") +public class CompositeSubscriptionTest { +} diff --git a/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java b/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java new file mode 100644 index 0000000000..17bb5b4619 --- /dev/null +++ b/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java @@ -0,0 +1,7 @@ +package rx.subscriptions; + +import org.junit.Ignore; + +@Ignore("WIP") +public class SubscriptionsTest { +} diff --git a/rxjava-core/src/test/java/rx/util/RangeTest.java b/rxjava-core/src/test/java/rx/util/RangeTest.java new file mode 100644 index 0000000000..640678b5c3 --- /dev/null +++ b/rxjava-core/src/test/java/rx/util/RangeTest.java @@ -0,0 +1,7 @@ +package rx.util; + +import org.junit.Ignore; + +@Ignore("WIP") +public class RangeTest { +} From cc7958d90f40090cd2f57038f7de95a3d4be69c5 Mon Sep 17 00:00:00 2001 From: Pedro Viegas Date: Mon, 4 Nov 2013 12:10:47 +0530 Subject: [PATCH 223/333] moving tests to test source folder #439 --- rxjava-core/src/main/java/rx/Scheduler.java | 59 +- .../concurrency/CurrentThreadScheduler.java | 136 +-- .../rx/concurrency/ImmediateScheduler.java | 103 +-- .../rx/observables/BlockingObservable.java | 258 +----- .../main/java/rx/operators/OperationAll.java | 83 +- .../main/java/rx/operators/OperationAny.java | 183 +--- .../java/rx/operators/OperationAverage.java | 100 --- .../java/rx/operators/OperationBuffer.java | 344 +------ .../java/rx/operators/OperationCache.java | 70 +- .../main/java/rx/operators/OperationCast.java | 41 - .../rx/operators/OperationCombineLatest.java | 631 +------------ .../java/rx/operators/OperationConcat.java | 547 +---------- .../java/rx/operators/OperationDebounce.java | 146 +-- .../rx/operators/OperationDefaultIfEmpty.java | 44 - .../java/rx/operators/OperationDefer.java | 43 - .../rx/operators/OperationDematerialize.java | 53 -- .../java/rx/operators/OperationDistinct.java | 182 +--- .../OperationDistinctUntilChanged.java | 181 +--- .../java/rx/operators/OperationElementAt.java | 114 +-- .../java/rx/operators/OperationFilter.java | 30 - .../java/rx/operators/OperationFinally.java | 32 - .../rx/operators/OperationFirstOrDefault.java | 79 +- .../java/rx/operators/OperationGroupBy.java | 331 +------ .../java/rx/operators/OperationInterval.java | 174 +--- .../main/java/rx/operators/OperationMap.java | 273 ------ .../rx/operators/OperationMaterialize.java | 144 --- .../java/rx/operators/OperationMerge.java | 456 +--------- .../operators/OperationMergeDelayError.java | 500 +---------- .../rx/operators/OperationMostRecent.java | 61 +- .../java/rx/operators/OperationMulticast.java | 93 -- .../main/java/rx/operators/OperationNext.java | 298 +----- .../java/rx/operators/OperationObserveOn.java | 68 -- ...OperationOnErrorResumeNextViaFunction.java | 165 +--- ...erationOnErrorResumeNextViaObservable.java | 127 +-- .../rx/operators/OperationOnErrorReturn.java | 128 +-- ...ionOnExceptionResumeNextViaObservable.java | 224 +---- .../java/rx/operators/OperationParallel.java | 44 +- .../java/rx/operators/OperationRetry.java | 110 +-- .../java/rx/operators/OperationSample.java | 92 +- .../main/java/rx/operators/OperationScan.java | 95 -- .../main/java/rx/operators/OperationSkip.java | 44 +- .../java/rx/operators/OperationSkipLast.java | 106 +-- .../java/rx/operators/OperationSkipWhile.java | 101 +-- .../rx/operators/OperationSubscribeOn.java | 32 - .../main/java/rx/operators/OperationSum.java | 104 --- .../java/rx/operators/OperationSwitch.java | 368 -------- .../rx/operators/OperationSynchronize.java | 199 ---- .../main/java/rx/operators/OperationTake.java | 225 +---- .../java/rx/operators/OperationTakeLast.java | 107 +-- .../java/rx/operators/OperationTakeUntil.java | 160 ---- .../java/rx/operators/OperationTakeWhile.java | 216 +---- .../rx/operators/OperationThrottleFirst.java | 116 +-- .../rx/operators/OperationTimeInterval.java | 57 -- .../OperationToObservableFuture.java | 49 +- .../OperationToObservableIterable.java | 26 - .../operators/OperationToObservableList.java | 55 +- .../OperationToObservableSortedList.java | 54 +- .../java/rx/operators/OperationWindow.java | 308 +------ .../main/java/rx/operators/OperationZip.java | 596 +----------- .../java/rx/operators/OperatorTester.java | 97 -- .../operators/SafeObservableSubscription.java | 17 +- .../rx/operators/SynchronizedObserver.java | 756 ---------------- .../main/java/rx/plugins/RxJavaPlugins.java | 79 +- .../main/java/rx/subjects/AsyncSubject.java | 145 +-- .../java/rx/subjects/BehaviorSubject.java | 146 +-- .../main/java/rx/subjects/PublishSubject.java | 377 +------- .../main/java/rx/subjects/ReplaySubject.java | 184 +--- .../subscriptions/CompositeSubscription.java | 70 +- .../java/rx/subscriptions/Subscriptions.java | 21 +- rxjava-core/src/main/java/rx/util/Range.java | 49 - .../src/test/java/rx/SchedulersTest.java | 850 +++++++++--------- .../CurrentThreadSchedulerTest.java | 126 ++- .../concurrency/ImmediateSchedulerTest.java | 56 +- .../observables/BlockingObservableTest.java | 245 ++++- .../java/rx/operators/OperationAllTest.java | 81 +- .../java/rx/operators/OperationAnyTest.java | 179 +++- .../rx/operators/OperationAverageTest.java | 108 ++- .../rx/operators/OperationBufferTest.java | 347 ++++++- .../java/rx/operators/OperationCacheTest.java | 69 +- .../java/rx/operators/OperationCastTest.java | 37 +- .../operators/OperationCombineLatestTest.java | 616 ++++++++++++- .../rx/operators/OperationConcatTest.java | 541 ++++++++++- .../rx/operators/OperationDebounceTest.java | 143 ++- .../OperationDefaultIfEmptyTest.java | 41 +- .../java/rx/operators/OperationDeferTest.java | 44 +- .../operators/OperationDematerializeTest.java | 55 +- .../rx/operators/OperationDistinctTest.java | 179 +++- .../OperationDistinctUntilChangedTest.java | 182 +++- .../rx/operators/OperationElementAtTest.java | 110 ++- .../rx/operators/OperationFilterTest.java | 32 +- .../rx/operators/OperationFinallyTest.java | 35 +- .../OperationFirstOrDefaultTest.java | 75 +- .../rx/operators/OperationGroupByTest.java | 328 ++++++- .../rx/operators/OperationIntervalTest.java | 173 +++- .../java/rx/operators/OperationMapTest.java | 273 +++++- .../operators/OperationMaterializeTest.java | 147 ++- .../OperationMergeDelayErrorTest.java | 497 +++++++++- .../java/rx/operators/OperationMergeTest.java | 455 +++++++++- .../rx/operators/OperationMostRecentTest.java | 56 +- .../rx/operators/OperationMulticastTest.java | 94 +- .../java/rx/operators/OperationNextTest.java | 278 +++++- .../rx/operators/OperationObserveOnTest.java | 65 +- ...ationOnErrorResumeNextViaFunctionTest.java | 165 +++- ...ionOnErrorResumeNextViaObservableTest.java | 124 ++- .../operators/OperationOnErrorReturnTest.java | 127 ++- ...nExceptionResumeNextViaObservableTest.java | 221 ++++- .../rx/operators/OperationParallelTest.java | 42 +- .../java/rx/operators/OperationRetryTest.java | 111 ++- .../rx/operators/OperationSampleTest.java | 88 +- .../java/rx/operators/OperationScanTest.java | 99 +- .../rx/operators/OperationSkipLastTest.java | 95 +- .../java/rx/operators/OperationSkipTest.java | 39 +- .../rx/operators/OperationSkipWhileTest.java | 101 ++- .../operators/OperationSubscribeOnTest.java | 37 +- .../java/rx/operators/OperationSumTest.java | 110 ++- .../rx/operators/OperationSwitchTest.java | 365 +++++++- .../operators/OperationSynchronizeTest.java | 200 ++++- .../rx/operators/OperationTakeLastTest.java | 94 +- .../java/rx/operators/OperationTakeTest.java | 210 ++++- .../rx/operators/OperationTakeUntilTest.java | 161 +++- .../rx/operators/OperationTakeWhileTest.java | 204 +++++ .../operators/OperationThrottleFirstTest.java | 111 ++- .../operators/OperationTimeIntervalTest.java | 60 ++ .../OperationToObservableFutureTest.java | 44 +- .../OperationToObservableIterableTest.java | 27 +- .../OperationToObservableListTest.java | 50 +- .../OperationToObservableSortedListTest.java | 47 +- .../rx/operators/OperationWindowTest.java | 312 ++++++- .../java/rx/operators/OperationZipTest.java | 579 +++++++++++- .../SafeObservableSubscriptionTest.java | 16 +- .../operators/SynchronizedObserverTest.java | 748 ++++++++++++++- .../java/rx/plugins/RxJavaPluginsTest.java | 74 +- .../java/rx/subjects/AsyncSubjectTest.java | 132 ++- .../java/rx/subjects/BehaviorSubjectTest.java | 132 ++- .../java/rx/subjects/PublishSubjectTest.java | 361 +++++++- .../java/rx/subjects/ReplaySubjectTest.java | 166 +++- .../CompositeSubscriptionTest.java | 67 +- .../rx/subscriptions/SubscriptionsTest.java | 17 +- .../src/test/java/rx/test/OperatorTester.java | 94 ++ .../src/test/java/rx/util/RangeTest.java | 47 +- 140 files changed, 11833 insertions(+), 12067 deletions(-) delete mode 100644 rxjava-core/src/main/java/rx/operators/OperatorTester.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java create mode 100644 rxjava-core/src/test/java/rx/test/OperatorTester.java diff --git a/rxjava-core/src/main/java/rx/Scheduler.java b/rxjava-core/src/main/java/rx/Scheduler.java index cb3430c1f3..3da38f41f2 100644 --- a/rxjava-core/src/main/java/rx/Scheduler.java +++ b/rxjava-core/src/main/java/rx/Scheduler.java @@ -15,26 +15,17 @@ */ package rx; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.Date; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mockito; - -import rx.concurrency.TestScheduler; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.MultipleAssignmentSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; import rx.util.functions.Action1; -import rx.util.functions.Func1; import rx.util.functions.Func2; +import java.util.Date; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + /** * Represents an object that schedules units of work. *

@@ -264,46 +255,4 @@ public long now() { public int degreeOfParallelism() { return Runtime.getRuntime().availableProcessors(); } - - public static class UnitTest { - @SuppressWarnings("unchecked") - // mocking is unchecked, unfortunately - @Test - public void testPeriodicScheduling() { - final Func1 calledOp = mock(Func1.class); - - final TestScheduler scheduler = new TestScheduler(); - Subscription subscription = scheduler.schedulePeriodically(new Action0() { - @Override - public void call() { - System.out.println(scheduler.now()); - calledOp.call(scheduler.now()); - } - }, 1, 2, TimeUnit.SECONDS); - - verify(calledOp, never()).call(anyLong()); - - InOrder inOrder = Mockito.inOrder(calledOp); - - scheduler.advanceTimeBy(999L, TimeUnit.MILLISECONDS); - inOrder.verify(calledOp, never()).call(anyLong()); - - scheduler.advanceTimeBy(1L, TimeUnit.MILLISECONDS); - inOrder.verify(calledOp, times(1)).call(1000L); - - scheduler.advanceTimeBy(1999L, TimeUnit.MILLISECONDS); - inOrder.verify(calledOp, never()).call(3000L); - - scheduler.advanceTimeBy(1L, TimeUnit.MILLISECONDS); - inOrder.verify(calledOp, times(1)).call(3000L); - - scheduler.advanceTimeBy(5L, TimeUnit.SECONDS); - inOrder.verify(calledOp, times(1)).call(5000L); - inOrder.verify(calledOp, times(1)).call(7000L); - - subscription.unsubscribe(); - scheduler.advanceTimeBy(11L, TimeUnit.SECONDS); - inOrder.verify(calledOp, never()).call(anyLong()); - } - } } diff --git a/rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java b/rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java index 455b49d04c..a096ef06e2 100644 --- a/rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java +++ b/rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java @@ -15,20 +15,14 @@ */ package rx.concurrency; -import static org.mockito.Mockito.*; +import rx.Scheduler; +import rx.Subscription; +import rx.util.functions.Func2; import java.util.PriorityQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Test; -import org.mockito.InOrder; - -import rx.Scheduler; -import rx.Subscription; -import rx.util.functions.Action0; -import rx.util.functions.Func2; - /** * Schedules work on the current thread but does not execute immediately. Work is put in a queue and executed after the current unit of work is completed. */ @@ -41,7 +35,7 @@ public static CurrentThreadScheduler getInstance() { private static final ThreadLocal> QUEUE = new ThreadLocal>(); - private CurrentThreadScheduler() { + CurrentThreadScheduler() { } private final AtomicInteger counter = new AtomicInteger(0); @@ -102,126 +96,4 @@ public int compareTo(TimedAction that) { return result; } } - - public static class UnitTest { - - @Test - public void testNestedActions() { - final CurrentThreadScheduler scheduler = new CurrentThreadScheduler(); - - final Action0 firstStepStart = mock(Action0.class); - final Action0 firstStepEnd = mock(Action0.class); - - final Action0 secondStepStart = mock(Action0.class); - final Action0 secondStepEnd = mock(Action0.class); - - final Action0 thirdStepStart = mock(Action0.class); - final Action0 thirdStepEnd = mock(Action0.class); - - final Action0 firstAction = new Action0() { - @Override - public void call() { - firstStepStart.call(); - firstStepEnd.call(); - } - }; - final Action0 secondAction = new Action0() { - @Override - public void call() { - secondStepStart.call(); - scheduler.schedule(firstAction); - secondStepEnd.call(); - - } - }; - final Action0 thirdAction = new Action0() { - @Override - public void call() { - thirdStepStart.call(); - scheduler.schedule(secondAction); - thirdStepEnd.call(); - } - }; - - InOrder inOrder = inOrder(firstStepStart, firstStepEnd, secondStepStart, secondStepEnd, thirdStepStart, thirdStepEnd); - - scheduler.schedule(thirdAction); - - inOrder.verify(thirdStepStart, times(1)).call(); - inOrder.verify(thirdStepEnd, times(1)).call(); - inOrder.verify(secondStepStart, times(1)).call(); - inOrder.verify(secondStepEnd, times(1)).call(); - inOrder.verify(firstStepStart, times(1)).call(); - inOrder.verify(firstStepEnd, times(1)).call(); - } - - @Test - public void testSequenceOfActions() { - final CurrentThreadScheduler scheduler = new CurrentThreadScheduler(); - - final Action0 first = mock(Action0.class); - final Action0 second = mock(Action0.class); - - scheduler.schedule(first); - scheduler.schedule(second); - - verify(first, times(1)).call(); - verify(second, times(1)).call(); - - } - - @Test - public void testSequenceOfDelayedActions() { - final CurrentThreadScheduler scheduler = new CurrentThreadScheduler(); - - final Action0 first = mock(Action0.class); - final Action0 second = mock(Action0.class); - - scheduler.schedule(new Action0() { - @Override - public void call() { - scheduler.schedule(first, 30, TimeUnit.MILLISECONDS); - scheduler.schedule(second, 10, TimeUnit.MILLISECONDS); - } - }); - - InOrder inOrder = inOrder(first, second); - - inOrder.verify(second, times(1)).call(); - inOrder.verify(first, times(1)).call(); - - - } - - @Test - public void testMixOfDelayedAndNonDelayedActions() { - final CurrentThreadScheduler scheduler = new CurrentThreadScheduler(); - - final Action0 first = mock(Action0.class); - final Action0 second = mock(Action0.class); - final Action0 third = mock(Action0.class); - final Action0 fourth = mock(Action0.class); - - scheduler.schedule(new Action0() { - @Override - public void call() { - scheduler.schedule(first); - scheduler.schedule(second, 300, TimeUnit.MILLISECONDS); - scheduler.schedule(third, 100, TimeUnit.MILLISECONDS); - scheduler.schedule(fourth); - } - }); - - InOrder inOrder = inOrder(first, second, third, fourth); - - inOrder.verify(first, times(1)).call(); - inOrder.verify(fourth, times(1)).call(); - inOrder.verify(third, times(1)).call(); - inOrder.verify(second, times(1)).call(); - - - } - - } - } diff --git a/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java b/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java index f3f4353bb7..d1d00355f4 100644 --- a/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java +++ b/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -15,96 +15,35 @@ */ package rx.concurrency; -import static org.mockito.Mockito.*; - -import java.util.concurrent.TimeUnit; - -import org.junit.Test; -import org.mockito.InOrder; - import rx.Scheduler; import rx.Subscription; -import rx.util.functions.Action0; import rx.util.functions.Func2; +import java.util.concurrent.TimeUnit; + /** * Executes work immediately on the current thread. */ public final class ImmediateScheduler extends Scheduler { - private static final ImmediateScheduler INSTANCE = new ImmediateScheduler(); - - public static ImmediateScheduler getInstance() { - return INSTANCE; - } - - private ImmediateScheduler() { - } - - @Override - public Subscription schedule(T state, Func2 action) { - return action.call(this, state); - } - - @Override - public Subscription schedule(T state, Func2 action, long dueTime, TimeUnit unit) { - // since we are executing immediately on this thread we must cause this thread to sleep - long execTime = now() + unit.toMillis(dueTime); - - return schedule(state, new SleepingAction(action, this, execTime)); - } - - public static class UnitTest { - - @Test - public void testNestedActions() { - final ImmediateScheduler scheduler = new ImmediateScheduler(); - - final Action0 firstStepStart = mock(Action0.class); - final Action0 firstStepEnd = mock(Action0.class); - - final Action0 secondStepStart = mock(Action0.class); - final Action0 secondStepEnd = mock(Action0.class); - - final Action0 thirdStepStart = mock(Action0.class); - final Action0 thirdStepEnd = mock(Action0.class); - - final Action0 firstAction = new Action0() { - @Override - public void call() { - firstStepStart.call(); - firstStepEnd.call(); - } - }; - final Action0 secondAction = new Action0() { - @Override - public void call() { - secondStepStart.call(); - scheduler.schedule(firstAction); - secondStepEnd.call(); - - } - }; - final Action0 thirdAction = new Action0() { - @Override - public void call() { - thirdStepStart.call(); - scheduler.schedule(secondAction); - thirdStepEnd.call(); - } - }; + private static final ImmediateScheduler INSTANCE = new ImmediateScheduler(); - InOrder inOrder = inOrder(firstStepStart, firstStepEnd, secondStepStart, secondStepEnd, thirdStepStart, thirdStepEnd); + public static ImmediateScheduler getInstance() { + return INSTANCE; + } - scheduler.schedule(thirdAction); + ImmediateScheduler() { + } - inOrder.verify(thirdStepStart, times(1)).call(); - inOrder.verify(secondStepStart, times(1)).call(); - inOrder.verify(firstStepStart, times(1)).call(); - inOrder.verify(firstStepEnd, times(1)).call(); - inOrder.verify(secondStepEnd, times(1)).call(); - inOrder.verify(thirdStepEnd, times(1)).call(); - } + @Override + public Subscription schedule(T state, Func2 action) { + return action.call(this, state); + } - } + @Override + public Subscription schedule(T state, Func2 action, long dueTime, TimeUnit unit) { + // since we are executing immediately on this thread we must cause this thread to sleep + long execTime = now() + unit.toMillis(dueTime); + return schedule(state, new SleepingAction(action, this, execTime)); + } } diff --git a/rxjava-core/src/main/java/rx/observables/BlockingObservable.java b/rxjava-core/src/main/java/rx/observables/BlockingObservable.java index 32e2db7400..b59f34d78c 100644 --- a/rxjava-core/src/main/java/rx/observables/BlockingObservable.java +++ b/rxjava-core/src/main/java/rx/observables/BlockingObservable.java @@ -15,33 +15,18 @@ */ package rx.observables; -import static org.junit.Assert.*; - -import java.util.Iterator; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - import rx.Observable; -import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.operators.OperationMostRecent; -import rx.operators.OperationNext; -import rx.operators.OperationToFuture; -import rx.operators.OperationToIterator; -import rx.operators.SafeObservableSubscription; -import rx.operators.SafeObserver; -import rx.subscriptions.BooleanSubscription; -import rx.subscriptions.Subscriptions; +import rx.operators.*; import rx.util.functions.Action1; import rx.util.functions.Func1; +import java.util.Iterator; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReference; + /** * An extension of {@link Observable} that provides blocking operators. *

@@ -367,235 +352,4 @@ public Iterator iterator() { } }; } - - public static class UnitTest { - - @Mock - Observer w; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testLast() { - BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three")); - - assertEquals("three", obs.last()); - } - - @Test - public void testLastEmptyObservable() { - BlockingObservable obs = BlockingObservable.from(Observable.empty()); - - assertNull(obs.last()); - } - - @Test - public void testLastOrDefault() { - BlockingObservable observable = BlockingObservable.from(Observable.from(1, 0, -1)); - int last = observable.lastOrDefault(-100, new Func1() { - @Override - public Boolean call(Integer args) { - return args >= 0; - } - }); - assertEquals(0, last); - } - - @Test - public void testLastOrDefault1() { - BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "three")); - assertEquals("three", observable.lastOrDefault("default")); - } - - @Test - public void testLastOrDefault2() { - BlockingObservable observable = BlockingObservable.from(Observable.empty()); - assertEquals("default", observable.lastOrDefault("default")); - } - - @Test - public void testLastOrDefaultWithPredicate() { - BlockingObservable observable = BlockingObservable.from(Observable.from(1, 0, -1)); - int last = observable.lastOrDefault(0, new Func1() { - @Override - public Boolean call(Integer args) { - return args < 0; - } - }); - - assertEquals(-1, last); - } - - @Test - public void testLastOrDefaultWrongPredicate() { - BlockingObservable observable = BlockingObservable.from(Observable.from(-1, -2, -3)); - int last = observable.lastOrDefault(0, new Func1() { - @Override - public Boolean call(Integer args) { - return args >= 0; - } - }); - assertEquals(0, last); - } - - @Test - public void testLastWithPredicate() { - BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three")); - - assertEquals("two", obs.last(new Func1() { - @Override - public Boolean call(String s) { - return s.length() == 3; - } - })); - } - - public void testSingle() { - BlockingObservable observable = BlockingObservable.from(Observable.from("one")); - assertEquals("one", observable.single()); - } - - @Test - public void testSingleDefault() { - BlockingObservable observable = BlockingObservable.from(Observable.empty()); - assertEquals("default", observable.singleOrDefault("default")); - } - - @Test(expected = IllegalStateException.class) - public void testSingleDefaultPredicateMatchesMoreThanOne() { - BlockingObservable.from(Observable.from("one", "two")).singleOrDefault("default", new Func1() { - @Override - public Boolean call(String args) { - return args.length() == 3; - } - }); - } - - @Test - public void testSingleDefaultPredicateMatchesNothing() { - BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two")); - String result = observable.singleOrDefault("default", new Func1() { - @Override - public Boolean call(String args) { - return args.length() == 4; - } - }); - assertEquals("default", result); - } - - @Test(expected = IllegalStateException.class) - public void testSingleDefaultWithMoreThanOne() { - BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "three")); - observable.singleOrDefault("default"); - } - - @Test - public void testSingleWithPredicateDefault() { - BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "four")); - assertEquals("four", observable.single(new Func1() { - @Override - public Boolean call(String s) { - return s.length() == 4; - } - })); - } - - @Test(expected = IllegalStateException.class) - public void testSingleWrong() { - BlockingObservable observable = BlockingObservable.from(Observable.from(1, 2)); - observable.single(); - } - - @Test(expected = IllegalStateException.class) - public void testSingleWrongPredicate() { - BlockingObservable observable = BlockingObservable.from(Observable.from(-1)); - observable.single(new Func1() { - @Override - public Boolean call(Integer args) { - return args > 0; - } - }); - } - - @Test - public void testToIterable() { - BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three")); - - Iterator it = obs.toIterable().iterator(); - - assertEquals(true, it.hasNext()); - assertEquals("one", it.next()); - - assertEquals(true, it.hasNext()); - assertEquals("two", it.next()); - - assertEquals(true, it.hasNext()); - assertEquals("three", it.next()); - - assertEquals(false, it.hasNext()); - - } - - @Test(expected = TestException.class) - public void testToIterableWithException() { - BlockingObservable obs = BlockingObservable.from(Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(Observer observer) { - observer.onNext("one"); - observer.onError(new TestException()); - return Subscriptions.empty(); - } - })); - - Iterator it = obs.toIterable().iterator(); - - assertEquals(true, it.hasNext()); - assertEquals("one", it.next()); - - assertEquals(true, it.hasNext()); - it.next(); - - } - - @Test - public void testForEachWithError() { - try { - BlockingObservable.from(Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(final Observer observer) { - final BooleanSubscription subscription = new BooleanSubscription(); - new Thread(new Runnable() { - - @Override - public void run() { - observer.onNext("one"); - observer.onNext("two"); - observer.onNext("three"); - observer.onCompleted(); - } - }).start(); - return subscription; - } - })).forEach(new Action1() { - - @Override - public void call(String t1) { - throw new RuntimeException("fail"); - } - }); - fail("we expect an exception to be thrown"); - } catch (Throwable e) { - // do nothing as we expect this - } - } - - private static class TestException extends RuntimeException { - private static final long serialVersionUID = 1L; - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationAll.java b/rxjava-core/src/main/java/rx/operators/OperationAll.java index e0a18154c0..908fd1f54e 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAll.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAll.java @@ -15,18 +15,14 @@ */ package rx.operators; -import static org.mockito.Mockito.*; - -import java.util.concurrent.atomic.AtomicBoolean; - -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.util.functions.Func1; +import java.util.concurrent.atomic.AtomicBoolean; + /** * Returns an Observable that emits a Boolean that indicates whether all items emitted by an * Observable satisfy a condition. @@ -94,79 +90,4 @@ public void onNext(T args) { } } - - public static class UnitTest { - - @Test - @SuppressWarnings("unchecked") - public void testAll() { - Observable obs = Observable.from("one", "two", "six"); - - Observer observer = mock(Observer.class); - Observable.create(all(obs, new Func1() { - @Override - public Boolean call(String s) { - return s.length() == 3; - } - })).subscribe(observer); - - verify(observer).onNext(true); - verify(observer).onCompleted(); - verifyNoMoreInteractions(observer); - } - - @Test - @SuppressWarnings("unchecked") - public void testNotAll() { - Observable obs = Observable.from("one", "two", "three", "six"); - - Observer observer = mock(Observer.class); - Observable.create(all(obs, new Func1() { - @Override - public Boolean call(String s) { - return s.length() == 3; - } - })).subscribe(observer); - - verify(observer).onNext(false); - verify(observer).onCompleted(); - verifyNoMoreInteractions(observer); - } - - @Test - @SuppressWarnings("unchecked") - public void testEmpty() { - Observable obs = Observable.empty(); - - Observer observer = mock(Observer.class); - Observable.create(all(obs, new Func1() { - @Override - public Boolean call(String s) { - return s.length() == 3; - } - })).subscribe(observer); - - verify(observer).onNext(true); - verify(observer).onCompleted(); - verifyNoMoreInteractions(observer); - } - - @Test - @SuppressWarnings("unchecked") - public void testError() { - Throwable error = new Throwable(); - Observable obs = Observable.error(error); - - Observer observer = mock(Observer.class); - Observable.create(all(obs, new Func1() { - @Override - public Boolean call(String s) { - return s.length() == 3; - } - })).subscribe(observer); - - verify(observer).onError(error); - verifyNoMoreInteractions(observer); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationAny.java b/rxjava-core/src/main/java/rx/operators/OperationAny.java index a5f12c224f..5830fbb1f2 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAny.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAny.java @@ -15,19 +15,16 @@ */ package rx.operators; -import static org.mockito.Mockito.*; -import static rx.util.functions.Functions.*; - -import java.util.concurrent.atomic.AtomicBoolean; - -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.util.functions.Func1; +import java.util.concurrent.atomic.AtomicBoolean; + +import static rx.util.functions.Functions.alwaysTrue; + /** * Returns an {@link Observable} that emits true if any element of * an observable sequence satisfies a condition, otherwise false. @@ -126,176 +123,4 @@ public void onCompleted() { } } - - public static class UnitTest { - - @Test - public void testAnyWithTwoItems() { - Observable w = Observable.from(1, 2); - Observable observable = Observable.create(any(w)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(false); - verify(aObserver, times(1)).onNext(true); - verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testIsEmptyWithTwoItems() { - Observable w = Observable.from(1, 2); - Observable observable = Observable.create(isEmpty(w)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(true); - verify(aObserver, times(1)).onNext(false); - verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testAnyWithOneItem() { - Observable w = Observable.from(1); - Observable observable = Observable.create(any(w)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(false); - verify(aObserver, times(1)).onNext(true); - verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testIsEmptyWithOneItem() { - Observable w = Observable.from(1); - Observable observable = Observable.create(isEmpty(w)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(true); - verify(aObserver, times(1)).onNext(false); - verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testAnyWithEmpty() { - Observable w = Observable.empty(); - Observable observable = Observable.create(any(w)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(false); - verify(aObserver, never()).onNext(true); - verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testIsEmptyWithEmpty() { - Observable w = Observable.empty(); - Observable observable = Observable.create(isEmpty(w)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(true); - verify(aObserver, never()).onNext(false); - verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testAnyWithPredicate1() { - Observable w = Observable.from(1, 2, 3); - Observable observable = Observable.create(any(w, - new Func1() { - - @Override - public Boolean call(Integer t1) { - return t1 < 2; - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(false); - verify(aObserver, times(1)).onNext(true); - verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testExists1() { - Observable w = Observable.from(1, 2, 3); - Observable observable = Observable.create(exists(w, - new Func1() { - - @Override - public Boolean call(Integer t1) { - return t1 < 2; - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(false); - verify(aObserver, times(1)).onNext(true); - verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testAnyWithPredicate2() { - Observable w = Observable.from(1, 2, 3); - Observable observable = Observable.create(any(w, - new Func1() { - - @Override - public Boolean call(Integer t1) { - return t1 < 1; - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(false); - verify(aObserver, never()).onNext(true); - verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testAnyWithEmptyAndPredicate() { - // If the source is empty, always output false. - Observable w = Observable.empty(); - Observable observable = Observable.create(any(w, - new Func1() { - - @Override - public Boolean call(Integer t1) { - return true; - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(false); - verify(aObserver, never()).onNext(true); - verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationAverage.java b/rxjava-core/src/main/java/rx/operators/OperationAverage.java index 054a117053..c130396f2a 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAverage.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAverage.java @@ -15,12 +15,7 @@ */ package rx.operators; -import static org.mockito.Mockito.*; - -import org.junit.Test; - import rx.Observable; -import rx.Observer; import rx.util.functions.Func1; import rx.util.functions.Func2; @@ -100,99 +95,4 @@ public Double call(Tuple2 result) { } }); } - - public static class UnitTest { - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - @SuppressWarnings("unchecked") - Observer wl = mock(Observer.class); - @SuppressWarnings("unchecked") - Observer wf = mock(Observer.class); - @SuppressWarnings("unchecked") - Observer wd = mock(Observer.class); - - @Test - public void testAverageOfAFewInts() throws Throwable { - Observable src = Observable.from(1, 2, 3, 4, 6); - average(src).subscribe(w); - - verify(w, times(1)).onNext(anyInt()); - verify(w).onNext(3); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testEmptyAverage() throws Throwable { - Observable src = Observable.empty(); - average(src).subscribe(w); - - verify(w, never()).onNext(anyInt()); - verify(w, times(1)).onError(any(ArithmeticException.class)); - verify(w, never()).onCompleted(); - } - - @Test - public void testAverageOfAFewLongs() throws Throwable { - Observable src = Observable.from(1L, 2L, 3L, 4L, 6L); - averageLongs(src).subscribe(wl); - - verify(wl, times(1)).onNext(anyLong()); - verify(wl).onNext(3L); - verify(wl, never()).onError(any(Throwable.class)); - verify(wl, times(1)).onCompleted(); - } - - @Test - public void testEmptyAverageLongs() throws Throwable { - Observable src = Observable.empty(); - averageLongs(src).subscribe(wl); - - verify(wl, never()).onNext(anyLong()); - verify(wl, times(1)).onError(any(ArithmeticException.class)); - verify(wl, never()).onCompleted(); - } - - @Test - public void testAverageOfAFewFloats() throws Throwable { - Observable src = Observable.from(1.0f, 2.0f); - averageFloats(src).subscribe(wf); - - verify(wf, times(1)).onNext(anyFloat()); - verify(wf).onNext(1.5f); - verify(wf, never()).onError(any(Throwable.class)); - verify(wf, times(1)).onCompleted(); - } - - @Test - public void testEmptyAverageFloats() throws Throwable { - Observable src = Observable.empty(); - averageFloats(src).subscribe(wf); - - verify(wf, never()).onNext(anyFloat()); - verify(wf, times(1)).onError(any(ArithmeticException.class)); - verify(wf, never()).onCompleted(); - } - - @Test - public void testAverageOfAFewDoubles() throws Throwable { - Observable src = Observable.from(1.0d, 2.0d); - averageDoubles(src).subscribe(wd); - - verify(wd, times(1)).onNext(anyDouble()); - verify(wd).onNext(1.5d); - verify(wd, never()).onError(any(Throwable.class)); - verify(wd, times(1)).onCompleted(); - } - - @Test - public void testEmptyAverageDoubles() throws Throwable { - Observable src = Observable.empty(); - averageDoubles(src).subscribe(wd); - - verify(wd, never()).onNext(anyDouble()); - verify(wd, times(1)).onError(any(ArithmeticException.class)); - verify(wd, never()).onCompleted(); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationBuffer.java b/rxjava-core/src/main/java/rx/operators/OperationBuffer.java index 898260d0ef..03d2dc02b8 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationBuffer.java +++ b/rxjava-core/src/main/java/rx/operators/OperationBuffer.java @@ -15,35 +15,20 @@ */ package rx.operators; -import static org.junit.Assert.assertFalse; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mockito; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; import rx.Subscription; import rx.concurrency.Schedulers; -import rx.concurrency.TestScheduler; -import rx.subscriptions.Subscriptions; import rx.util.Closing; -import rx.util.Closings; import rx.util.Opening; -import rx.util.Openings; -import rx.util.functions.Action0; -import rx.util.functions.Action1; import rx.util.functions.Func0; import rx.util.functions.Func1; +import java.util.List; +import java.util.concurrent.TimeUnit; + public final class OperationBuffer extends ChunkedOperation { private static Func0> bufferMaker() { @@ -372,327 +357,4 @@ public List getContents() { return contents; } } - - public static class UnitTest { - - private Observer> observer; - private TestScheduler scheduler; - - @Before - @SuppressWarnings("unchecked") - public void before() { - observer = Mockito.mock(Observer.class); - scheduler = new TestScheduler(); - } - - @Test - public void testComplete() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - observer.onCompleted(); - return Subscriptions.empty(); - } - }); - - Observable> buffered = Observable.create(buffer(source, 3, 3)); - buffered.subscribe(observer); - - Mockito.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); - Mockito.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); - Mockito.verify(observer, Mockito.times(1)).onCompleted(); - } - - @Test - public void testSkipAndCountOverlappingBuffers() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - observer.onNext("one"); - observer.onNext("two"); - observer.onNext("three"); - observer.onNext("four"); - observer.onNext("five"); - return Subscriptions.empty(); - } - }); - - Observable> buffered = Observable.create(buffer(source, 3, 1)); - buffered.subscribe(observer); - - InOrder inOrder = Mockito.inOrder(observer); - inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three")); - inOrder.verify(observer, Mockito.times(1)).onNext(list("two", "three", "four")); - inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four", "five")); - inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); - inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); - inOrder.verify(observer, Mockito.never()).onCompleted(); - } - - @Test - public void testSkipAndCountGaplessBuffers() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - observer.onNext("one"); - observer.onNext("two"); - observer.onNext("three"); - observer.onNext("four"); - observer.onNext("five"); - observer.onCompleted(); - return Subscriptions.empty(); - } - }); - - Observable> buffered = Observable.create(buffer(source, 3, 3)); - buffered.subscribe(observer); - - InOrder inOrder = Mockito.inOrder(observer); - inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three")); - inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five")); - inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); - inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); - inOrder.verify(observer, Mockito.times(1)).onCompleted(); - } - - @Test - public void testSkipAndCountBuffersWithGaps() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - observer.onNext("one"); - observer.onNext("two"); - observer.onNext("three"); - observer.onNext("four"); - observer.onNext("five"); - observer.onCompleted(); - return Subscriptions.empty(); - } - }); - - Observable> buffered = Observable.create(buffer(source, 2, 3)); - buffered.subscribe(observer); - - InOrder inOrder = Mockito.inOrder(observer); - inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two")); - inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five")); - inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); - inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); - inOrder.verify(observer, Mockito.times(1)).onCompleted(); - } - - @Test - public void testTimedAndCount() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 10); - push(observer, "two", 90); - push(observer, "three", 110); - push(observer, "four", 190); - push(observer, "five", 210); - complete(observer, 250); - return Subscriptions.empty(); - } - }); - - Observable> buffered = Observable.create(buffer(source, 100, TimeUnit.MILLISECONDS, 2, scheduler)); - buffered.subscribe(observer); - - InOrder inOrder = Mockito.inOrder(observer); - scheduler.advanceTimeTo(100, TimeUnit.MILLISECONDS); - inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two")); - - scheduler.advanceTimeTo(200, TimeUnit.MILLISECONDS); - inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four")); - - scheduler.advanceTimeTo(300, TimeUnit.MILLISECONDS); - inOrder.verify(observer, Mockito.times(1)).onNext(list("five")); - inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); - inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); - inOrder.verify(observer, Mockito.times(1)).onCompleted(); - } - - @Test - public void testTimed() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 98); - push(observer, "two", 99); - push(observer, "three", 100); - push(observer, "four", 101); - push(observer, "five", 102); - complete(observer, 150); - return Subscriptions.empty(); - } - }); - - Observable> buffered = Observable.create(buffer(source, 100, TimeUnit.MILLISECONDS, scheduler)); - buffered.subscribe(observer); - - InOrder inOrder = Mockito.inOrder(observer); - scheduler.advanceTimeTo(101, TimeUnit.MILLISECONDS); - inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three")); - - scheduler.advanceTimeTo(201, TimeUnit.MILLISECONDS); - inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five")); - inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); - inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); - inOrder.verify(observer, Mockito.times(1)).onCompleted(); - } - - @Test - public void testObservableBasedOpenerAndCloser() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 10); - push(observer, "two", 60); - push(observer, "three", 110); - push(observer, "four", 160); - push(observer, "five", 210); - complete(observer, 500); - return Subscriptions.empty(); - } - }); - - Observable openings = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, Openings.create(), 50); - push(observer, Openings.create(), 200); - complete(observer, 250); - return Subscriptions.empty(); - } - }); - - Func1> closer = new Func1>() { - @Override - public Observable call(Opening opening) { - return Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, Closings.create(), 100); - complete(observer, 101); - return Subscriptions.empty(); - } - }); - } - }; - - Observable> buffered = Observable.create(buffer(source, openings, closer)); - buffered.subscribe(observer); - - InOrder inOrder = Mockito.inOrder(observer); - scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); - inOrder.verify(observer, Mockito.times(1)).onNext(list("two", "three")); - inOrder.verify(observer, Mockito.times(1)).onNext(list("five")); - inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); - inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); - inOrder.verify(observer, Mockito.times(1)).onCompleted(); - } - - @Test - public void testObservableBasedCloser() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 10); - push(observer, "two", 60); - push(observer, "three", 110); - push(observer, "four", 160); - push(observer, "five", 210); - complete(observer, 250); - return Subscriptions.empty(); - } - }); - - Func0> closer = new Func0>() { - @Override - public Observable call() { - return Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, Closings.create(), 100); - complete(observer, 101); - return Subscriptions.empty(); - } - }); - } - }; - - Observable> buffered = Observable.create(buffer(source, closer)); - buffered.subscribe(observer); - - InOrder inOrder = Mockito.inOrder(observer); - scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); - inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two")); - inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four")); - inOrder.verify(observer, Mockito.times(1)).onNext(list("five")); - inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); - inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); - inOrder.verify(observer, Mockito.times(1)).onCompleted(); - } - - @Test - public void testLongTimeAction() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - LongTimeAction action = new LongTimeAction(latch); - Observable.from(1).buffer(10, TimeUnit.MILLISECONDS, 10) - .subscribe(action); - latch.await(); - assertFalse(action.fail); - } - - private static class LongTimeAction implements Action1> { - - CountDownLatch latch; - boolean fail = false; - - public LongTimeAction(CountDownLatch latch) { - this.latch = latch; - } - - @Override - public void call(List t1) { - try { - if (fail) { - return; - } - Thread.sleep(200); - } catch (InterruptedException e) { - fail = true; - } finally { - latch.countDown(); - } - } - } - - private List list(String... args) { - List list = new ArrayList(); - for (String arg : args) { - list.add(arg); - } - return list; - } - - private void push(final Observer observer, final T value, int delay) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onNext(value); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void complete(final Observer observer, int delay) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onCompleted(); - } - }, delay, TimeUnit.MILLISECONDS); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationCache.java b/rxjava-core/src/main/java/rx/operators/OperationCache.java index e5f8ce69e3..44749bbd15 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationCache.java +++ b/rxjava-core/src/main/java/rx/operators/OperationCache.java @@ -15,22 +15,13 @@ */ package rx.operators; -import static org.junit.Assert.*; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.subjects.ReplaySubject; -import rx.subscriptions.BooleanSubscription; -import rx.util.functions.Action1; + +import java.util.concurrent.atomic.AtomicBoolean; /** * This method has similar behavior to {@link Observable#replay()} except that this auto-subscribes @@ -70,61 +61,4 @@ public Subscription onSubscribe(Observer observer) { }; } - - public static class UnitTest { - - @Test - public void testCache() throws InterruptedException { - final AtomicInteger counter = new AtomicInteger(); - Observable o = Observable.create(cache(Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(final Observer observer) { - final BooleanSubscription subscription = new BooleanSubscription(); - new Thread(new Runnable() { - - @Override - public void run() { - counter.incrementAndGet(); - System.out.println("published observable being executed"); - observer.onNext("one"); - observer.onCompleted(); - } - }).start(); - return subscription; - } - }))); - - // we then expect the following 2 subscriptions to get that same value - final CountDownLatch latch = new CountDownLatch(2); - - // subscribe once - o.subscribe(new Action1() { - - @Override - public void call(String v) { - assertEquals("one", v); - System.out.println("v: " + v); - latch.countDown(); - } - }); - - // subscribe again - o.subscribe(new Action1() { - - @Override - public void call(String v) { - assertEquals("one", v); - System.out.println("v: " + v); - latch.countDown(); - } - }); - - if (!latch.await(1000, TimeUnit.MILLISECONDS)) { - fail("subscriptions did not receive values"); - } - assertEquals(1, counter.get()); - } - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationCast.java b/rxjava-core/src/main/java/rx/operators/OperationCast.java index a4887c3afe..0643624a02 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationCast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationCast.java @@ -1,15 +1,7 @@ package rx.operators; -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 org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; -import rx.Observer; import rx.util.functions.Func1; /** @@ -25,37 +17,4 @@ public R call(T t) { } }); } - - public static class UnitTest { - - @Test - public void testCast() { - Observable source = Observable.from(1, 2); - Observable observable = Observable.create(cast(source, - Integer.class)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(1); - verify(aObserver, times(1)).onNext(1); - verify(aObserver, never()).onError( - org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testCastWithWrongType() { - Observable source = Observable.from(1, 2); - Observable observable = Observable.create(cast(source, - Boolean.class)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onError( - org.mockito.Matchers.any(ClassCastException.class)); - } - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java b/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java index 020254d8a0..b87aaea0ba 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java +++ b/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java @@ -15,10 +15,12 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.*; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -26,26 +28,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Matchers; - -import rx.Observable; -import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscription; -import rx.subscriptions.Subscriptions; -import rx.util.functions.Func2; -import rx.util.functions.Func3; -import rx.util.functions.Func4; -import rx.util.functions.Func5; -import rx.util.functions.Func6; -import rx.util.functions.Func7; -import rx.util.functions.Func8; -import rx.util.functions.Func9; -import rx.util.functions.FuncN; -import rx.util.functions.Functions; - /** * Returns an Observable that combines the emissions of multiple source observables. Once each * source Observable has emitted at least one item, combineLatest emits an item whenever any of @@ -179,7 +161,7 @@ public static OnSubscribeFunc combine return a; } - private static class CombineObserver implements Observer { + static class CombineObserver implements Observer { final Observable w; final Aggregator a; private Subscription subscription; @@ -217,7 +199,7 @@ public void onNext(T args) { * whenever we have received an event from one of the observables, as soon as each Observable has received * at least one event. */ - private static class Aggregator implements OnSubscribeFunc { + static class Aggregator implements OnSubscribeFunc { private volatile Observer observer; @@ -351,603 +333,4 @@ private void stop() { } } } - - public static class UnitTest { - - @Test - public void testCombineLatestWithFunctionThatThrowsAnException() { - @SuppressWarnings("unchecked") // mock calls don't do generics - Observer w = mock(Observer.class); - - TestObservable w1 = new TestObservable(); - TestObservable w2 = new TestObservable(); - - Observable combined = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), new Func2() { - @Override - public String call(String v1, String v2) { - throw new RuntimeException("I don't work."); - } - })); - combined.subscribe(w); - - w1.observer.onNext("first value of w1"); - w2.observer.onNext("first value of w2"); - - verify(w, never()).onNext(anyString()); - verify(w, never()).onCompleted(); - verify(w, times(1)).onError(Matchers.any()); - } - - @Test - public void testCombineLatestDifferentLengthObservableSequences1() { - @SuppressWarnings("unchecked") // mock calls don't do generics - Observer w = mock(Observer.class); - - TestObservable w1 = new TestObservable(); - TestObservable w2 = new TestObservable(); - TestObservable w3 = new TestObservable(); - - Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction())); - combineLatestW.subscribe(w); - - /* simulate sending data */ - // once for w1 - w1.observer.onNext("1a"); - w2.observer.onNext("2a"); - w3.observer.onNext("3a"); - w1.observer.onCompleted(); - // twice for w2 - w2.observer.onNext("2b"); - w2.observer.onCompleted(); - // 4 times for w3 - w3.observer.onNext("3b"); - w3.observer.onNext("3c"); - w3.observer.onNext("3d"); - w3.observer.onCompleted(); - - /* we should have been called 4 times on the Observer */ - InOrder inOrder = inOrder(w); - inOrder.verify(w).onNext("1a2a3a"); - inOrder.verify(w).onNext("1a2b3a"); - inOrder.verify(w).onNext("1a2b3b"); - inOrder.verify(w).onNext("1a2b3c"); - inOrder.verify(w).onNext("1a2b3d"); - inOrder.verify(w, never()).onNext(anyString()); - inOrder.verify(w, times(1)).onCompleted(); - } - - @Test - public void testCombineLatestDifferentLengthObservableSequences2() { - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - - TestObservable w1 = new TestObservable(); - TestObservable w2 = new TestObservable(); - TestObservable w3 = new TestObservable(); - - Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction())); - combineLatestW.subscribe(w); - - /* simulate sending data */ - // 4 times for w1 - w1.observer.onNext("1a"); - w1.observer.onNext("1b"); - w1.observer.onNext("1c"); - w1.observer.onNext("1d"); - w1.observer.onCompleted(); - // twice for w2 - w2.observer.onNext("2a"); - w2.observer.onNext("2b"); - w2.observer.onCompleted(); - // 1 times for w3 - w3.observer.onNext("3a"); - w3.observer.onCompleted(); - - /* we should have been called 1 time only on the Observer since we only combine the "latest" we don't go back and loop through others once completed */ - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("1d2b3a"); - inOrder.verify(w, never()).onNext(anyString()); - - inOrder.verify(w, times(1)).onCompleted(); - - } - - @Test - public void testCombineLatestWithInterleavingSequences() { - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - - TestObservable w1 = new TestObservable(); - TestObservable w2 = new TestObservable(); - TestObservable w3 = new TestObservable(); - - Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction())); - combineLatestW.subscribe(w); - - /* simulate sending data */ - w1.observer.onNext("1a"); - w2.observer.onNext("2a"); - w2.observer.onNext("2b"); - w3.observer.onNext("3a"); - - w1.observer.onNext("1b"); - w2.observer.onNext("2c"); - w2.observer.onNext("2d"); - w3.observer.onNext("3b"); - - w1.observer.onCompleted(); - w2.observer.onCompleted(); - w3.observer.onCompleted(); - - /* we should have been called 5 times on the Observer */ - InOrder inOrder = inOrder(w); - inOrder.verify(w).onNext("1a2b3a"); - inOrder.verify(w).onNext("1b2b3a"); - inOrder.verify(w).onNext("1b2c3a"); - inOrder.verify(w).onNext("1b2d3a"); - inOrder.verify(w).onNext("1b2d3b"); - - inOrder.verify(w, never()).onNext(anyString()); - inOrder.verify(w, times(1)).onCompleted(); - } - - /** - * Testing internal private logic due to the complexity so I want to use TDD to test as a I build it rather than relying purely on the overall functionality expected by the public methods. - */ - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorSimple() { - FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all Observables provide values */ - Aggregator a = new Aggregator(combineLatestFunction); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - Observable.create(a).subscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - CombineObserver r1 = mock(CombineObserver.class); - CombineObserver r2 = mock(CombineObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - - InOrder inOrder = inOrder(aObserver); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("helloworld"); - - a.next(r1, "hello "); - a.next(r2, "again"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("hello again"); - - a.complete(r1); - a.complete(r2); - - inOrder.verify(aObserver, never()).onNext(anyString()); - verify(aObserver, times(1)).onCompleted(); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorDifferentSizedResultsWithOnComplete() { - FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all Observables provide values */ - Aggregator a = new Aggregator(combineLatestFunction); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - Observable.create(a).subscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - CombineObserver r1 = mock(CombineObserver.class); - CombineObserver r2 = mock(CombineObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - a.complete(r2); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("helloworld"); - - a.next(r1, "hi"); - a.complete(r1); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("hiworld"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregateMultipleTypes() { - FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all Observables provide values */ - Aggregator a = new Aggregator(combineLatestFunction); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - Observable.create(a).subscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - CombineObserver r1 = mock(CombineObserver.class); - CombineObserver r2 = mock(CombineObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - a.complete(r2); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("helloworld"); - - a.next(r1, "hi"); - a.complete(r1); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("hiworld"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregate3Types() { - FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all Observables provide values */ - Aggregator a = new Aggregator(combineLatestFunction); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - Observable.create(a).subscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - CombineObserver r1 = mock(CombineObserver.class); - CombineObserver r2 = mock(CombineObserver.class); - CombineObserver r3 = mock(CombineObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - a.addObserver(r3); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, 2); - a.next(r3, new int[] { 5, 6, 7 }); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("hello2[5, 6, 7]"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorsWithDifferentSizesAndTiming() { - FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all Observables provide values */ - Aggregator a = new Aggregator(combineLatestFunction); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - Observable.create(a).subscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - CombineObserver r1 = mock(CombineObserver.class); - CombineObserver r2 = mock(CombineObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "one"); - a.next(r1, "two"); - a.next(r1, "three"); - a.next(r2, "A"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("threeA"); - - a.next(r1, "four"); - a.complete(r1); - a.next(r2, "B"); - verify(aObserver, times(1)).onNext("fourB"); - a.next(r2, "C"); - verify(aObserver, times(1)).onNext("fourC"); - a.next(r2, "D"); - verify(aObserver, times(1)).onNext("fourD"); - a.next(r2, "E"); - verify(aObserver, times(1)).onNext("fourE"); - a.complete(r2); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorError() { - FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all Observables provide values */ - Aggregator a = new Aggregator(combineLatestFunction); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - Observable.create(a).subscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - CombineObserver r1 = mock(CombineObserver.class); - CombineObserver r2 = mock(CombineObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("helloworld"); - - a.error(new RuntimeException("")); - a.next(r1, "hello"); - a.next(r2, "again"); - - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - // we don't want to be called again after an error - verify(aObserver, times(0)).onNext("helloagain"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorUnsubscribe() { - FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all Observables provide values */ - Aggregator a = new Aggregator(combineLatestFunction); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - Subscription subscription = Observable.create(a).subscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - CombineObserver r1 = mock(CombineObserver.class); - CombineObserver r2 = mock(CombineObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("helloworld"); - - subscription.unsubscribe(); - a.next(r1, "hello"); - a.next(r2, "again"); - - verify(aObserver, times(0)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - // we don't want to be called again after an error - verify(aObserver, times(0)).onNext("helloagain"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorEarlyCompletion() { - FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all Observables provide values */ - Aggregator a = new Aggregator(combineLatestFunction); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - Observable.create(a).subscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - CombineObserver r1 = mock(CombineObserver.class); - CombineObserver r2 = mock(CombineObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "one"); - a.next(r1, "two"); - a.complete(r1); - a.next(r2, "A"); - - InOrder inOrder = inOrder(aObserver); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("twoA"); - - a.complete(r2); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, times(1)).onCompleted(); - // we shouldn't get this since completed is called before any other onNext calls could trigger this - inOrder.verify(aObserver, never()).onNext(anyString()); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testCombineLatest2Types() { - Func2 combineLatestFunction = getConcatStringIntegerCombineLatestFunction(); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - - Observable w = Observable.create(combineLatest(Observable.from("one", "two"), Observable.from(2, 3, 4), combineLatestFunction)); - w.subscribe(aObserver); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("two2"); - verify(aObserver, times(1)).onNext("two3"); - verify(aObserver, times(1)).onNext("two4"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testCombineLatest3TypesA() { - Func3 combineLatestFunction = getConcatStringIntegerIntArrayCombineLatestFunction(); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - - Observable w = Observable.create(combineLatest(Observable.from("one", "two"), Observable.from(2), Observable.from(new int[] { 4, 5, 6 }), combineLatestFunction)); - w.subscribe(aObserver); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("two2[4, 5, 6]"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testCombineLatest3TypesB() { - Func3 combineLatestFunction = getConcatStringIntegerIntArrayCombineLatestFunction(); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - - Observable w = Observable.create(combineLatest(Observable.from("one"), Observable.from(2), Observable.from(new int[] { 4, 5, 6 }, new int[] { 7, 8 }), combineLatestFunction)); - w.subscribe(aObserver); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one2[4, 5, 6]"); - verify(aObserver, times(1)).onNext("one2[7, 8]"); - } - - private Func3 getConcat3StringsCombineLatestFunction() { - Func3 combineLatestFunction = new Func3() { - - @Override - public String call(String a1, String a2, String a3) { - if (a1 == null) { - a1 = ""; - } - if (a2 == null) { - a2 = ""; - } - if (a3 == null) { - a3 = ""; - } - return a1 + a2 + a3; - } - - }; - return combineLatestFunction; - } - - private FuncN getConcatCombineLatestFunction() { - FuncN combineLatestFunction = new FuncN() { - - @Override - public String call(Object... args) { - String returnValue = ""; - for (Object o : args) { - if (o != null) { - returnValue += getStringValue(o); - } - } - System.out.println("returning: " + returnValue); - return returnValue; - } - - }; - return combineLatestFunction; - } - - private Func2 getConcatStringIntegerCombineLatestFunction() { - Func2 combineLatestFunction = new Func2() { - - @Override - public String call(String s, Integer i) { - return getStringValue(s) + getStringValue(i); - } - - }; - return combineLatestFunction; - } - - private Func3 getConcatStringIntegerIntArrayCombineLatestFunction() { - Func3 combineLatestFunction = new Func3() { - - @Override - public String call(String s, Integer i, int[] iArray) { - return getStringValue(s) + getStringValue(i) + getStringValue(iArray); - } - - }; - return combineLatestFunction; - } - - private static String getStringValue(Object o) { - if (o == null) { - return ""; - } else { - if (o instanceof int[]) { - return Arrays.toString((int[]) o); - } else { - return String.valueOf(o); - } - } - } - - private static class TestObservable implements OnSubscribeFunc { - - Observer observer; - - @Override - public Subscription onSubscribe(Observer observer) { - // just store the variable where it can be accessed so we can manually trigger it - this.observer = observer; - return Subscriptions.empty(); - } - - } - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationConcat.java b/rxjava-core/src/main/java/rx/operators/OperationConcat.java index 734751e9d0..4488ddf177 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationConcat.java +++ b/rxjava-core/src/main/java/rx/operators/OperationConcat.java @@ -15,28 +15,14 @@ */ package rx.operators; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.subscriptions.BooleanSubscription; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; /** * Returns an Observable that emits the items emitted by two or more Observables, one after the @@ -166,529 +152,4 @@ public void unsubscribe() { }; } } - - public static class UnitTest { - - @Test - public void testConcat() { - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - - final String[] o = { "1", "3", "5", "7" }; - final String[] e = { "2", "4", "6" }; - - final Observable odds = Observable.from(o); - final Observable even = Observable.from(e); - - @SuppressWarnings("unchecked") - Observable concat = Observable.create(concat(odds, even)); - concat.subscribe(observer); - - verify(observer, times(7)).onNext(anyString()); - } - - @Test - public void testConcatWithList() { - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - - final String[] o = { "1", "3", "5", "7" }; - final String[] e = { "2", "4", "6" }; - - final Observable odds = Observable.from(o); - final Observable even = Observable.from(e); - final List> list = new ArrayList>(); - list.add(odds); - list.add(even); - Observable concat = Observable.create(concat(list)); - concat.subscribe(observer); - - verify(observer, times(7)).onNext(anyString()); - } - - @Test - public void testConcatObservableOfObservables() { - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - - final String[] o = { "1", "3", "5", "7" }; - final String[] e = { "2", "4", "6" }; - - final Observable odds = Observable.from(o); - final Observable even = Observable.from(e); - - Observable> observableOfObservables = Observable.create(new OnSubscribeFunc>() { - - @Override - public Subscription onSubscribe(Observer> observer) { - // simulate what would happen in an observable - observer.onNext(odds); - observer.onNext(even); - observer.onCompleted(); - - return new Subscription() { - - @Override - public void unsubscribe() { - // unregister ... will never be called here since we are executing synchronously - } - - }; - } - - }); - Observable concat = Observable.create(concat(observableOfObservables)); - - concat.subscribe(observer); - - verify(observer, times(7)).onNext(anyString()); - } - - /** - * Simple concat of 2 asynchronous observables ensuring it emits in correct order. - */ - @SuppressWarnings("unchecked") - @Test - public void testSimpleAsyncConcat() { - Observer observer = mock(Observer.class); - - TestObservable o1 = new TestObservable("one", "two", "three"); - TestObservable o2 = new TestObservable("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 an async Observable that emits more async Observables - */ - @SuppressWarnings("unchecked") - @Test - public void testNestedAsyncConcat() throws Throwable { - Observer observer = mock(Observer.class); - - final TestObservable o1 = new TestObservable("one", "two", "three"); - final TestObservable o2 = new TestObservable("four", "five", "six"); - final TestObservable o3 = new TestObservable("seven", "eight", "nine"); - final CountDownLatch allowThird = new CountDownLatch(1); - - final AtomicReference parent = new AtomicReference(); - Observable> observableOfObservables = Observable.create(new OnSubscribeFunc>() { - - @Override - public Subscription onSubscribe(final Observer> 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(); - return s; - } - }); - - Observable.create(concat(observableOfObservables)).subscribe(observer); - - // wait for parent to start - while (parent.get() == null) { - Thread.sleep(1); - } - - try { - // wait for first 2 async observables to complete - while (o1.t == null) { - Thread.sleep(1); - } - System.out.println("Thread1 started ... waiting for it to complete ..."); - o1.t.join(); - while (o2.t == null) { - Thread.sleep(1); - } - System.out.println("Thread2 started ... waiting for it to complete ..."); - o2.t.join(); - } 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 { - while (o3.t == null) { - Thread.sleep(1); - } - // wait for 3rd to complete - o3.t.join(); - } 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)); - } - - @SuppressWarnings("unchecked") - @Test - public void testBlockedObservableOfObservables() { - Observer observer = mock(Observer.class); - - final String[] o = { "1", "3", "5", "7" }; - final String[] e = { "2", "4", "6" }; - final Observable odds = Observable.from(o); - final Observable even = Observable.from(e); - final CountDownLatch callOnce = new CountDownLatch(1); - final CountDownLatch okToContinue = new CountDownLatch(1); - TestObservable> observableOfObservables = new TestObservable>(callOnce, okToContinue, odds, even); - OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables)); - Observable concat = Observable.create(concatF); - concat.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 testConcatConcurrentWithInfinity() { - final TestObservable w1 = new TestObservable("one", "two", "three"); - //This observable will send "hello" MAX_VALUE time. - final TestObservable w2 = new TestObservable("hello", Integer.MAX_VALUE); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - @SuppressWarnings("unchecked") - TestObservable> observableOfObservables = new TestObservable>(Observable.create(w1), Observable.create(w2)); - OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables)); - - Observable concat = Observable.create(concatF); - - concat.take(50).subscribe(aObserver); - - //Wait for the thread to start up. - try { - Thread.sleep(25); - w1.t.join(); - w2.t.join(); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - InOrder inOrder = inOrder(aObserver); - inOrder.verify(aObserver, times(1)).onNext("one"); - inOrder.verify(aObserver, times(1)).onNext("two"); - inOrder.verify(aObserver, times(1)).onNext("three"); - inOrder.verify(aObserver, times(47)).onNext("hello"); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, never()).onError(any(Throwable.class)); - } - - @Test - public void testConcatNonBlockingObservables() { - - final CountDownLatch okToContinueW1 = new CountDownLatch(1); - final CountDownLatch okToContinueW2 = new CountDownLatch(1); - - final TestObservable w1 = new TestObservable(null, okToContinueW1, "one", "two", "three"); - final TestObservable w2 = new TestObservable(null, okToContinueW2, "four", "five", "six"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - Observable> observableOfObservables = Observable.create(new OnSubscribeFunc>() { - - @Override - public Subscription onSubscribe(Observer> observer) { - // simulate what would happen in an observable - observer.onNext(Observable.create(w1)); - observer.onNext(Observable.create(w2)); - observer.onCompleted(); - - return new Subscription() { - - @Override - public void unsubscribe() { - } - - }; - } - - }); - Observable concat = Observable.create(concat(observableOfObservables)); - concat.subscribe(aObserver); - - verify(aObserver, 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) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - InOrder inOrder = inOrder(aObserver); - inOrder.verify(aObserver, times(1)).onNext("one"); - inOrder.verify(aObserver, times(1)).onNext("two"); - inOrder.verify(aObserver, times(1)).onNext("three"); - inOrder.verify(aObserver, times(1)).onNext("four"); - inOrder.verify(aObserver, times(1)).onNext("five"); - inOrder.verify(aObserver, times(1)).onNext("six"); - verify(aObserver, times(1)).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 w1 = new TestObservable("one", "two", "three"); - final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six"); - - @SuppressWarnings("unchecked") - final Observer aObserver = mock(Observer.class); - @SuppressWarnings("unchecked") - final Observable concat = Observable.create(concat(Observable.create(w1), Observable.create(w2))); - final SafeObservableSubscription s1 = new SafeObservableSubscription(); - - try { - // Subscribe - s1.wrap(concat.subscribe(aObserver)); - //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(aObserver); - inOrder.verify(aObserver, times(1)).onNext("one"); - inOrder.verify(aObserver, times(1)).onNext("two"); - inOrder.verify(aObserver, times(1)).onNext("three"); - inOrder.verify(aObserver, times(1)).onNext("four"); - inOrder.verify(aObserver, never()).onNext("five"); - inOrder.verify(aObserver, never()).onNext("six"); - inOrder.verify(aObserver, never()).onCompleted(); - - } - - /** - * 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 w1 = new TestObservable("one", "two", "three"); - final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - @SuppressWarnings("unchecked") - TestObservable> observableOfObservables = new TestObservable>(Observable.create(w1), Observable.create(w2)); - OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables)); - - Observable concat = Observable.create(concatF); - - Subscription s1 = concat.subscribe(aObserver); - - 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(aObserver); - inOrder.verify(aObserver, times(1)).onNext("one"); - inOrder.verify(aObserver, times(1)).onNext("two"); - inOrder.verify(aObserver, times(1)).onNext("three"); - inOrder.verify(aObserver, times(1)).onNext("four"); - inOrder.verify(aObserver, never()).onNext("five"); - inOrder.verify(aObserver, never()).onNext("six"); - verify(aObserver, never()).onCompleted(); - verify(aObserver, never()).onError(any(Throwable.class)); - } - - private static class TestObservable implements OnSubscribeFunc { - - private final Subscription s = new Subscription() { - - @Override - public void unsubscribe() { - subscribed = false; - } - - }; - private final List values; - private Thread t = null; - private int count = 0; - private boolean subscribed = true; - private final CountDownLatch once; - private final CountDownLatch okToContinue; - private final T seed; - private final int size; - - public TestObservable(T... values) { - this(null, null, values); - } - - public TestObservable(CountDownLatch once, CountDownLatch okToContinue, T... values) { - this.values = Arrays.asList(values); - this.size = this.values.size(); - this.once = once; - this.okToContinue = okToContinue; - this.seed = null; - } - - public TestObservable(T seed, int size) { - values = null; - once = null; - okToContinue = null; - this.seed = seed; - this.size = size; - } - - @Override - public Subscription onSubscribe(final Observer observer) { - t = new Thread(new Runnable() { - - @Override - public void run() { - try { - while (count < size && subscribed) { - if (null != values) - observer.onNext(values.get(count)); - else - observer.onNext(seed); - count++; - //Unblock the main thread to call unsubscribe. - if (null != once) - once.countDown(); - //Block until the main thread has called unsubscribe. - if (null != okToContinue) - okToContinue.await(5, TimeUnit.SECONDS); - } - if (subscribed) - observer.onCompleted(); - } catch (InterruptedException e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - }); - t.start(); - return s; - } - - } - - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationDebounce.java b/rxjava-core/src/main/java/rx/operators/OperationDebounce.java index d225477069..ebbcbc8ad1 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDebounce.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDebounce.java @@ -15,27 +15,18 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; import rx.Subscription; import rx.concurrency.Schedulers; -import rx.concurrency.TestScheduler; -import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; import rx.util.functions.Func1; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + /** * This operation is used to filter out bursts of events. This is done by ignoring the events from an observable which are too * quickly followed up with other values. Values which are not followed up by other values within the specified timeout are published @@ -160,135 +151,4 @@ public void call() { } } } - - public static class UnitTest { - - private TestScheduler scheduler; - private Observer observer; - - @Before - @SuppressWarnings("unchecked") - public void before() { - scheduler = new TestScheduler(); - observer = mock(Observer.class); - } - - @Test - public void testDebounceWithCompleted() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 100, "one"); // Should be skipped since "two" will arrive before the timeout expires. - publishNext(observer, 400, "two"); // Should be published since "three" will arrive after the timeout expires. - publishNext(observer, 900, "three"); // Should be skipped since onCompleted will arrive before the timeout expires. - publishCompleted(observer, 1000); // Should be published as soon as the timeout expires. - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationDebounce.debounce(source, 400, TimeUnit.MILLISECONDS, scheduler)); - sampled.subscribe(observer); - - scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); - InOrder inOrder = inOrder(observer); - // must go to 800 since it must be 400 after when two is sent, which is at 400 - scheduler.advanceTimeTo(800, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("two"); - scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testDebounceNeverEmits() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - // all should be skipped since they are happening faster than the 200ms timeout - publishNext(observer, 100, "a"); // Should be skipped - publishNext(observer, 200, "b"); // Should be skipped - publishNext(observer, 300, "c"); // Should be skipped - publishNext(observer, 400, "d"); // Should be skipped - publishNext(observer, 500, "e"); // Should be skipped - publishNext(observer, 600, "f"); // Should be skipped - publishNext(observer, 700, "g"); // Should be skipped - publishNext(observer, 800, "h"); // Should be skipped - publishCompleted(observer, 900); // Should be published as soon as the timeout expires. - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationDebounce.debounce(source, 200, TimeUnit.MILLISECONDS, scheduler)); - sampled.subscribe(observer); - - scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(0)).onNext(anyString()); - scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testDebounceWithError() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - Exception error = new TestException(); - publishNext(observer, 100, "one"); // Should be published since "two" will arrive after the timeout expires. - publishNext(observer, 600, "two"); // Should be skipped since onError will arrive before the timeout expires. - publishError(observer, 700, error); // Should be published as soon as the timeout expires. - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationDebounce.debounce(source, 400, TimeUnit.MILLISECONDS, scheduler)); - sampled.subscribe(observer); - - scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); - InOrder inOrder = inOrder(observer); - // 100 + 400 means it triggers at 500 - scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); - inOrder.verify(observer).onNext("one"); - scheduler.advanceTimeTo(701, TimeUnit.MILLISECONDS); - inOrder.verify(observer).onError(any(TestException.class)); - inOrder.verifyNoMoreInteractions(); - } - - private void publishCompleted(final Observer observer, long delay) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onCompleted(); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void publishError(final Observer observer, long delay, final Exception error) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onError(error); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void publishNext(final Observer observer, final long delay, final T value) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onNext(value); - } - }, delay, TimeUnit.MILLISECONDS); - } - - @SuppressWarnings("serial") - private class TestException extends Exception { - } - - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java b/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java index 195422dadc..cfe7f27a2f 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java @@ -1,12 +1,5 @@ package rx.operators; -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 org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -82,41 +75,4 @@ public void onCompleted() { })); } } - - public static class UnitTest { - - @Test - public void testDefaultIfEmpty() { - Observable source = Observable.from(1, 2, 3); - Observable observable = Observable.create(defaultIfEmpty( - source, 10)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(10); - verify(aObserver, times(1)).onNext(1); - verify(aObserver, times(1)).onNext(2); - verify(aObserver, times(1)).onNext(3); - verify(aObserver, never()).onError( - org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testDefaultIfEmptyWithEmpty() { - Observable source = Observable.empty(); - Observable observable = Observable.create(defaultIfEmpty( - source, 10)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(10); - verify(aObserver, never()).onError( - org.mockito.Matchers.any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationDefer.java b/rxjava-core/src/main/java/rx/operators/OperationDefer.java index 4be1ff01df..0d326b364e 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDefer.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDefer.java @@ -15,10 +15,6 @@ */ package rx.operators; -import static org.mockito.Mockito.*; - -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -48,43 +44,4 @@ public Subscription onSubscribe(Observer observer) { }; } - - public static class UnitTest { - @Test - @SuppressWarnings("unchecked") - public void testDefer() throws Throwable { - - Func0> factory = mock(Func0.class); - - Observable firstObservable = Observable.from("one", "two"); - Observable secondObservable = Observable.from("three", "four"); - when(factory.call()).thenReturn(firstObservable, secondObservable); - - Observable deferred = Observable.defer(factory); - - verifyZeroInteractions(factory); - - Observer firstObserver = mock(Observer.class); - deferred.subscribe(firstObserver); - - verify(factory, times(1)).call(); - verify(firstObserver, times(1)).onNext("one"); - verify(firstObserver, times(1)).onNext("two"); - verify(firstObserver, times(0)).onNext("three"); - verify(firstObserver, times(0)).onNext("four"); - verify(firstObserver, times(1)).onCompleted(); - - Observer secondObserver = mock(Observer.class); - deferred.subscribe(secondObserver); - - verify(factory, times(2)).call(); - verify(secondObserver, times(0)).onNext("one"); - verify(secondObserver, times(0)).onNext("two"); - verify(secondObserver, times(1)).onNext("three"); - verify(secondObserver, times(1)).onNext("four"); - verify(secondObserver, times(1)).onCompleted(); - - } - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationDematerialize.java b/rxjava-core/src/main/java/rx/operators/OperationDematerialize.java index 529c507b14..8e958cbc14 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDematerialize.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDematerialize.java @@ -15,11 +15,6 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import org.junit.Test; - import rx.Notification; import rx.Observable; import rx.Observable.OnSubscribeFunc; @@ -85,52 +80,4 @@ public void onNext(Notification value) { }); } } - - public static class UnitTest { - - @Test - @SuppressWarnings("unchecked") - public void testDematerialize1() { - Observable> notifications = Observable.from(1, 2).materialize(); - Observable dematerialize = notifications.dematerialize(); - - Observer aObserver = mock(Observer.class); - dematerialize.subscribe(aObserver); - - verify(aObserver, times(1)).onNext(1); - verify(aObserver, times(1)).onNext(2); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, never()).onError(any(Throwable.class)); - } - - @Test - @SuppressWarnings("unchecked") - public void testDematerialize2() { - Throwable exception = new Throwable("test"); - Observable observable = Observable.error(exception); - Observable dematerialize = Observable.create(dematerialize(observable.materialize())); - - Observer aObserver = mock(Observer.class); - dematerialize.subscribe(aObserver); - - verify(aObserver, times(1)).onError(exception); - verify(aObserver, times(0)).onCompleted(); - verify(aObserver, times(0)).onNext(any(Integer.class)); - } - - @Test - @SuppressWarnings("unchecked") - public void testDematerialize3() { - Exception exception = new Exception("test"); - Observable observable = Observable.error(exception); - Observable dematerialize = Observable.create(dematerialize(observable.materialize())); - - Observer aObserver = mock(Observer.class); - dematerialize.subscribe(aObserver); - - verify(aObserver, times(1)).onError(exception); - verify(aObserver, times(0)).onCompleted(); - verify(aObserver, times(0)).onNext(any(Integer.class)); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationDistinct.java b/rxjava-core/src/main/java/rx/operators/OperationDistinct.java index b56f0cd22f..9c0bccce2c 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDistinct.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDistinct.java @@ -15,24 +15,6 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.MockitoAnnotations.initMocks; -import static rx.Observable.create; -import static rx.Observable.empty; -import static rx.Observable.from; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mock; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -42,6 +24,8 @@ import rx.util.functions.Func1; import rx.util.functions.Functions; +import java.util.*; + /** * Returns an Observable that emits all distinct items emitted by the source. * @@ -192,166 +176,4 @@ public void call() { }); } } - - public static class UnitTest { - @Mock - Observer w; - @Mock - Observer w2; - - // nulls lead to exceptions - final Func1 TO_UPPER_WITH_EXCEPTION = new Func1() { - @Override - public String call(String s) { - if (s.equals("x")) { - return "XX"; - } - return s.toUpperCase(); - } - }; - - final Comparator COMPARE_LENGTH = new Comparator() { - @Override - public int compare(String s1, String s2) { - return s1.length() - s2.length(); - } - }; - - @Before - public void before() { - initMocks(this); - } - - @Test - public void testDistinctOfNone() { - Observable src = empty(); - create(distinct(src)).subscribe(w); - - verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testDistinctOfNoneWithKeySelector() { - Observable src = empty(); - create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); - - verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testDistinctOfNormalSource() { - Observable src = from("a", "b", "c", "c", "c", "b", "b", "a", "e"); - create(distinct(src)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("b"); - inOrder.verify(w, times(1)).onNext("c"); - inOrder.verify(w, times(1)).onNext("e"); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testDistinctOfNormalSourceWithKeySelector() { - Observable src = from("a", "B", "c", "C", "c", "B", "b", "a", "E"); - create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("B"); - inOrder.verify(w, times(1)).onNext("c"); - inOrder.verify(w, times(1)).onNext("E"); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testDistinctOfNormalSourceWithComparator() { - Observable src = from("1", "12", "123", "aaa", "321", "12", "21", "1", "12345"); - create(distinct(src, COMPARE_LENGTH)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("1"); - inOrder.verify(w, times(1)).onNext("12"); - inOrder.verify(w, times(1)).onNext("123"); - inOrder.verify(w, times(1)).onNext("12345"); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testDistinctOfNormalSourceWithKeySelectorAndComparator() { - Observable src = from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd"); - create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("x"); - inOrder.verify(w, times(1)).onNext("abc"); - inOrder.verify(w, times(1)).onNext("abcd"); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testDistinctOfNormalSourceWithKeySelectorAndComparatorAndTwoSubscriptions() { - Observable src = from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd"); - create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("x"); - create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2); - inOrder.verify(w, times(1)).onNext("abc"); - inOrder.verify(w, times(1)).onNext("abcd"); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - - InOrder inOrder2 = inOrder(w2); - inOrder2.verify(w2, times(1)).onNext("a"); - inOrder2.verify(w2, times(1)).onNext("x"); - inOrder2.verify(w2, times(1)).onNext("abc"); - inOrder2.verify(w2, times(1)).onNext("abcd"); - inOrder2.verify(w2, times(1)).onCompleted(); - inOrder2.verify(w2, never()).onNext(anyString()); - verify(w2, never()).onError(any(Throwable.class)); - } - - @Test - public void testDistinctOfSourceWithNulls() { - Observable src = from(null, "a", "a", null, null, "b", null); - create(distinct(src)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext(null); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("b"); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testDistinctOfSourceWithExceptionsFromKeySelector() { - Observable src = from("a", "b", null, "c"); - create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("b"); - inOrder.verify(w, times(1)).onError(any(NullPointerException.class)); - inOrder.verify(w, never()).onNext(anyString()); - inOrder.verify(w, never()).onCompleted(); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationDistinctUntilChanged.java b/rxjava-core/src/main/java/rx/operators/OperationDistinctUntilChanged.java index c43edc0202..d1ea86b653 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDistinctUntilChanged.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDistinctUntilChanged.java @@ -15,20 +15,6 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.MockitoAnnotations.initMocks; -import static rx.Observable.create; -import static rx.Observable.empty; -import static rx.Observable.from; - -import java.util.Comparator; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mock; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -38,6 +24,8 @@ import rx.util.functions.Func1; import rx.util.functions.Functions; +import java.util.Comparator; + /** * Returns an Observable that emits all sequentially distinct items emitted by the source. */ @@ -152,169 +140,4 @@ public void call() { }); } } - - public static class UnitTest { - @Mock - Observer w; - @Mock - Observer w2; - - // nulls lead to exceptions - final Func1 TO_UPPER_WITH_EXCEPTION = new Func1() { - @Override - public String call(String s) { - if (s.equals("x")) { - return "xx"; - } - return s.toUpperCase(); - } - }; - - final Comparator COMPARE_LENGTH = new Comparator() { - @Override - public int compare(String s1, String s2) { - return s1.length() - s2.length(); - } - }; - - @Before - public void before() { - initMocks(this); - } - - @Test - public void testDistinctUntilChangedOfNone() { - Observable src = empty(); - create(distinctUntilChanged(src)).subscribe(w); - - verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testDistinctUntilChangedOfNoneWithKeySelector() { - Observable src = empty(); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); - - verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testDistinctUntilChangedOfNormalSource() { - Observable src = from("a", "b", "c", "c", "c", "b", "b", "a", "e"); - create(distinctUntilChanged(src)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("b"); - inOrder.verify(w, times(1)).onNext("c"); - inOrder.verify(w, times(1)).onNext("b"); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("e"); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testDistinctUntilChangedOfNormalSourceWithKeySelector() { - Observable src = from("a", "b", "c", "C", "c", "B", "b", "a", "e"); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("b"); - inOrder.verify(w, times(1)).onNext("c"); - inOrder.verify(w, times(1)).onNext("B"); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("e"); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testDistinctUntilChangedOfSourceWithNulls() { - Observable src = from(null, "a", "a", null, null, "b", null, null); - create(distinctUntilChanged(src)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext(null); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext(null); - inOrder.verify(w, times(1)).onNext("b"); - inOrder.verify(w, times(1)).onNext(null); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testDistinctUntilChangedOfSourceWithExceptionsFromKeySelector() { - Observable src = from("a", "b", null, "c"); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("b"); - verify(w, times(1)).onError(any(NullPointerException.class)); - inOrder.verify(w, never()).onNext(anyString()); - inOrder.verify(w, never()).onCompleted(); - } - - @Test - public void testDistinctUntilChangedWithComparator() { - Observable src = from("a", "b", "c", "aa", "bb", "c", "ddd"); - create(distinctUntilChanged(src, COMPARE_LENGTH)).subscribe(w); - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("aa"); - inOrder.verify(w, times(1)).onNext("c"); - inOrder.verify(w, times(1)).onNext("ddd"); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testDistinctUntilChangedWithComparatorAndKeySelector() { - Observable src = from("a", "b", "x", "aa", "bb", "c", "ddd"); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("x"); - inOrder.verify(w, times(1)).onNext("c"); - inOrder.verify(w, times(1)).onNext("ddd"); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testDistinctUntilChangedWithComparatorAndKeySelectorandTwoSubscriptions() { - Observable src = from("a", "b", "x", "aa", "bb", "c", "ddd"); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext("a"); - inOrder.verify(w, times(1)).onNext("x"); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2); - inOrder.verify(w, times(1)).onNext("c"); - inOrder.verify(w, times(1)).onNext("ddd"); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onNext(anyString()); - verify(w, never()).onError(any(Throwable.class)); - - InOrder inOrder2 = inOrder(w2); - inOrder2.verify(w2, times(1)).onNext("a"); - inOrder2.verify(w2, times(1)).onNext("x"); - inOrder2.verify(w2, times(1)).onNext("c"); - inOrder2.verify(w2, times(1)).onNext("ddd"); - inOrder2.verify(w2, times(1)).onCompleted(); - inOrder2.verify(w2, never()).onNext(anyString()); - verify(w2, never()).onError(any(Throwable.class)); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationElementAt.java b/rxjava-core/src/main/java/rx/operators/OperationElementAt.java index 0d91709be1..c1310bc013 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationElementAt.java +++ b/rxjava-core/src/main/java/rx/operators/OperationElementAt.java @@ -15,25 +15,13 @@ */ package rx.operators; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import java.util.Iterator; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; +import java.util.concurrent.atomic.AtomicInteger; + /** * Returns the element at a specified index in a sequence. */ @@ -146,102 +134,4 @@ public void onCompleted() { })); } } - - public static class UnitTest { - - @Test - public void testElementAt() { - Observable w = Observable.from(1, 2); - Observable observable = Observable.create(elementAt(w, 1)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(1); - verify(aObserver, times(1)).onNext(2); - verify(aObserver, never()).onError( - any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testElementAtWithMinusIndex() { - Observable w = Observable.from(1, 2); - Observable observable = Observable - .create(elementAt(w, -1)); - - try { - Iterator iter = OperationToIterator - .toIterator(observable); - assertTrue(iter.hasNext()); - iter.next(); - fail("expect an IndexOutOfBoundsException when index is out of bounds"); - } catch (IndexOutOfBoundsException e) { - } - } - - @Test - public void testElementAtWithIndexOutOfBounds() - throws InterruptedException, ExecutionException { - Observable w = Observable.from(1, 2); - Observable observable = Observable.create(elementAt(w, 2)); - try { - Iterator iter = OperationToIterator - .toIterator(observable); - assertTrue(iter.hasNext()); - iter.next(); - fail("expect an IndexOutOfBoundsException when index is out of bounds"); - } catch (IndexOutOfBoundsException e) { - } - } - - @Test - public void testElementAtOrDefault() throws InterruptedException, - ExecutionException { - Observable w = Observable.from(1, 2); - Observable observable = Observable - .create(elementAtOrDefault(w, 1, 0)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(1); - verify(aObserver, times(1)).onNext(2); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testElementAtOrDefaultWithIndexOutOfBounds() - throws InterruptedException, ExecutionException { - Observable w = Observable.from(1, 2); - Observable observable = Observable - .create(elementAtOrDefault(w, 2, 0)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(1); - verify(aObserver, never()).onNext(2); - verify(aObserver, times(1)).onNext(0); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testElementAtOrDefaultWithMinusIndex() { - Observable w = Observable.from(1, 2); - Observable observable = Observable - .create(elementAtOrDefault(w, -1, 0)); - - try { - Iterator iter = OperationToIterator - .toIterator(observable); - assertTrue(iter.hasNext()); - iter.next(); - fail("expect an IndexOutOfBoundsException when index is out of bounds"); - } catch (IndexOutOfBoundsException e) { - } - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationFilter.java b/rxjava-core/src/main/java/rx/operators/OperationFilter.java index 5026f11d0c..560600dc9c 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationFilter.java +++ b/rxjava-core/src/main/java/rx/operators/OperationFilter.java @@ -15,12 +15,6 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import org.junit.Test; -import org.mockito.Mockito; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -74,28 +68,4 @@ public void onCompleted() { } } - - public static class UnitTest { - - @Test - public void testFilter() { - Observable w = Observable.from("one", "two", "three"); - Observable observable = Observable.create(filter(w, new Func1() { - - @Override - public Boolean call(String t1) { - return t1.equals("two"); - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, Mockito.never()).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationFinally.java b/rxjava-core/src/main/java/rx/operators/OperationFinally.java index ad711b08ca..6e4d58768a 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationFinally.java +++ b/rxjava-core/src/main/java/rx/operators/OperationFinally.java @@ -15,11 +15,6 @@ */ package rx.operators; -import static org.mockito.Mockito.*; - -import org.junit.Before; -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -105,31 +100,4 @@ public void onNext(T args) { } } } - - public static class UnitTest { - private Action0 aAction0; - private Observer aObserver; - - @SuppressWarnings("unchecked") // mocking has to be unchecked, unfortunately - @Before - public void before() { - aAction0 = mock(Action0.class); - aObserver = mock(Observer.class); - } - - private void checkActionCalled(Observable input) { - Observable.create(finallyDo(input, aAction0)).subscribe(aObserver); - verify(aAction0, times(1)).call(); - } - - @Test - public void testFinallyCalledOnComplete() { - checkActionCalled(Observable.from(new String[] {"1", "2", "3"})); - } - - @Test - public void testFinallyCalledOnError() { - checkActionCalled(Observable.error(new RuntimeException("expected"))); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationFirstOrDefault.java b/rxjava-core/src/main/java/rx/operators/OperationFirstOrDefault.java index 73283a8a3b..66dcb6e90d 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationFirstOrDefault.java +++ b/rxjava-core/src/main/java/rx/operators/OperationFirstOrDefault.java @@ -15,20 +15,6 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.MockitoAnnotations.initMocks; -import static rx.Observable.create; -import static rx.Observable.empty; -import static rx.Observable.from; -import static rx.util.functions.Functions.alwaysTrue; - -import java.util.concurrent.atomic.AtomicBoolean; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -37,6 +23,10 @@ import rx.util.functions.Action0; import rx.util.functions.Func1; +import java.util.concurrent.atomic.AtomicBoolean; + +import static rx.util.functions.Functions.alwaysTrue; + /** * Returns an Observable that emits the first item emitted by the source * Observable, or a default value if the source emits nothing. @@ -126,65 +116,4 @@ public void call() { }); } } - - public static class UnitTest { - @Mock - Observer w; - - private static final Func1 IS_D = new Func1() { - @Override - public Boolean call(String value) { - return "d".equals(value); - } - }; - - @Before - public void before() { - initMocks(this); - } - - @Test - public void testFirstOrElseOfNone() { - Observable src = empty(); - create(firstOrDefault(src, "default")).subscribe(w); - - verify(w, times(1)).onNext(anyString()); - verify(w, times(1)).onNext("default"); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testFirstOrElseOfSome() { - Observable src = from("a", "b", "c"); - create(firstOrDefault(src, "default")).subscribe(w); - - verify(w, times(1)).onNext(anyString()); - verify(w, times(1)).onNext("a"); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testFirstOrElseWithPredicateOfNoneMatchingThePredicate() { - Observable src = from("a", "b", "c"); - create(firstOrDefault(src, IS_D, "default")).subscribe(w); - - verify(w, times(1)).onNext(anyString()); - verify(w, times(1)).onNext("default"); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testFirstOrElseWithPredicateOfSome() { - Observable src = from("a", "b", "c", "d", "e", "f"); - create(firstOrDefault(src, IS_D, "default")).subscribe(w); - - verify(w, times(1)).onNext(anyString()); - verify(w, times(1)).onNext("d"); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java b/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java index 96a6861406..e4311c5421 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java +++ b/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java @@ -15,32 +15,19 @@ */ package rx.operators; -import static org.junit.Assert.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.observables.GroupedObservable; -import rx.subscriptions.BooleanSubscription; -import rx.subscriptions.Subscriptions; -import rx.util.functions.Action1; import rx.util.functions.Func1; import rx.util.functions.Functions; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + /** * Groups the items emitted by an Observable according to a specified criterion, and emits these * grouped items as Observables, one Observable per group. @@ -251,312 +238,4 @@ private KeyValue(K key, V value) { this.value = value; } } - - public static class UnitTest { - final Func1 length = new Func1() { - @Override - public Integer call(String s) { - return s.length(); - } - }; - - @Test - public void testGroupBy() { - Observable source = Observable.from("one", "two", "three", "four", "five", "six"); - Observable> grouped = Observable.create(groupBy(source, length)); - - Map> map = toMap(grouped); - - assertEquals(3, map.size()); - assertArrayEquals(Arrays.asList("one", "two", "six").toArray(), map.get(3).toArray()); - assertArrayEquals(Arrays.asList("four", "five").toArray(), map.get(4).toArray()); - assertArrayEquals(Arrays.asList("three").toArray(), map.get(5).toArray()); - } - - @Test - public void testEmpty() { - Observable source = Observable.empty(); - Observable> grouped = Observable.create(groupBy(source, length)); - - Map> map = toMap(grouped); - - assertTrue(map.isEmpty()); - } - - @Test - public void testError() { - Observable sourceStrings = Observable.from("one", "two", "three", "four", "five", "six"); - Observable errorSource = Observable.error(new RuntimeException("forced failure")); - Observable source = Observable.concat(sourceStrings, errorSource); - - Observable> grouped = Observable.create(groupBy(source, length)); - - final AtomicInteger groupCounter = new AtomicInteger(); - final AtomicInteger eventCounter = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); - - grouped.mapMany(new Func1, Observable>() { - - @Override - public Observable call(final GroupedObservable o) { - groupCounter.incrementAndGet(); - return o.map(new Func1() { - - @Override - public String call(String v) { - return "Event => key: " + o.getKey() + " value: " + v; - } - }); - } - }).subscribe(new Observer() { - - @Override - public void onCompleted() { - - } - - @Override - public void onError(Throwable e) { - e.printStackTrace(); - error.set(e); - } - - @Override - public void onNext(String v) { - eventCounter.incrementAndGet(); - System.out.println(v); - - } - }); - - assertEquals(3, groupCounter.get()); - assertEquals(6, eventCounter.get()); - assertNotNull(error.get()); - } - - private static Map> toMap(Observable> observable) { - - final ConcurrentHashMap> result = new ConcurrentHashMap>(); - - observable.toBlockingObservable().forEach(new Action1>() { - - @Override - public void call(final GroupedObservable o) { - result.put(o.getKey(), new ConcurrentLinkedQueue()); - o.subscribe(new Action1() { - - @Override - public void call(V v) { - result.get(o.getKey()).add(v); - } - - }); - } - }); - - return result; - } - - /** - * Assert that only a single subscription to a stream occurs and that all events are received. - * - * @throws Throwable - */ - @Test - public void testGroupedEventStream() throws Throwable { - - final AtomicInteger eventCounter = new AtomicInteger(); - final AtomicInteger subscribeCounter = new AtomicInteger(); - final AtomicInteger groupCounter = new AtomicInteger(); - final CountDownLatch latch = new CountDownLatch(1); - final int count = 100; - final int groupCount = 2; - - Observable es = Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("*** Subscribing to EventStream ***"); - subscribeCounter.incrementAndGet(); - new Thread(new Runnable() { - - @Override - public void run() { - for (int i = 0; i < count; i++) { - Event e = new Event(); - e.source = i % groupCount; - e.message = "Event-" + i; - observer.onNext(e); - } - observer.onCompleted(); - } - - }).start(); - return Subscriptions.empty(); - } - - }); - - es.groupBy(new Func1() { - - @Override - public Integer call(Event e) { - return e.source; - } - }).mapMany(new Func1, Observable>() { - - @Override - public Observable call(GroupedObservable eventGroupedObservable) { - System.out.println("GroupedObservable Key: " + eventGroupedObservable.getKey()); - groupCounter.incrementAndGet(); - - return eventGroupedObservable.map(new Func1() { - - @Override - public String call(Event event) { - return "Source: " + event.source + " Message: " + event.message; - } - }); - - } - }).subscribe(new Observer() { - - @Override - public void onCompleted() { - latch.countDown(); - } - - @Override - public void onError(Throwable e) { - e.printStackTrace(); - latch.countDown(); - } - - @Override - public void onNext(String outputMessage) { - System.out.println(outputMessage); - eventCounter.incrementAndGet(); - } - }); - - latch.await(5000, TimeUnit.MILLISECONDS); - assertEquals(1, subscribeCounter.get()); - assertEquals(groupCount, groupCounter.get()); - assertEquals(count, eventCounter.get()); - - } - - /* - * We will only take 1 group with 20 events from it and then unsubscribe. - */ - @Test - public void testUnsubscribe() throws InterruptedException { - - final AtomicInteger eventCounter = new AtomicInteger(); - final AtomicInteger subscribeCounter = new AtomicInteger(); - final AtomicInteger groupCounter = new AtomicInteger(); - final AtomicInteger sentEventCounter = new AtomicInteger(); - final CountDownLatch latch = new CountDownLatch(1); - final int count = 100; - final int groupCount = 2; - - Observable es = Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(final Observer observer) { - final BooleanSubscription s = new BooleanSubscription(); - System.out.println("testUnsubscribe => *** Subscribing to EventStream ***"); - subscribeCounter.incrementAndGet(); - new Thread(new Runnable() { - - @Override - public void run() { - for (int i = 0; i < count; i++) { - if (s.isUnsubscribed()) { - break; - } - Event e = new Event(); - e.source = i % groupCount; - e.message = "Event-" + i; - observer.onNext(e); - sentEventCounter.incrementAndGet(); - } - observer.onCompleted(); - } - - }).start(); - return s; - } - - }); - - es.groupBy(new Func1() { - - @Override - public Integer call(Event e) { - return e.source; - } - }) - .take(1) // we want only the first group - .mapMany(new Func1, Observable>() { - - @Override - public Observable call(GroupedObservable eventGroupedObservable) { - System.out.println("testUnsubscribe => GroupedObservable Key: " + eventGroupedObservable.getKey()); - groupCounter.incrementAndGet(); - - return eventGroupedObservable - .take(20) // limit to only 20 events on this group - .map(new Func1() { - - @Override - public String call(Event event) { - return "testUnsubscribe => Source: " + event.source + " Message: " + event.message; - } - }); - - } - }).subscribe(new Observer() { - - @Override - public void onCompleted() { - latch.countDown(); - } - - @Override - public void onError(Throwable e) { - e.printStackTrace(); - latch.countDown(); - } - - @Override - public void onNext(String outputMessage) { - System.out.println(outputMessage); - eventCounter.incrementAndGet(); - } - }); - - latch.await(5000, TimeUnit.MILLISECONDS); - assertEquals(1, subscribeCounter.get()); - assertEquals(1, groupCounter.get()); - assertEquals(20, eventCounter.get()); - // sentEvents will go until 'eventCounter' hits 20 and then unsubscribes - // which means it will also send (but ignore) the 19/20 events for the other group - // It will not however send all 100 events. - assertEquals(39, sentEventCounter.get(), 10); - // gave it a delta of 10 to account for the threading/unsubscription race condition which can vary depending on a machines performance, thread-scheduler, etc - } - - private static class Event { - int source; - String message; - - @Override - public String toString() { - return "Event => source: " + source + " message: " + message; - } - } - - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationInterval.java b/rxjava-core/src/main/java/rx/operators/OperationInterval.java index 0711bff4a2..0c767eada7 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationInterval.java +++ b/rxjava-core/src/main/java/rx/operators/OperationInterval.java @@ -15,27 +15,16 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; - -import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; import rx.Subscription; import rx.concurrency.Schedulers; -import rx.concurrency.TestScheduler; -import rx.observables.ConnectableObservable; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import java.util.concurrent.TimeUnit; + /** * Returns an observable sequence that produces a value after each period. * The value starts at 0 and counts up each period. @@ -94,163 +83,4 @@ public void call() { }); } } - - public static class UnitTest { - private TestScheduler scheduler; - private Observer observer; - private Observer observer2; - - @Before - @SuppressWarnings("unchecked") // due to mocking - public void before() { - scheduler = new TestScheduler(); - observer = mock(Observer.class); - observer2 = mock(Observer.class); - } - - @Test - public void testInterval() { - Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler)); - Subscription sub = w.subscribe(observer); - - verify(observer, never()).onNext(0L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(0L); - inOrder.verify(observer, times(1)).onNext(1L); - inOrder.verify(observer, never()).onNext(2L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - sub.unsubscribe(); - scheduler.advanceTimeTo(4, TimeUnit.SECONDS); - verify(observer, never()).onNext(2L); - verify(observer, times(1)).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - } - - @Test - public void testWithMultipleSubscribersStartingAtSameTime() { - Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler)); - Subscription sub1 = w.subscribe(observer); - Subscription sub2 = w.subscribe(observer2); - - verify(observer, never()).onNext(anyLong()); - verify(observer2, never()).onNext(anyLong()); - - scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - - InOrder inOrder1 = inOrder(observer); - InOrder inOrder2 = inOrder(observer2); - - inOrder1.verify(observer, times(1)).onNext(0L); - inOrder1.verify(observer, times(1)).onNext(1L); - inOrder1.verify(observer, never()).onNext(2L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - inOrder2.verify(observer2, times(1)).onNext(0L); - inOrder2.verify(observer2, times(1)).onNext(1L); - inOrder2.verify(observer2, never()).onNext(2L); - verify(observer2, never()).onCompleted(); - verify(observer2, never()).onError(any(Throwable.class)); - - sub1.unsubscribe(); - sub2.unsubscribe(); - scheduler.advanceTimeTo(4, TimeUnit.SECONDS); - - verify(observer, never()).onNext(2L); - verify(observer, times(1)).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - verify(observer2, never()).onNext(2L); - verify(observer2, times(1)).onCompleted(); - verify(observer2, never()).onError(any(Throwable.class)); - } - - @Test - public void testWithMultipleStaggeredSubscribers() { - Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler)); - Subscription sub1 = w.subscribe(observer); - - verify(observer, never()).onNext(anyLong()); - - scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - Subscription sub2 = w.subscribe(observer2); - - InOrder inOrder1 = inOrder(observer); - inOrder1.verify(observer, times(1)).onNext(0L); - inOrder1.verify(observer, times(1)).onNext(1L); - inOrder1.verify(observer, never()).onNext(2L); - - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - verify(observer2, never()).onNext(anyLong()); - - scheduler.advanceTimeTo(4, TimeUnit.SECONDS); - - inOrder1.verify(observer, times(1)).onNext(2L); - inOrder1.verify(observer, times(1)).onNext(3L); - - InOrder inOrder2 = inOrder(observer2); - inOrder2.verify(observer2, times(1)).onNext(0L); - inOrder2.verify(observer2, times(1)).onNext(1L); - - sub1.unsubscribe(); - sub2.unsubscribe(); - - inOrder1.verify(observer, never()).onNext(anyLong()); - inOrder1.verify(observer, times(1)).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - inOrder2.verify(observer2, never()).onNext(anyLong()); - inOrder2.verify(observer2, times(1)).onCompleted(); - verify(observer2, never()).onError(any(Throwable.class)); - } - - @Test - public void testWithMultipleStaggeredSubscribersAndPublish() { - ConnectableObservable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler)).publish(); - Subscription sub1 = w.subscribe(observer); - w.connect(); - - verify(observer, never()).onNext(anyLong()); - - scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - Subscription sub2 = w.subscribe(observer2); - - InOrder inOrder1 = inOrder(observer); - inOrder1.verify(observer, times(1)).onNext(0L); - inOrder1.verify(observer, times(1)).onNext(1L); - inOrder1.verify(observer, never()).onNext(2L); - - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - verify(observer2, never()).onNext(anyLong()); - - scheduler.advanceTimeTo(4, TimeUnit.SECONDS); - - inOrder1.verify(observer, times(1)).onNext(2L); - inOrder1.verify(observer, times(1)).onNext(3L); - - InOrder inOrder2 = inOrder(observer2); - inOrder2.verify(observer2, times(1)).onNext(2L); - inOrder2.verify(observer2, times(1)).onNext(3L); - - sub1.unsubscribe(); - sub2.unsubscribe(); - - inOrder1.verify(observer, never()).onNext(anyLong()); - inOrder1.verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - inOrder2.verify(observer2, never()).onNext(anyLong()); - inOrder2.verify(observer2, never()).onCompleted(); - verify(observer2, never()).onError(any(Throwable.class)); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationMap.java b/rxjava-core/src/main/java/rx/operators/OperationMap.java index 940147b0b8..f2543326f2 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMap.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMap.java @@ -15,29 +15,10 @@ */ package rx.operators; -import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.concurrency.Schedulers; import rx.util.functions.Func1; import rx.util.functions.Func2; @@ -156,258 +137,4 @@ public void onCompleted() { }))); } } - - public static class UnitTest { - @Mock - Observer stringObserver; - @Mock - Observer stringObserver2; - - final static Func2 APPEND_INDEX = new Func2() { - @Override - public String call(String value, Integer index) { - return value + index; - } - }; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testMap() { - Map m1 = getMap("One"); - Map m2 = getMap("Two"); - Observable> observable = Observable.from(m1, m2); - - Observable m = Observable.create(map(observable, new Func1, String>() { - - @Override - public String call(Map map) { - return map.get("firstName"); - } - - })); - m.subscribe(stringObserver); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(1)).onNext("OneFirst"); - verify(stringObserver, times(1)).onNext("TwoFirst"); - verify(stringObserver, times(1)).onCompleted(); - } - - @Test - public void testMapWithIndex() { - Observable w = Observable.from("a", "b", "c"); - Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX)); - m.subscribe(stringObserver); - InOrder inOrder = inOrder(stringObserver); - inOrder.verify(stringObserver, times(1)).onNext("a0"); - inOrder.verify(stringObserver, times(1)).onNext("b1"); - inOrder.verify(stringObserver, times(1)).onNext("c2"); - inOrder.verify(stringObserver, times(1)).onCompleted(); - verify(stringObserver, never()).onError(any(Throwable.class)); - } - - @Test - public void testMapWithIndexAndMultipleSubscribers() { - Observable w = Observable.from("a", "b", "c"); - Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX)); - m.subscribe(stringObserver); - m.subscribe(stringObserver2); - InOrder inOrder = inOrder(stringObserver); - inOrder.verify(stringObserver, times(1)).onNext("a0"); - inOrder.verify(stringObserver, times(1)).onNext("b1"); - inOrder.verify(stringObserver, times(1)).onNext("c2"); - inOrder.verify(stringObserver, times(1)).onCompleted(); - verify(stringObserver, never()).onError(any(Throwable.class)); - - InOrder inOrder2 = inOrder(stringObserver2); - inOrder2.verify(stringObserver2, times(1)).onNext("a0"); - inOrder2.verify(stringObserver2, times(1)).onNext("b1"); - inOrder2.verify(stringObserver2, times(1)).onNext("c2"); - inOrder2.verify(stringObserver2, times(1)).onCompleted(); - verify(stringObserver2, never()).onError(any(Throwable.class)); - } - - @Test - public void testMapMany() { - /* simulate a top-level async call which returns IDs */ - Observable ids = Observable.from(1, 2); - - /* now simulate the behavior to take those IDs and perform nested async calls based on them */ - Observable m = Observable.create(mapMany(ids, new Func1>() { - - @Override - public Observable call(Integer id) { - /* simulate making a nested async call which creates another Observable */ - Observable> subObservable = null; - if (id == 1) { - Map m1 = getMap("One"); - Map m2 = getMap("Two"); - subObservable = Observable.from(m1, m2); - } else { - Map m3 = getMap("Three"); - Map m4 = getMap("Four"); - subObservable = Observable.from(m3, m4); - } - - /* simulate kicking off the async call and performing a select on it to transform the data */ - return Observable.create(map(subObservable, new Func1, String>() { - @Override - public String call(Map map) { - return map.get("firstName"); - } - })); - } - - })); - m.subscribe(stringObserver); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(1)).onNext("OneFirst"); - verify(stringObserver, times(1)).onNext("TwoFirst"); - verify(stringObserver, times(1)).onNext("ThreeFirst"); - verify(stringObserver, times(1)).onNext("FourFirst"); - verify(stringObserver, times(1)).onCompleted(); - } - - @Test - public void testMapMany2() { - Map m1 = getMap("One"); - Map m2 = getMap("Two"); - Observable> observable1 = Observable.from(m1, m2); - - Map m3 = getMap("Three"); - Map m4 = getMap("Four"); - Observable> observable2 = Observable.from(m3, m4); - - Observable>> observable = Observable.from(observable1, observable2); - - Observable m = Observable.create(mapMany(observable, new Func1>, Observable>() { - - @Override - public Observable call(Observable> o) { - return Observable.create(map(o, new Func1, String>() { - - @Override - public String call(Map map) { - return map.get("firstName"); - } - })); - } - - })); - m.subscribe(stringObserver); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(1)).onNext("OneFirst"); - verify(stringObserver, times(1)).onNext("TwoFirst"); - verify(stringObserver, times(1)).onNext("ThreeFirst"); - verify(stringObserver, times(1)).onNext("FourFirst"); - verify(stringObserver, times(1)).onCompleted(); - - } - - @Test - public void testMapWithError() { - Observable w = Observable.from("one", "fail", "two", "three", "fail"); - Observable m = Observable.create(map(w, new Func1() { - @Override - public String call(String s) { - if ("fail".equals(s)) { - throw new RuntimeException("Forced Failure"); - } - return s; - } - })); - - m.subscribe(stringObserver); - verify(stringObserver, times(1)).onNext("one"); - verify(stringObserver, never()).onNext("two"); - verify(stringObserver, never()).onNext("three"); - verify(stringObserver, never()).onCompleted(); - verify(stringObserver, times(1)).onError(any(Throwable.class)); - } - - @Test - public void testMapWithSynchronousObservableContainingError() { - Observable w = Observable.from("one", "fail", "two", "three", "fail"); - final AtomicInteger c1 = new AtomicInteger(); - final AtomicInteger c2 = new AtomicInteger(); - Observable m = Observable.create(map(w, new Func1() { - @Override - public String call(String s) { - if ("fail".equals(s)) - throw new RuntimeException("Forced Failure"); - System.out.println("BadMapper:" + s); - c1.incrementAndGet(); - return s; - } - })).map(new Func1() { - @Override - public String call(String s) { - System.out.println("SecondMapper:" + s); - c2.incrementAndGet(); - return s; - } - }); - - m.subscribe(stringObserver); - - verify(stringObserver, times(1)).onNext("one"); - verify(stringObserver, never()).onNext("two"); - verify(stringObserver, never()).onNext("three"); - verify(stringObserver, never()).onCompleted(); - verify(stringObserver, times(1)).onError(any(Throwable.class)); - - // we should have only returned 1 value: "one" - assertEquals(1, c1.get()); - assertEquals(1, c2.get()); - } - - @Test(expected = IllegalArgumentException.class) - public void testMapWithIssue417() { - Observable.from(1).observeOn(Schedulers.threadPoolForComputation()) - .map(new Func1() { - public Integer call(Integer arg0) { - throw new IllegalArgumentException("any error"); - } - }).toBlockingObservable().single(); - } - - @Test - public void testMapWithErrorInFuncAndThreadPoolScheduler() throws InterruptedException { - // The error will throw in one of threads in the thread pool. - // If map does not handle it, the error will disappear. - // so map needs to handle the error by itself. - final CountDownLatch latch = new CountDownLatch(1); - Observable m = Observable.from("one") - .observeOn(Schedulers.threadPoolForComputation()) - .map(new Func1() { - public String call(String arg0) { - try { - throw new IllegalArgumentException("any error"); - } finally { - latch.countDown(); - } - } - }); - - m.subscribe(stringObserver); - latch.await(); - InOrder inorder = inOrder(stringObserver); - inorder.verify(stringObserver, times(1)).onError(any(IllegalArgumentException.class)); - inorder.verifyNoMoreInteractions(); - } - - private static Map getMap(String prefix) { - Map m = new HashMap(); - m.put("firstName", prefix + "First"); - m.put("lastName", prefix + "Last"); - return m; - } - - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationMaterialize.java b/rxjava-core/src/main/java/rx/operators/OperationMaterialize.java index 7d906720fa..f1998ffe64 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMaterialize.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMaterialize.java @@ -15,14 +15,6 @@ */ package rx.operators; -import static org.junit.Assert.*; - -import java.util.List; -import java.util.Vector; -import java.util.concurrent.ExecutionException; - -import org.junit.Test; - import rx.Notification; import rx.Observable; import rx.Observable.OnSubscribeFunc; @@ -85,140 +77,4 @@ public void onNext(T value) { } } - - public static class UnitTest { - @Test - public void testMaterialize1() { - // null will cause onError to be triggered before "three" can be returned - final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", null, "three"); - - TestObserver Observer = new TestObserver(); - Observable> m = Observable.create(materialize(Observable.create(o1))); - m.subscribe(Observer); - - try { - o1.t.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - assertFalse(Observer.onError); - assertTrue(Observer.onCompleted); - assertEquals(3, Observer.notifications.size()); - assertEquals("one", Observer.notifications.get(0).getValue()); - assertTrue(Observer.notifications.get(0).isOnNext()); - assertEquals("two", Observer.notifications.get(1).getValue()); - assertTrue(Observer.notifications.get(1).isOnNext()); - assertEquals(NullPointerException.class, Observer.notifications.get(2).getThrowable().getClass()); - assertTrue(Observer.notifications.get(2).isOnError()); - } - - @Test - public void testMaterialize2() { - final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", "three"); - - TestObserver Observer = new TestObserver(); - Observable> m = Observable.create(materialize(Observable.create(o1))); - m.subscribe(Observer); - - try { - o1.t.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - assertFalse(Observer.onError); - assertTrue(Observer.onCompleted); - assertEquals(4, Observer.notifications.size()); - assertEquals("one", Observer.notifications.get(0).getValue()); - assertTrue(Observer.notifications.get(0).isOnNext()); - assertEquals("two", Observer.notifications.get(1).getValue()); - assertTrue(Observer.notifications.get(1).isOnNext()); - assertEquals("three", Observer.notifications.get(2).getValue()); - assertTrue(Observer.notifications.get(2).isOnNext()); - assertTrue(Observer.notifications.get(3).isOnCompleted()); - } - - @Test - public void testMultipleSubscribes() throws InterruptedException, ExecutionException { - final TestAsyncErrorObservable o = new TestAsyncErrorObservable("one", "two", null, "three"); - - Observable> m = Observable.create(materialize(Observable.create(o))); - - assertEquals(3, m.toList().toBlockingObservable().toFuture().get().size()); - assertEquals(3, m.toList().toBlockingObservable().toFuture().get().size()); - } - - } - - private static class TestObserver implements Observer> { - - boolean onCompleted = false; - boolean onError = false; - List> notifications = new Vector>(); - - @Override - public void onCompleted() { - this.onCompleted = true; - } - - @Override - public void onError(Throwable e) { - this.onError = true; - } - - @Override - public void onNext(Notification value) { - this.notifications.add(value); - } - - } - - private static class TestAsyncErrorObservable implements OnSubscribeFunc { - - String[] valuesToReturn; - - TestAsyncErrorObservable(String... values) { - valuesToReturn = values; - } - - volatile Thread t; - - @Override - public Subscription onSubscribe(final Observer observer) { - t = new Thread(new Runnable() { - - @Override - public void run() { - for (String s : valuesToReturn) { - if (s == null) { - System.out.println("throwing exception"); - try { - Thread.sleep(100); - } catch (Throwable e) { - - } - observer.onError(new NullPointerException()); - return; - } else { - observer.onNext(s); - } - } - System.out.println("subscription complete"); - observer.onCompleted(); - } - - }); - t.start(); - - return new Subscription() { - - @Override - public void unsubscribe() { - - } - - }; - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationMerge.java b/rxjava-core/src/main/java/rx/operators/OperationMerge.java index ab226c1eee..4f98439292 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMerge.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMerge.java @@ -15,32 +15,15 @@ */ package rx.operators; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.subscriptions.CompositeSubscription; -import rx.subscriptions.Subscriptions; -import rx.util.functions.Action0; -import rx.util.functions.Action1; + +import java.util.Arrays; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; /** * Flattens a list of Observables into one Observable sequence, without any transformation. @@ -287,435 +270,4 @@ public void onNext(T args) { } } - - public static class UnitTest { - @Mock - Observer stringObserver; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testMergeObservableOfObservables() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); - - Observable> observableOfObservables = Observable.create(new OnSubscribeFunc>() { - - @Override - public Subscription onSubscribe(Observer> observer) { - // simulate what would happen in an observable - observer.onNext(o1); - observer.onNext(o2); - observer.onCompleted(); - - return new Subscription() { - - @Override - public void unsubscribe() { - // unregister ... will never be called here since we are executing synchronously - } - - }; - } - - }); - Observable m = Observable.create(merge(observableOfObservables)); - m.subscribe(stringObserver); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(1)).onCompleted(); - verify(stringObserver, times(2)).onNext("hello"); - } - - @Test - public void testMergeArray() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(merge(o1, o2)); - m.subscribe(stringObserver); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(2)).onNext("hello"); - verify(stringObserver, times(1)).onCompleted(); - } - - @Test - public void testMergeList() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); - List> listOfObservables = new ArrayList>(); - listOfObservables.add(o1); - listOfObservables.add(o2); - - Observable m = Observable.create(merge(listOfObservables)); - m.subscribe(stringObserver); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(1)).onCompleted(); - verify(stringObserver, times(2)).onNext("hello"); - } - - @Test - public void testUnSubscribe() { - TestObservable tA = new TestObservable(); - TestObservable tB = new TestObservable(); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(merge(Observable.create(tA), Observable.create(tB))); - Subscription s = m.subscribe(stringObserver); - - tA.sendOnNext("Aone"); - tB.sendOnNext("Bone"); - s.unsubscribe(); - tA.sendOnNext("Atwo"); - tB.sendOnNext("Btwo"); - tA.sendOnCompleted(); - tB.sendOnCompleted(); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(1)).onNext("Aone"); - verify(stringObserver, times(1)).onNext("Bone"); - assertTrue(tA.unsubscribed); - assertTrue(tB.unsubscribed); - verify(stringObserver, never()).onNext("Atwo"); - verify(stringObserver, never()).onNext("Btwo"); - verify(stringObserver, never()).onCompleted(); - } - - @Test - public void testUnSubscribeObservableOfObservables() throws InterruptedException { - - final AtomicBoolean unsubscribed = new AtomicBoolean(); - final CountDownLatch latch = new CountDownLatch(1); - - Observable> source = Observable.create(new OnSubscribeFunc>() { - - @Override - public Subscription onSubscribe(final Observer> observer) { - // verbose on purpose so I can track the inside of it - final Subscription s = Subscriptions.create(new Action0() { - - @Override - public void call() { - System.out.println("*** unsubscribed"); - unsubscribed.set(true); - } - - }); - - new Thread(new Runnable() { - - @Override - public void run() { - - while (!unsubscribed.get()) { - observer.onNext(Observable.from(1L, 2L)); - } - System.out.println("Done looping after unsubscribe: " + unsubscribed.get()); - observer.onCompleted(); - - // mark that the thread is finished - latch.countDown(); - } - }).start(); - - return s; - }; - - }); - - final AtomicInteger count = new AtomicInteger(); - Observable.create(merge(source)).take(6).toBlockingObservable().forEach(new Action1() { - - @Override - public void call(Long v) { - System.out.println("Value: " + v); - int c = count.incrementAndGet(); - if (c > 6) { - fail("Should be only 6"); - } - - } - }); - - latch.await(1000, TimeUnit.MILLISECONDS); - - System.out.println("unsubscribed: " + unsubscribed.get()); - - assertTrue(unsubscribed.get()); - - } - - @Test - public void testMergeArrayWithThreading() { - final TestASynchronousObservable o1 = new TestASynchronousObservable(); - final TestASynchronousObservable o2 = new TestASynchronousObservable(); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(merge(Observable.create(o1), Observable.create(o2))); - m.subscribe(stringObserver); - - try { - o1.t.join(); - o2.t.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(2)).onNext("hello"); - verify(stringObserver, times(1)).onCompleted(); - } - - @Test - public void testSynchronizationOfMultipleSequences() throws Throwable { - final TestASynchronousObservable o1 = new TestASynchronousObservable(); - final TestASynchronousObservable o2 = new TestASynchronousObservable(); - - // use this latch to cause onNext to wait until we're ready to let it go - final CountDownLatch endLatch = new CountDownLatch(1); - - final AtomicInteger concurrentCounter = new AtomicInteger(); - final AtomicInteger totalCounter = new AtomicInteger(); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(merge(Observable.create(o1), Observable.create(o2))); - m.subscribe(new Observer() { - - @Override - public void onCompleted() { - - } - - @Override - public void onError(Throwable e) { - throw new RuntimeException("failed", e); - } - - @Override - public void onNext(String v) { - totalCounter.incrementAndGet(); - concurrentCounter.incrementAndGet(); - try { - // wait here until we're done asserting - endLatch.await(); - } catch (InterruptedException e) { - e.printStackTrace(); - throw new RuntimeException("failed", e); - } finally { - concurrentCounter.decrementAndGet(); - } - } - - }); - - // wait for both observables to send (one should be blocked) - o1.onNextBeingSent.await(); - o2.onNextBeingSent.await(); - - // I can't think of a way to know for sure that both threads have or are trying to send onNext - // since I can't use a CountDownLatch for "after" onNext since I want to catch during it - // but I can't know for sure onNext is invoked - // so I'm unfortunately reverting to using a Thread.sleep to allow the process scheduler time - // to make sure after o1.onNextBeingSent and o2.onNextBeingSent are hit that the following - // onNext is invoked. - - Thread.sleep(300); - - try { // in try/finally so threads are released via latch countDown even if assertion fails - assertEquals(1, concurrentCounter.get()); - } finally { - // release so it can finish - endLatch.countDown(); - } - - try { - o1.t.join(); - o2.t.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - assertEquals(2, totalCounter.get()); - assertEquals(0, concurrentCounter.get()); - } - - /** - * unit test from OperationMergeDelayError backported here to show how these use cases work with normal merge - */ - @Test - public void testError1() { - // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior - final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" - final Observable o2 = Observable.create(new TestErrorObservable("one", "two", "three")); // we expect to lose all of these since o1 is done first and fails - - @SuppressWarnings("unchecked") - Observable m = Observable.create(merge(o1, o2)); - m.subscribe(stringObserver); - - verify(stringObserver, times(1)).onError(any(NullPointerException.class)); - verify(stringObserver, never()).onCompleted(); - verify(stringObserver, times(0)).onNext("one"); - verify(stringObserver, times(0)).onNext("two"); - verify(stringObserver, times(0)).onNext("three"); - verify(stringObserver, times(1)).onNext("four"); - verify(stringObserver, times(0)).onNext("five"); - verify(stringObserver, times(0)).onNext("six"); - } - - /** - * unit test from OperationMergeDelayError backported here to show how these use cases work with normal merge - */ - @Test - public void testError2() { - // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior - final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); - final Observable o2 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" - final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null));// we expect to lose all of these since o2 is done first and fails - final Observable o4 = Observable.create(new TestErrorObservable("nine"));// we expect to lose all of these since o2 is done first and fails - - @SuppressWarnings("unchecked") - Observable m = Observable.create(merge(o1, o2, o3, o4)); - m.subscribe(stringObserver); - - verify(stringObserver, times(1)).onError(any(NullPointerException.class)); - verify(stringObserver, never()).onCompleted(); - verify(stringObserver, times(1)).onNext("one"); - verify(stringObserver, times(1)).onNext("two"); - verify(stringObserver, times(1)).onNext("three"); - verify(stringObserver, times(1)).onNext("four"); - verify(stringObserver, times(0)).onNext("five"); - verify(stringObserver, times(0)).onNext("six"); - verify(stringObserver, times(0)).onNext("seven"); - verify(stringObserver, times(0)).onNext("eight"); - verify(stringObserver, times(0)).onNext("nine"); - } - - private static class TestSynchronousObservable implements OnSubscribeFunc { - - @Override - public Subscription onSubscribe(Observer observer) { - - observer.onNext("hello"); - observer.onCompleted(); - - return new Subscription() { - - @Override - public void unsubscribe() { - // unregister ... will never be called here since we are executing synchronously - } - - }; - } - } - - private static class TestASynchronousObservable implements OnSubscribeFunc { - Thread t; - final CountDownLatch onNextBeingSent = new CountDownLatch(1); - - @Override - public Subscription onSubscribe(final Observer observer) { - t = new Thread(new Runnable() { - - @Override - public void run() { - onNextBeingSent.countDown(); - observer.onNext("hello"); - // I can't use a countDownLatch to prove we are actually sending 'onNext' - // since it will block if synchronized and I'll deadlock - observer.onCompleted(); - } - - }); - t.start(); - - return new Subscription() { - - @Override - public void unsubscribe() { - - } - - }; - } - } - - /** - * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. - */ - private static class TestObservable implements OnSubscribeFunc { - - Observer observer = null; - volatile boolean unsubscribed = false; - Subscription s = new Subscription() { - - @Override - public void unsubscribe() { - unsubscribed = true; - - } - - }; - - /* used to simulate subscription */ - public void sendOnCompleted() { - observer.onCompleted(); - } - - /* used to simulate subscription */ - public void sendOnNext(String value) { - observer.onNext(value); - } - - /* used to simulate subscription */ - @SuppressWarnings("unused") - public void sendOnError(Throwable e) { - observer.onError(e); - } - - @Override - public Subscription onSubscribe(final Observer observer) { - this.observer = observer; - return s; - } - } - - private static class TestErrorObservable implements OnSubscribeFunc { - - String[] valuesToReturn; - - TestErrorObservable(String... values) { - valuesToReturn = values; - } - - @Override - public Subscription onSubscribe(Observer observer) { - - for (String s : valuesToReturn) { - if (s == null) { - System.out.println("throwing exception"); - observer.onError(new NullPointerException()); - } else { - observer.onNext(s); - } - } - observer.onCompleted(); - - return new Subscription() { - - @Override - public void unsubscribe() { - // unregister ... will never be called here since we are executing synchronously - } - - }; - } - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java b/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java index abb450dd4d..5f1af2ff80 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java @@ -15,27 +15,17 @@ */ package rx.operators; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.util.CompositeException; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; + /** * This behaves like {@link OperationMerge} except that if any of the merged Observables notify of * an error via onError, mergeDelayError will refrain from propagating that error @@ -342,484 +332,4 @@ public void onNext(T args) { } } - - public static class UnitTest { - @Mock - Observer stringObserver; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testErrorDelayed1() { - final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final Observable o2 = Observable.create(new TestErrorObservable("one", "two", "three")); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(mergeDelayError(o1, o2)); - m.subscribe(stringObserver); - - verify(stringObserver, times(1)).onError(any(NullPointerException.class)); - verify(stringObserver, never()).onCompleted(); - verify(stringObserver, times(1)).onNext("one"); - verify(stringObserver, times(1)).onNext("two"); - verify(stringObserver, times(1)).onNext("three"); - verify(stringObserver, times(1)).onNext("four"); - verify(stringObserver, times(0)).onNext("five"); - verify(stringObserver, times(0)).onNext("six"); - } - - @Test - public void testErrorDelayed2() { - final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); - final Observable o2 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null)); - final Observable o4 = Observable.create(new TestErrorObservable("nine")); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4)); - m.subscribe(stringObserver); - - verify(stringObserver, times(1)).onError(any(NullPointerException.class)); - verify(stringObserver, never()).onCompleted(); - verify(stringObserver, times(1)).onNext("one"); - verify(stringObserver, times(1)).onNext("two"); - verify(stringObserver, times(1)).onNext("three"); - verify(stringObserver, times(1)).onNext("four"); - verify(stringObserver, times(0)).onNext("five"); - verify(stringObserver, times(0)).onNext("six"); - verify(stringObserver, times(1)).onNext("seven"); - verify(stringObserver, times(1)).onNext("eight"); - verify(stringObserver, times(1)).onNext("nine"); - } - - @Test - public void testErrorDelayed3() { - final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); - final Observable o2 = Observable.create(new TestErrorObservable("four", "five", "six")); - final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null)); - final Observable o4 = Observable.create(new TestErrorObservable("nine")); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4)); - m.subscribe(stringObserver); - - verify(stringObserver, times(1)).onError(any(NullPointerException.class)); - verify(stringObserver, never()).onCompleted(); - verify(stringObserver, times(1)).onNext("one"); - verify(stringObserver, times(1)).onNext("two"); - verify(stringObserver, times(1)).onNext("three"); - verify(stringObserver, times(1)).onNext("four"); - verify(stringObserver, times(1)).onNext("five"); - verify(stringObserver, times(1)).onNext("six"); - verify(stringObserver, times(1)).onNext("seven"); - verify(stringObserver, times(1)).onNext("eight"); - verify(stringObserver, times(1)).onNext("nine"); - } - - @Test - public void testErrorDelayed4() { - final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); - final Observable o2 = Observable.create(new TestErrorObservable("four", "five", "six")); - final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight")); - final Observable o4 = Observable.create(new TestErrorObservable("nine", null)); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4)); - m.subscribe(stringObserver); - - verify(stringObserver, times(1)).onError(any(NullPointerException.class)); - verify(stringObserver, never()).onCompleted(); - verify(stringObserver, times(1)).onNext("one"); - verify(stringObserver, times(1)).onNext("two"); - verify(stringObserver, times(1)).onNext("three"); - verify(stringObserver, times(1)).onNext("four"); - verify(stringObserver, times(1)).onNext("five"); - verify(stringObserver, times(1)).onNext("six"); - verify(stringObserver, times(1)).onNext("seven"); - verify(stringObserver, times(1)).onNext("eight"); - verify(stringObserver, times(1)).onNext("nine"); - } - - @Test - public void testErrorDelayed4WithThreading() { - final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", "three"); - final TestAsyncErrorObservable o2 = new TestAsyncErrorObservable("four", "five", "six"); - final TestAsyncErrorObservable o3 = new TestAsyncErrorObservable("seven", "eight"); - // throw the error at the very end so no onComplete will be called after it - final TestAsyncErrorObservable o4 = new TestAsyncErrorObservable("nine", null); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(mergeDelayError(Observable.create(o1), Observable.create(o2), Observable.create(o3), Observable.create(o4))); - m.subscribe(stringObserver); - - try { - o1.t.join(); - o2.t.join(); - o3.t.join(); - o4.t.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - verify(stringObserver, times(1)).onError(any(NullPointerException.class)); - verify(stringObserver, never()).onCompleted(); - verify(stringObserver, times(1)).onNext("one"); - verify(stringObserver, times(1)).onNext("two"); - verify(stringObserver, times(1)).onNext("three"); - verify(stringObserver, times(1)).onNext("four"); - verify(stringObserver, times(1)).onNext("five"); - verify(stringObserver, times(1)).onNext("six"); - verify(stringObserver, times(1)).onNext("seven"); - verify(stringObserver, times(1)).onNext("eight"); - verify(stringObserver, times(1)).onNext("nine"); - } - - @Test - public void testCompositeErrorDelayed1() { - final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final Observable o2 = Observable.create(new TestErrorObservable("one", "two", null)); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(mergeDelayError(o1, o2)); - m.subscribe(stringObserver); - - verify(stringObserver, times(1)).onError(any(CompositeException.class)); - verify(stringObserver, never()).onCompleted(); - verify(stringObserver, times(1)).onNext("one"); - verify(stringObserver, times(1)).onNext("two"); - verify(stringObserver, times(0)).onNext("three"); - verify(stringObserver, times(1)).onNext("four"); - verify(stringObserver, times(0)).onNext("five"); - verify(stringObserver, times(0)).onNext("six"); - } - - @Test - public void testCompositeErrorDelayed2() { - final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final Observable o2 = Observable.create(new TestErrorObservable("one", "two", null)); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(mergeDelayError(o1, o2)); - CaptureObserver w = new CaptureObserver(); - m.subscribe(w); - - assertNotNull(w.e); - if (w.e instanceof CompositeException) { - assertEquals(2, ((CompositeException) w.e).getExceptions().size()); - w.e.printStackTrace(); - } else { - fail("Expecting CompositeException"); - } - - } - - /** - * The unit tests below are from OperationMerge and should ensure the normal merge functionality is correct. - */ - - @Test - public void testMergeObservableOfObservables() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); - - Observable> observableOfObservables = Observable.create(new OnSubscribeFunc>() { - - @Override - public Subscription onSubscribe(Observer> observer) { - // simulate what would happen in an observable - observer.onNext(o1); - observer.onNext(o2); - observer.onCompleted(); - - return new Subscription() { - - @Override - public void unsubscribe() { - // unregister ... will never be called here since we are executing synchronously - } - - }; - } - - }); - Observable m = Observable.create(mergeDelayError(observableOfObservables)); - m.subscribe(stringObserver); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(1)).onCompleted(); - verify(stringObserver, times(2)).onNext("hello"); - } - - @Test - public void testMergeArray() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(mergeDelayError(o1, o2)); - m.subscribe(stringObserver); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(2)).onNext("hello"); - verify(stringObserver, times(1)).onCompleted(); - } - - @Test - public void testMergeList() { - final Observable o1 = Observable.create(new TestSynchronousObservable()); - final Observable o2 = Observable.create(new TestSynchronousObservable()); - List> listOfObservables = new ArrayList>(); - listOfObservables.add(o1); - listOfObservables.add(o2); - - Observable m = Observable.create(mergeDelayError(listOfObservables)); - m.subscribe(stringObserver); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(1)).onCompleted(); - verify(stringObserver, times(2)).onNext("hello"); - } - - @Test - public void testUnSubscribe() { - TestObservable tA = new TestObservable(); - TestObservable tB = new TestObservable(); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(mergeDelayError(Observable.create(tA), Observable.create(tB))); - Subscription s = m.subscribe(stringObserver); - - tA.sendOnNext("Aone"); - tB.sendOnNext("Bone"); - s.unsubscribe(); - tA.sendOnNext("Atwo"); - tB.sendOnNext("Btwo"); - tA.sendOnCompleted(); - tB.sendOnCompleted(); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(1)).onNext("Aone"); - verify(stringObserver, times(1)).onNext("Bone"); - assertTrue(tA.unsubscribed); - assertTrue(tB.unsubscribed); - verify(stringObserver, never()).onNext("Atwo"); - verify(stringObserver, never()).onNext("Btwo"); - verify(stringObserver, never()).onCompleted(); - } - - @Test - public void testMergeArrayWithThreading() { - final TestASynchronousObservable o1 = new TestASynchronousObservable(); - final TestASynchronousObservable o2 = new TestASynchronousObservable(); - - @SuppressWarnings("unchecked") - Observable m = Observable.create(mergeDelayError(Observable.create(o1), Observable.create(o2))); - m.subscribe(stringObserver); - - try { - o1.t.join(); - o2.t.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(2)).onNext("hello"); - verify(stringObserver, times(1)).onCompleted(); - } - - private static class TestSynchronousObservable implements OnSubscribeFunc { - - @Override - public Subscription onSubscribe(Observer observer) { - - observer.onNext("hello"); - observer.onCompleted(); - - return new Subscription() { - - @Override - public void unsubscribe() { - // unregister ... will never be called here since we are executing synchronously - } - - }; - } - } - - private static class TestASynchronousObservable implements OnSubscribeFunc { - Thread t; - - @Override - public Subscription onSubscribe(final Observer observer) { - t = new Thread(new Runnable() { - - @Override - public void run() { - observer.onNext("hello"); - observer.onCompleted(); - } - - }); - t.start(); - - return new Subscription() { - - @Override - public void unsubscribe() { - - } - - }; - } - } - - /** - * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. - */ - private static class TestObservable implements OnSubscribeFunc { - - Observer observer = null; - volatile boolean unsubscribed = false; - Subscription s = new Subscription() { - - @Override - public void unsubscribe() { - unsubscribed = true; - - } - - }; - - /* used to simulate subscription */ - public void sendOnCompleted() { - observer.onCompleted(); - } - - /* used to simulate subscription */ - public void sendOnNext(String value) { - observer.onNext(value); - } - - /* used to simulate subscription */ - @SuppressWarnings("unused") - public void sendOnError(Throwable e) { - observer.onError(e); - } - - @Override - public Subscription onSubscribe(final Observer observer) { - this.observer = observer; - return s; - } - } - - private static class TestErrorObservable implements OnSubscribeFunc { - - String[] valuesToReturn; - - TestErrorObservable(String... values) { - valuesToReturn = values; - } - - @Override - public Subscription onSubscribe(Observer observer) { - boolean errorThrown = false; - for (String s : valuesToReturn) { - if (s == null) { - System.out.println("throwing exception"); - observer.onError(new NullPointerException()); - errorThrown = true; - // purposefully not returning here so it will continue calling onNext - // so that we also test that we handle bad sequences like this - } else { - observer.onNext(s); - } - } - if (!errorThrown) { - observer.onCompleted(); - } - - return new Subscription() { - - @Override - public void unsubscribe() { - // unregister ... will never be called here since we are executing synchronously - } - - }; - } - } - - private static class TestAsyncErrorObservable implements OnSubscribeFunc { - - String[] valuesToReturn; - - TestAsyncErrorObservable(String... values) { - valuesToReturn = values; - } - - Thread t; - - @Override - public Subscription onSubscribe(final Observer observer) { - t = new Thread(new Runnable() { - - @Override - public void run() { - for (String s : valuesToReturn) { - if (s == null) { - System.out.println("throwing exception"); - try { - Thread.sleep(100); - } catch (Throwable e) { - - } - observer.onError(new NullPointerException()); - return; - } else { - observer.onNext(s); - } - } - System.out.println("subscription complete"); - observer.onCompleted(); - } - - }); - t.start(); - - return new Subscription() { - - @Override - public void unsubscribe() { - - } - - }; - } - } - - private static class CaptureObserver implements Observer { - volatile Throwable e; - - @Override - public void onCompleted() { - - } - - @Override - public void onError(Throwable e) { - this.e = e; - } - - @Override - public void onNext(String args) { - - } - - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationMostRecent.java b/rxjava-core/src/main/java/rx/operators/OperationMostRecent.java index b2eabb2893..003b54aaad 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMostRecent.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMostRecent.java @@ -15,20 +15,14 @@ */ package rx.operators; -import static org.junit.Assert.*; +import rx.Observable; +import rx.Observer; +import rx.util.Exceptions; import java.util.Iterator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import org.junit.Test; - -import rx.Observable; -import rx.Observer; -import rx.subjects.PublishSubject; -import rx.subjects.Subject; -import rx.util.Exceptions; - /** * Returns an Iterable that always returns the item most recently emitted by an Observable, or a * seed value if no item has yet been emitted. @@ -117,53 +111,4 @@ private T getRecentValue() { } } - - public static class UnitTest { - @Test - public void testMostRecent() { - Subject observable = PublishSubject.create(); - - Iterator it = mostRecent(observable, "default").iterator(); - - assertTrue(it.hasNext()); - assertEquals("default", it.next()); - assertEquals("default", it.next()); - - observable.onNext("one"); - assertTrue(it.hasNext()); - assertEquals("one", it.next()); - assertEquals("one", it.next()); - - observable.onNext("two"); - assertTrue(it.hasNext()); - assertEquals("two", it.next()); - assertEquals("two", it.next()); - - observable.onCompleted(); - assertFalse(it.hasNext()); - - } - - @Test(expected = TestException.class) - public void testMostRecentWithException() { - Subject observable = PublishSubject.create(); - - Iterator it = mostRecent(observable, "default").iterator(); - - assertTrue(it.hasNext()); - assertEquals("default", it.next()); - assertEquals("default", it.next()); - - observable.onError(new TestException()); - assertTrue(it.hasNext()); - - it.next(); - } - - private static class TestException extends RuntimeException { - private static final long serialVersionUID = 1L; - } - - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationMulticast.java b/rxjava-core/src/main/java/rx/operators/OperationMulticast.java index e24c24a91a..b93f9917f1 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMulticast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMulticast.java @@ -15,15 +15,10 @@ */ package rx.operators; -import static org.mockito.Mockito.*; - -import org.junit.Test; - import rx.Observable; import rx.Observer; import rx.Subscription; import rx.observables.ConnectableObservable; -import rx.subjects.PublishSubject; import rx.subjects.Subject; public class OperationMulticast { @@ -88,92 +83,4 @@ public void unsubscribe() { } - - public static class UnitTest { - - @Test - public void testMulticast() { - Subject source = PublishSubject.create(); - - ConnectableObservable multicasted = OperationMulticast.multicast(source, - PublishSubject.create()); - - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - multicasted.subscribe(observer); - - source.onNext("one"); - source.onNext("two"); - - multicasted.connect(); - - source.onNext("three"); - source.onNext("four"); - source.onCompleted(); - - verify(observer, never()).onNext("one"); - verify(observer, never()).onNext("two"); - verify(observer, times(1)).onNext("three"); - verify(observer, times(1)).onNext("four"); - verify(observer, times(1)).onCompleted(); - - } - - @Test - public void testMulticastConnectTwice() { - Subject source = PublishSubject.create(); - - ConnectableObservable multicasted = OperationMulticast.multicast(source, - PublishSubject.create()); - - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - multicasted.subscribe(observer); - - source.onNext("one"); - - multicasted.connect(); - multicasted.connect(); - - source.onNext("two"); - source.onCompleted(); - - verify(observer, never()).onNext("one"); - verify(observer, times(1)).onNext("two"); - verify(observer, times(1)).onCompleted(); - - } - - @Test - public void testMulticastDisconnect() { - Subject source = PublishSubject.create(); - - ConnectableObservable multicasted = OperationMulticast.multicast(source, - PublishSubject.create()); - - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - multicasted.subscribe(observer); - - source.onNext("one"); - - Subscription connection = multicasted.connect(); - source.onNext("two"); - - connection.unsubscribe(); - source.onNext("three"); - - multicasted.connect(); - source.onNext("four"); - source.onCompleted(); - - verify(observer, never()).onNext("one"); - verify(observer, times(1)).onNext("two"); - verify(observer, never()).onNext("three"); - verify(observer, times(1)).onNext("four"); - verify(observer, times(1)).onCompleted(); - - } - - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationNext.java b/rxjava-core/src/main/java/rx/operators/OperationNext.java index 43100e1096..a4812881e3 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationNext.java +++ b/rxjava-core/src/main/java/rx/operators/OperationNext.java @@ -15,32 +15,16 @@ */ package rx.operators; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import rx.Notification; +import rx.Observable; +import rx.Observer; +import rx.util.Exceptions; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; - -import rx.Notification; -import rx.Observable; -import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscription; -import rx.concurrency.Schedulers; -import rx.subjects.PublishSubject; -import rx.subjects.Subject; -import rx.subscriptions.Subscriptions; -import rx.util.Exceptions; /** * Returns an Iterable that blocks until the Observable emits another item, then returns that item. @@ -180,278 +164,4 @@ public Notification takeNext() throws InterruptedException { } } - - public static class UnitTest { - - private void fireOnNextInNewThread(final Subject o, final String value) { - new Thread() { - @Override - public void run() { - try { - Thread.sleep(500); - } catch (InterruptedException e) { - // ignore - } - o.onNext(value); - } - }.start(); - } - - private void fireOnErrorInNewThread(final Subject o) { - new Thread() { - @Override - public void run() { - try { - Thread.sleep(500); - } catch (InterruptedException e) { - // ignore - } - o.onError(new TestException()); - } - }.start(); - } - - - @Test - public void testNext() { - Subject obs = PublishSubject.create(); - Iterator it = next(obs).iterator(); - fireOnNextInNewThread(obs, "one"); - assertTrue(it.hasNext()); - assertEquals("one", it.next()); - - fireOnNextInNewThread(obs, "two"); - assertTrue(it.hasNext()); - assertEquals("two", it.next()); - - obs.onCompleted(); - assertFalse(it.hasNext()); - try { - it.next(); - fail("At the end of an iterator should throw a NoSuchElementException"); - } - catch(NoSuchElementException e){ - } - - // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException. - assertFalse(it.hasNext()); - try { - it.next(); - fail("At the end of an iterator should throw a NoSuchElementException"); - } - catch(NoSuchElementException e){ - } - } - - @Test - public void testNextWithError() { - Subject obs = PublishSubject.create(); - Iterator it = next(obs).iterator(); - fireOnNextInNewThread(obs, "one"); - assertTrue(it.hasNext()); - assertEquals("one", it.next()); - - fireOnErrorInNewThread(obs); - try { - it.hasNext(); - fail("Expected an TestException"); - } - catch(TestException e) { - } - - assertErrorAfterObservableFail(it); - } - - @Test - public void testNextWithEmpty() { - Observable obs = Observable.empty().observeOn(Schedulers.newThread()); - Iterator it = next(obs).iterator(); - - assertFalse(it.hasNext()); - try { - it.next(); - fail("At the end of an iterator should throw a NoSuchElementException"); - } - catch(NoSuchElementException e){ - } - - // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException. - assertFalse(it.hasNext()); - try { - it.next(); - fail("At the end of an iterator should throw a NoSuchElementException"); - } - catch(NoSuchElementException e){ - } - } - - @Test - public void testOnError() throws Throwable { - Subject obs = PublishSubject.create(); - Iterator it = next(obs).iterator(); - - obs.onError(new TestException()); - try { - it.hasNext(); - fail("Expected an TestException"); - } - catch(TestException e) { - // successful - } - - assertErrorAfterObservableFail(it); - } - - @Test - public void testOnErrorInNewThread() { - Subject obs = PublishSubject.create(); - Iterator it = next(obs).iterator(); - - fireOnErrorInNewThread(obs); - - try { - it.hasNext(); - fail("Expected an TestException"); - } - catch(TestException e) { - // successful - } - - assertErrorAfterObservableFail(it); - } - - private void assertErrorAfterObservableFail(Iterator it) { - // After the observable fails, hasNext and next always throw the exception. - try { - it.hasNext(); - fail("hasNext should throw a TestException"); - } - catch(TestException e){ - } - try { - it.next(); - fail("next should throw a TestException"); - } - catch(TestException e){ - } - } - - @Test - public void testNextWithOnlyUsingNextMethod() { - Subject obs = PublishSubject.create(); - Iterator it = next(obs).iterator(); - fireOnNextInNewThread(obs, "one"); - assertEquals("one", it.next()); - - fireOnNextInNewThread(obs, "two"); - assertEquals("two", it.next()); - - obs.onCompleted(); - try { - it.next(); - fail("At the end of an iterator should throw a NoSuchElementException"); - } - catch(NoSuchElementException e){ - } - } - - @Test - public void testNextWithCallingHasNextMultipleTimes() { - Subject obs = PublishSubject.create(); - Iterator it = next(obs).iterator(); - fireOnNextInNewThread(obs, "one"); - assertTrue(it.hasNext()); - assertTrue(it.hasNext()); - assertTrue(it.hasNext()); - assertTrue(it.hasNext()); - assertEquals("one", it.next()); - - obs.onCompleted(); - try { - it.next(); - fail("At the end of an iterator should throw a NoSuchElementException"); - } - catch(NoSuchElementException e){ - } - } - - @SuppressWarnings("serial") - private static class TestException extends RuntimeException { - - } - - /** - * Confirm that no buffering or blocking of the Observable onNext calls occurs and it just grabs the next emitted value. - * - * This results in output such as => a: 1 b: 2 c: 89 - * - * @throws Throwable - */ - @Test - public void testNoBufferingOrBlockingOfSequence() throws Throwable { - final CountDownLatch finished = new CountDownLatch(1); - final int COUNT = 30; - final CountDownLatch timeHasPassed = new CountDownLatch(COUNT); - final AtomicBoolean running = new AtomicBoolean(true); - final AtomicInteger count = new AtomicInteger(0); - final Observable obs = Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(final Observer o) { - new Thread(new Runnable() { - - @Override - public void run() { - try { - while (running.get()) { - o.onNext(count.incrementAndGet()); - timeHasPassed.countDown(); - } - o.onCompleted(); - } catch (Throwable e) { - o.onError(e); - } finally { - finished.countDown(); - } - } - }).start(); - return Subscriptions.empty(); - } - - }); - - Iterator it = next(obs).iterator(); - - assertTrue(it.hasNext()); - int a = it.next(); - assertTrue(it.hasNext()); - int b = it.next(); - // we should have a different value - assertTrue("a and b should be different", a != b); - - // wait for some time (if times out we are blocked somewhere so fail ... set very high for very slow, constrained machines) - timeHasPassed.await(8000, TimeUnit.MILLISECONDS); - - assertTrue(it.hasNext()); - int c = it.next(); - - assertTrue("c should not just be the next in sequence", c != (b + 1)); - assertTrue("expected that c [" + c + "] is higher than or equal to " + COUNT, c >= COUNT); - - assertTrue(it.hasNext()); - int d = it.next(); - assertTrue(d > c); - - // shut down the thread - running.set(false); - - finished.await(); - - assertFalse(it.hasNext()); - - System.out.println("a: " + a + " b: " + b + " c: " + c); - } - - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java b/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java index aef1cb9548..e1272b8152 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java @@ -15,27 +15,13 @@ */ package rx.operators; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; import rx.Subscription; import rx.concurrency.ImmediateScheduler; -import rx.concurrency.Schedulers; import rx.subscriptions.CompositeSubscription; -import rx.util.functions.Func2; /** * Asynchronously notify Observers on the specified Scheduler. @@ -69,58 +55,4 @@ public Subscription onSubscribe(final Observer observer) { } } } - - public static class UnitTest { - - /** - * This is testing a no-op path since it uses Schedulers.immediate() which will not do scheduling. - */ - @Test - @SuppressWarnings("unchecked") - public void testObserveOn() { - Observer observer = mock(Observer.class); - Observable.create(observeOn(Observable.from(1, 2, 3), Schedulers.immediate())).subscribe(observer); - - verify(observer, times(1)).onNext(1); - verify(observer, times(1)).onNext(2); - verify(observer, times(1)).onNext(3); - verify(observer, times(1)).onCompleted(); - } - - @Test - @SuppressWarnings("unchecked") - public void testOrdering() throws InterruptedException { - Observable obs = Observable.from("one", null, "two", "three", "four"); - - Observer observer = mock(Observer.class); - - InOrder inOrder = inOrder(observer); - - final CountDownLatch completedLatch = new CountDownLatch(1); - doAnswer(new Answer() { - - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - completedLatch.countDown(); - return null; - } - }).when(observer).onCompleted(); - - obs.observeOn(Schedulers.threadPoolForComputation()).subscribe(observer); - - if (!completedLatch.await(1000, TimeUnit.MILLISECONDS)) { - fail("timed out waiting"); - } - - inOrder.verify(observer, times(1)).onNext("one"); - inOrder.verify(observer, times(1)).onNext(null); - 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)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaFunction.java b/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaFunction.java index 28bf8be311..3f63463cf9 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaFunction.java +++ b/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaFunction.java @@ -15,24 +15,16 @@ */ package rx.operators; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; -import org.mockito.Mockito; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.subscriptions.Subscriptions; import rx.util.CompositeException; import rx.util.functions.Func1; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReference; + /** * Instruct an Observable to pass control to another Observable (the return value of a function) * rather than invoking onError if it encounters an error. @@ -122,155 +114,4 @@ public void unsubscribe() { }; } } - - public static class UnitTest { - - @Test - public void testResumeNextWithSynchronousExecution() { - final AtomicReference receivedException = new AtomicReference(); - Observable w = Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(Observer observer) { - observer.onNext("one"); - observer.onError(new Throwable("injected failure")); - return Subscriptions.empty(); - } - }); - - Func1> resume = new Func1>() { - - @Override - public Observable call(Throwable t1) { - receivedException.set(t1); - return Observable.from("twoResume", "threeResume"); - } - - }; - Observable observable = Observable.create(onErrorResumeNextViaFunction(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - assertNotNull(receivedException.get()); - } - - @Test - public void testResumeNextWithAsyncExecution() { - final AtomicReference receivedException = new AtomicReference(); - Subscription s = mock(Subscription.class); - TestObservable w = new TestObservable(s, "one"); - Func1> resume = new Func1>() { - - @Override - public Observable call(Throwable t1) { - receivedException.set(t1); - return Observable.from("twoResume", "threeResume"); - } - - }; - Observable observable = Observable.create(onErrorResumeNextViaFunction(Observable.create(w), resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - w.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - assertNotNull(receivedException.get()); - } - - /** - * Test that when a function throws an exception this is propagated through onError - */ - @Test - public void testFunctionThrowsError() { - Subscription s = mock(Subscription.class); - TestObservable w = new TestObservable(s, "one"); - Func1> resume = new Func1>() { - - @Override - public Observable call(Throwable t1) { - throw new RuntimeException("exception from function"); - } - - }; - Observable observable = Observable.create(onErrorResumeNextViaFunction(Observable.create(w), resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - w.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - // we should get the "one" value before the error - verify(aObserver, times(1)).onNext("one"); - - // we should have received an onError call on the Observer since the resume function threw an exception - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, times(0)).onCompleted(); - } - - private static class TestObservable implements OnSubscribeFunc { - - final Subscription s; - final String[] values; - Thread t = null; - - public TestObservable(Subscription s, String... values) { - this.s = s; - this.values = values; - } - - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { - - @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); - } - throw new RuntimeException("Forced Failure"); - } catch (Throwable e) { - observer.onError(e); - } - } - - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; - } - - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaObservable.java b/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaObservable.java index 3b49ce5f9c..8e09ce027a 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaObservable.java +++ b/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaObservable.java @@ -15,20 +15,12 @@ */ package rx.operators; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; -import org.mockito.Mockito; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.util.functions.Func1; + +import java.util.concurrent.atomic.AtomicReference; /** * Instruct an Observable to pass control to another Observable rather than invoking @@ -116,119 +108,4 @@ public void unsubscribe() { }; } } - - public static class UnitTest { - - @Test - public void testResumeNext() { - Subscription s = mock(Subscription.class); - // Trigger failure on second element - TestObservable f = new TestObservable(s, "one", "fail", "two", "three"); - Observable w = Observable.create(f); - Observable resume = Observable.from("twoResume", "threeResume"); - Observable observable = Observable.create(onErrorResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - } - - @Test - public void testMapResumeAsyncNext() { - Subscription sr = mock(Subscription.class); - // Trigger multiple failures - Observable w = Observable.from("one", "fail", "two", "three", "fail"); - // Resume Observable is async - TestObservable f = new TestObservable(sr, "twoResume", "threeResume"); - Observable resume = Observable.create(f); - - // Introduce map function that fails intermittently (Map does not prevent this when the observer is a - // rx.operator incl onErrorResumeNextViaObservable) - w = w.map(new Func1() { - public String call(String s) { - if ("fail".equals(s)) - throw new RuntimeException("Forced Failure"); - System.out.println("BadMapper:" + s); - return s; - } - }); - - Observable observable = Observable.create(onErrorResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - } - - private static class TestObservable implements OnSubscribeFunc { - - final Subscription s; - final String[] values; - Thread t = null; - - public TestObservable(Subscription s, String... values) { - this.s = s; - this.values = values; - } - - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { - - @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - if ("fail".equals(s)) - throw new RuntimeException("Forced Failure"); - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); - } - System.out.println("TestObservable onCompleted"); - observer.onCompleted(); - } catch (Throwable e) { - System.out.println("TestObservable onError: " + e); - observer.onError(e); - } - } - - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; - } - - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationOnErrorReturn.java b/rxjava-core/src/main/java/rx/operators/OperationOnErrorReturn.java index 02a268c04a..fd04d078b1 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationOnErrorReturn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationOnErrorReturn.java @@ -15,16 +15,6 @@ */ package rx.operators; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; -import org.mockito.Mockito; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -32,6 +22,9 @@ import rx.util.CompositeException; import rx.util.functions.Func1; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReference; + /** * Instruct an Observable to emit a particular item to its Observer's onNext method * rather than invoking onError if it encounters an error. @@ -125,119 +118,4 @@ public void unsubscribe() { }; } } - - public static class UnitTest { - - @Test - public void testResumeNext() { - Subscription s = mock(Subscription.class); - TestObservable f = new TestObservable(s, "one"); - Observable w = Observable.create(f); - final AtomicReference capturedException = new AtomicReference(); - - Observable observable = Observable.create(onErrorReturn(w, new Func1() { - - @Override - public String call(Throwable e) { - capturedException.set(e); - return "failure"; - } - - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("failure"); - assertNotNull(capturedException.get()); - } - - /** - * Test that when a function throws an exception this is propagated through onError - */ - @Test - public void testFunctionThrowsError() { - Subscription s = mock(Subscription.class); - TestObservable f = new TestObservable(s, "one"); - Observable w = Observable.create(f); - final AtomicReference capturedException = new AtomicReference(); - - Observable observable = Observable.create(onErrorReturn(w, new Func1() { - - @Override - public String call(Throwable e) { - capturedException.set(e); - throw new RuntimeException("exception from function"); - } - - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - // we should get the "one" value before the error - verify(aObserver, times(1)).onNext("one"); - - // we should have received an onError call on the Observer since the resume function threw an exception - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, times(0)).onCompleted(); - assertNotNull(capturedException.get()); - } - - private static class TestObservable implements OnSubscribeFunc { - - final Subscription s; - final String[] values; - Thread t = null; - - public TestObservable(Subscription s, String... values) { - this.s = s; - this.values = values; - } - - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { - - @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); - } - throw new RuntimeException("Forced Failure"); - } catch (Throwable e) { - observer.onError(e); - } - } - - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; - } - - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationOnExceptionResumeNextViaObservable.java b/rxjava-core/src/main/java/rx/operators/OperationOnExceptionResumeNextViaObservable.java index 9642ed9269..20e8fce867 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationOnExceptionResumeNextViaObservable.java +++ b/rxjava-core/src/main/java/rx/operators/OperationOnExceptionResumeNextViaObservable.java @@ -15,20 +15,12 @@ */ package rx.operators; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; -import org.mockito.Mockito; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.util.functions.Func1; + +import java.util.concurrent.atomic.AtomicReference; /** * Instruct an Observable to pass control to another Observable rather than invoking @@ -123,216 +115,4 @@ public void unsubscribe() { }; } } - - public static class UnitTest { - - @Test - public void testResumeNextWithException() { - Subscription s = mock(Subscription.class); - // Trigger failure on second element - TestObservable f = new TestObservable(s, "one", "EXCEPTION", "two", "three"); - Observable w = Observable.create(f); - Observable resume = Observable.from("twoResume", "threeResume"); - Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testResumeNextWithRuntimeException() { - Subscription s = mock(Subscription.class); - // Trigger failure on second element - TestObservable f = new TestObservable(s, "one", "RUNTIMEEXCEPTION", "two", "three"); - Observable w = Observable.create(f); - Observable resume = Observable.from("twoResume", "threeResume"); - Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testThrowablePassesThru() { - Subscription s = mock(Subscription.class); - // Trigger failure on second element - TestObservable f = new TestObservable(s, "one", "THROWABLE", "two", "three"); - Observable w = Observable.create(f); - Observable resume = Observable.from("twoResume", "threeResume"); - Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, never()).onNext("twoResume"); - verify(aObserver, never()).onNext("threeResume"); - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testErrorPassesThru() { - Subscription s = mock(Subscription.class); - // Trigger failure on second element - TestObservable f = new TestObservable(s, "one", "ERROR", "two", "three"); - Observable w = Observable.create(f); - Observable resume = Observable.from("twoResume", "threeResume"); - Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, never()).onNext("twoResume"); - verify(aObserver, never()).onNext("threeResume"); - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testMapResumeAsyncNext() { - Subscription sr = mock(Subscription.class); - // Trigger multiple failures - Observable w = Observable.from("one", "fail", "two", "three", "fail"); - // Resume Observable is async - TestObservable f = new TestObservable(sr, "twoResume", "threeResume"); - Observable resume = Observable.create(f); - - // Introduce map function that fails intermittently (Map does not prevent this when the observer is a - // rx.operator incl onErrorResumeNextViaObservable) - w = w.map(new Func1() { - public String call(String s) { - if ("fail".equals(s)) - throw new RuntimeException("Forced Failure"); - System.out.println("BadMapper:" + s); - return s; - } - }); - - Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - // if the thread gets started (which it shouldn't if it's working correctly) - if (f.t != null) { - f.t.join(); - } - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - private static class TestObservable implements OnSubscribeFunc { - - final Subscription s; - final String[] values; - Thread t = null; - - public TestObservable(Subscription s, String... values) { - this.s = s; - this.values = values; - } - - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { - - @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - if ("EXCEPTION".equals(s)) - throw new Exception("Forced Exception"); - else if ("RUNTIMEEXCEPTION".equals(s)) - throw new RuntimeException("Forced RuntimeException"); - else if ("ERROR".equals(s)) - throw new Error("Forced Error"); - else if ("THROWABLE".equals(s)) - throw new Throwable("Forced Throwable"); - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); - } - System.out.println("TestObservable onCompleted"); - observer.onCompleted(); - } catch (Throwable e) { - System.out.println("TestObservable onError: " + e); - observer.onError(e); - } - } - - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; - } - - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationParallel.java b/rxjava-core/src/main/java/rx/operators/OperationParallel.java index 125fd2d291..25a955e616 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationParallel.java +++ b/rxjava-core/src/main/java/rx/operators/OperationParallel.java @@ -15,20 +15,15 @@ */ package rx.operators; -import static org.junit.Assert.*; - -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; - import rx.Observable; import rx.Scheduler; import rx.concurrency.Schedulers; import rx.observables.GroupedObservable; -import rx.util.functions.Action1; import rx.util.functions.Func0; import rx.util.functions.Func1; +import java.util.concurrent.atomic.AtomicInteger; + /** * Identifies unit of work that can be executed in parallel on a given Scheduler. */ @@ -61,39 +56,4 @@ public Observable call(GroupedObservable group) { } }); } - - public static class UnitTest { - - @Test - public void testParallel() { - int NUM = 1000; - final AtomicInteger count = new AtomicInteger(); - Observable.range(1, NUM).parallel( - new Func1, Observable>() { - - @Override - public Observable call(Observable o) { - return o.map(new Func1() { - - @Override - public Integer[] call(Integer t) { - return new Integer[] { t, t * 99 }; - } - - }); - } - }).toBlockingObservable().forEach(new Action1() { - - @Override - public void call(Integer[] v) { - count.incrementAndGet(); - System.out.println("V: " + v[0] + " R: " + v[1] + " Thread: " + Thread.currentThread()); - } - - }); - - // just making sure we finish and get the number we expect - assertEquals(NUM, count.get()); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationRetry.java b/rxjava-core/src/main/java/rx/operators/OperationRetry.java index 0f664ee3ec..864caf3c4e 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationRetry.java +++ b/rxjava-core/src/main/java/rx/operators/OperationRetry.java @@ -15,13 +15,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; -import org.mockito.InOrder; import rx.Observable; import rx.Observable.OnSubscribeFunc; @@ -31,9 +24,10 @@ import rx.concurrency.Schedulers; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.MultipleAssignmentSubscription; -import rx.subscriptions.Subscriptions; import rx.util.functions.Func2; +import java.util.concurrent.atomic.AtomicInteger; + public class OperationRetry { private static final int INFINITE_RETRY = -1; @@ -103,104 +97,4 @@ public void onNext(T v) { } } - - public static class UnitTest { - - @Test - public void testOriginFails() { - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(2)); - origin.subscribe(observer); - - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext("beginningEveryTime"); - inOrder.verify(observer, times(1)).onError(any(RuntimeException.class)); - inOrder.verify(observer, never()).onNext("onSuccessOnly"); - inOrder.verify(observer, never()).onCompleted(); - } - - @Test - public void testRetryFail() { - int NUM_RETRIES = 1; - int NUM_FAILURES = 2; - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); - Observable.create(retry(origin, NUM_RETRIES)).subscribe(observer); - - InOrder inOrder = inOrder(observer); - // should show 2 attempts (first time fail, second time (1st retry) fail) - inOrder.verify(observer, times(1 + NUM_RETRIES)).onNext("beginningEveryTime"); - // should only retry once, fail again and emit onError - inOrder.verify(observer, times(1)).onError(any(RuntimeException.class)); - // no success - inOrder.verify(observer, never()).onNext("onSuccessOnly"); - inOrder.verify(observer, never()).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testRetrySuccess() { - int NUM_RETRIES = 3; - int NUM_FAILURES = 2; - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); - Observable.create(retry(origin, NUM_RETRIES)).subscribe(observer); - - InOrder inOrder = inOrder(observer); - // should show 3 attempts - inOrder.verify(observer, times(1 + NUM_FAILURES)).onNext("beginningEveryTime"); - // should have no errors - inOrder.verify(observer, never()).onError(any(Throwable.class)); - // should have a single success - inOrder.verify(observer, times(1)).onNext("onSuccessOnly"); - // should have a single successful onCompleted - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testInfiniteRetry() { - int NUM_FAILURES = 20; - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); - Observable.create(retry(origin)).subscribe(observer); - - InOrder inOrder = inOrder(observer); - // should show 3 attempts - inOrder.verify(observer, times(1 + NUM_FAILURES)).onNext("beginningEveryTime"); - // should have no errors - inOrder.verify(observer, never()).onError(any(Throwable.class)); - // should have a single success - inOrder.verify(observer, times(1)).onNext("onSuccessOnly"); - // should have a single successful onCompleted - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - public static class FuncWithErrors implements OnSubscribeFunc { - - private final int numFailures; - private final AtomicInteger count = new AtomicInteger(0); - - FuncWithErrors(int count) { - this.numFailures = count; - } - - @Override - public Subscription onSubscribe(Observer o) { - o.onNext("beginningEveryTime"); - if (count.incrementAndGet() <= numFailures) { - o.onError(new RuntimeException("forced failure: " + count.get())); - } else { - o.onNext("onSuccessOnly"); - o.onCompleted(); - } - return Subscriptions.empty(); - } - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationSample.java b/rxjava-core/src/main/java/rx/operators/OperationSample.java index 3be189f32b..32c58b1f04 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSample.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSample.java @@ -15,28 +15,19 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; import rx.Subscription; import rx.concurrency.Schedulers; -import rx.concurrency.TestScheduler; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + /** * Returns an Observable that emits the results of sampling the items emitted by the source * Observable at a specified time interval. @@ -122,79 +113,4 @@ public void call() { }); } } - - public static class UnitTest { - private TestScheduler scheduler; - private Observer observer; - - @Before - @SuppressWarnings("unchecked") // due to mocking - public void before() { - scheduler = new TestScheduler(); - observer = mock(Observer.class); - } - - @Test - public void testSample() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(final Observer observer1) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer1.onNext(1L); - } - }, 1, TimeUnit.SECONDS); - scheduler.schedule(new Action0() { - @Override - public void call() { - observer1.onNext(2L); - } - }, 2, TimeUnit.SECONDS); - scheduler.schedule(new Action0() { - @Override - public void call() { - observer1.onCompleted(); - } - }, 3, TimeUnit.SECONDS); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSample.sample(source, 400L, TimeUnit.MILLISECONDS, scheduler)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(800L, TimeUnit.MILLISECONDS); - verify(observer, never()).onNext(any(Long.class)); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(1200L, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext(1L); - verify(observer, never()).onNext(2L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(1600L, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext(1L); - verify(observer, never()).onNext(2L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(2000L, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(1L); - inOrder.verify(observer, times(1)).onNext(2L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(3000L, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(1L); - inOrder.verify(observer, times(2)).onNext(2L); - verify(observer, times(1)).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationScan.java b/rxjava-core/src/main/java/rx/operators/OperationScan.java index 513646b8ef..4d0cfc95d0 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationScan.java +++ b/rxjava-core/src/main/java/rx/operators/OperationScan.java @@ -15,13 +15,6 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.MockitoAnnotations; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -171,92 +164,4 @@ public void onCompleted() { observer.onCompleted(); } } - - public static class UnitTest { - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testScanIntegersWithInitialValue() { - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - - Observable observable = Observable.from(1, 2, 3); - - Observable m = Observable.create(scan(observable, "", new Func2() { - - @Override - public String call(String s, Integer n) { - return s + n.toString(); - } - - })); - m.subscribe(observer); - - verify(observer, never()).onError(any(Throwable.class)); - verify(observer, times(1)).onNext(""); - verify(observer, times(1)).onNext("1"); - verify(observer, times(1)).onNext("12"); - verify(observer, times(1)).onNext("123"); - verify(observer, times(4)).onNext(anyString()); - verify(observer, times(1)).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - } - - @Test - public void testScanIntegersWithoutInitialValue() { - @SuppressWarnings("unchecked") - Observer Observer = mock(Observer.class); - - Observable observable = Observable.from(1, 2, 3); - - Observable m = Observable.create(scan(observable, new Func2() { - - @Override - public Integer call(Integer t1, Integer t2) { - return t1 + t2; - } - - })); - m.subscribe(Observer); - - verify(Observer, never()).onError(any(Throwable.class)); - verify(Observer, never()).onNext(0); - verify(Observer, times(1)).onNext(1); - verify(Observer, times(1)).onNext(3); - verify(Observer, times(1)).onNext(6); - verify(Observer, times(3)).onNext(anyInt()); - verify(Observer, times(1)).onCompleted(); - verify(Observer, never()).onError(any(Throwable.class)); - } - - @Test - public void testScanIntegersWithoutInitialValueAndOnlyOneValue() { - @SuppressWarnings("unchecked") - Observer Observer = mock(Observer.class); - - Observable observable = Observable.from(1); - - Observable m = Observable.create(scan(observable, new Func2() { - - @Override - public Integer call(Integer t1, Integer t2) { - return t1 + t2; - } - - })); - m.subscribe(Observer); - - verify(Observer, never()).onError(any(Throwable.class)); - verify(Observer, never()).onNext(0); - verify(Observer, times(1)).onNext(1); - verify(Observer, times(1)).onNext(anyInt()); - verify(Observer, times(1)).onCompleted(); - verify(Observer, never()).onError(any(Throwable.class)); - } - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationSkip.java b/rxjava-core/src/main/java/rx/operators/OperationSkip.java index 11fb10f1b5..63b873d826 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSkip.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSkip.java @@ -15,18 +15,13 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; +import java.util.concurrent.atomic.AtomicInteger; + /** * Returns an Observable that skips the first num items emitted by the source * Observable. @@ -112,39 +107,4 @@ public void onNext(T args) { } } - - public static class UnitTest { - - @Test - public void testSkip1() { - Observable w = Observable.from("one", "two", "three"); - Observable skip = Observable.create(skip(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - skip.subscribe(aObserver); - verify(aObserver, never()).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkip2() { - Observable w = Observable.from("one", "two", "three"); - Observable skip = Observable.create(skip(w, 1)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - skip.subscribe(aObserver); - verify(aObserver, never()).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java b/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java index 10da0c06b4..9bd8253392 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java @@ -15,25 +15,15 @@ */ package rx.operators; -import static org.mockito.Matchers.any; -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.Deque; -import java.util.LinkedList; -import java.util.concurrent.locks.ReentrantLock; - -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; +import java.util.Deque; +import java.util.LinkedList; +import java.util.concurrent.locks.ReentrantLock; + /** * Bypasses a specified number of elements at the end of an observable sequence. */ @@ -133,92 +123,4 @@ public void onNext(T value) { })); } } - - public static class UnitTest { - - @Test - public void testSkipLastEmpty() { - Observable w = Observable.empty(); - Observable observable = Observable.create(skipLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(any(String.class)); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkipLast1() { - Observable w = Observable.from("one", "two", "three"); - Observable observable = Observable.create(skipLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - InOrder inOrder = inOrder(aObserver); - observable.subscribe(aObserver); - inOrder.verify(aObserver, never()).onNext("two"); - inOrder.verify(aObserver, never()).onNext("three"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkipLast2() { - Observable w = Observable.from("one", "two"); - Observable observable = Observable.create(skipLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(any(String.class)); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkipLastWithZeroCount() { - Observable w = Observable.from("one", "two"); - Observable observable = Observable.create(skipLast(w, 0)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkipLastWithNull() { - Observable w = Observable.from("one", null, "two"); - Observable observable = Observable.create(skipLast(w, 1)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext(null); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkipLastWithNegativeCount() { - Observable w = Observable.from("one"); - Observable observable = Observable.create(skipLast(w, -1)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(any(String.class)); - verify(aObserver, times(1)).onError( - any(IndexOutOfBoundsException.class)); - verify(aObserver, never()).onCompleted(); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationSkipWhile.java b/rxjava-core/src/main/java/rx/operators/OperationSkipWhile.java index 8277f80fc2..bf1528d8a2 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSkipWhile.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSkipWhile.java @@ -15,16 +15,6 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; -import static rx.Observable.create; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -32,6 +22,9 @@ import rx.util.functions.Func1; import rx.util.functions.Func2; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + /** * Skips any emitted source items as long as the specified condition holds true. Emits all further source items * as soon as the condition becomes false. @@ -102,92 +95,4 @@ public void onNext(T next) { } } - - public static class UnitTest { - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - - private static final Func1 LESS_THAN_FIVE = new Func1() { - @Override - public Boolean call(Integer v) { - if (v == 42) throw new RuntimeException("that's not the answer to everything!"); - return v < 5; - } - }; - - private static final Func2 INDEX_LESS_THAN_THREE = new Func2() { - @Override - public Boolean call(Integer value, Integer index) { - return index < 3; - } - }; - - @Test - public void testSkipWithIndex() { - Observable src = Observable.from(1, 2, 3, 4, 5); - create(skipWhileWithIndex(src, INDEX_LESS_THAN_THREE)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext(4); - inOrder.verify(w, times(1)).onNext(5); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testSkipEmpty() { - Observable src = Observable.empty(); - create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); - verify(w, never()).onNext(anyInt()); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testSkipEverything() { - Observable src = Observable.from(1, 2, 3, 4, 3, 2, 1); - create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); - verify(w, never()).onNext(anyInt()); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testSkipNothing() { - Observable src = Observable.from(5, 3, 1); - create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext(5); - inOrder.verify(w, times(1)).onNext(3); - inOrder.verify(w, times(1)).onNext(1); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testSkipSome() { - Observable src = Observable.from(1, 2, 3, 4, 5, 3, 1, 5); - create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext(5); - inOrder.verify(w, times(1)).onNext(3); - inOrder.verify(w, times(1)).onNext(1); - inOrder.verify(w, times(1)).onNext(5); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testSkipError() { - Observable src = Observable.from(1, 2, 42, 5, 3, 1); - create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, never()).onNext(anyInt()); - inOrder.verify(w, never()).onCompleted(); - inOrder.verify(w, times(1)).onError(any(RuntimeException.class)); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java b/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java index 6f75a32b57..f1cac4fe9d 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java @@ -15,17 +15,11 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; import rx.Subscription; -import rx.concurrency.Schedulers; import rx.util.functions.Action0; import rx.util.functions.Func2; @@ -79,30 +73,4 @@ public void call() { }); } } - - public static class UnitTest { - - @Test - @SuppressWarnings("unchecked") - public void testSubscribeOn() { - Observable w = Observable.from(1, 2, 3); - - Scheduler scheduler = spy(OperatorTester.UnitTest.forwardingScheduler(Schedulers.immediate())); - - Observer observer = mock(Observer.class); - Subscription subscription = Observable.create(subscribeOn(w, scheduler)).subscribe(observer); - - verify(scheduler, times(1)).schedule(isNull(), any(Func2.class)); - subscription.unsubscribe(); - verify(scheduler, times(1)).schedule(any(Action0.class)); - verifyNoMoreInteractions(scheduler); - - verify(observer, times(1)).onNext(1); - verify(observer, times(1)).onNext(2); - verify(observer, times(1)).onNext(3); - verify(observer, times(1)).onCompleted(); - } - - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationSum.java b/rxjava-core/src/main/java/rx/operators/OperationSum.java index 5892a00520..cc34f79520 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSum.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSum.java @@ -15,12 +15,7 @@ */ package rx.operators; -import static org.mockito.Mockito.*; - -import org.junit.Test; - import rx.Observable; -import rx.Observer; import rx.util.functions.Func2; /** @@ -63,103 +58,4 @@ public Double call(Double accu, Double next) { } }); } - - public static class UnitTest { - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - @SuppressWarnings("unchecked") - Observer wl = mock(Observer.class); - @SuppressWarnings("unchecked") - Observer wf = mock(Observer.class); - @SuppressWarnings("unchecked") - Observer wd = mock(Observer.class); - - @Test - public void testSumOfAFewInts() throws Throwable { - Observable src = Observable.from(1, 2, 3, 4, 5); - sum(src).subscribe(w); - - verify(w, times(1)).onNext(anyInt()); - verify(w).onNext(15); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testEmptySum() throws Throwable { - Observable src = Observable.empty(); - sum(src).subscribe(w); - - 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 testSumOfAFewLongs() throws Throwable { - Observable src = Observable.from(1L, 2L, 3L, 4L, 5L); - sumLongs(src).subscribe(wl); - - verify(wl, times(1)).onNext(anyLong()); - verify(wl).onNext(15L); - verify(wl, never()).onError(any(Throwable.class)); - verify(wl, times(1)).onCompleted(); - } - - @Test - public void testEmptySumLongs() throws Throwable { - Observable src = Observable.empty(); - sumLongs(src).subscribe(wl); - - verify(wl, times(1)).onNext(anyLong()); - verify(wl).onNext(0L); - verify(wl, never()).onError(any(Throwable.class)); - verify(wl, times(1)).onCompleted(); - } - - @Test - public void testSumOfAFewFloats() throws Throwable { - Observable src = Observable.from(1.0f); - sumFloats(src).subscribe(wf); - - verify(wf, times(1)).onNext(anyFloat()); - verify(wf).onNext(1.0f); - verify(wf, never()).onError(any(Throwable.class)); - verify(wf, times(1)).onCompleted(); - } - - @Test - public void testEmptySumFloats() throws Throwable { - Observable src = Observable.empty(); - sumFloats(src).subscribe(wf); - - verify(wf, times(1)).onNext(anyFloat()); - verify(wf).onNext(0.0f); - verify(wf, never()).onError(any(Throwable.class)); - verify(wf, times(1)).onCompleted(); - } - - @Test - public void testSumOfAFewDoubles() throws Throwable { - Observable src = Observable.from(0.0d, 1.0d, 0.5d); - sumDoubles(src).subscribe(wd); - - verify(wd, times(1)).onNext(anyDouble()); - verify(wd).onNext(1.5d); - verify(wd, never()).onError(any(Throwable.class)); - verify(wd, times(1)).onCompleted(); - } - - @Test - public void testEmptySumDoubles() throws Throwable { - Observable src = Observable.empty(); - sumDoubles(src).subscribe(wd); - - verify(wd, times(1)).onNext(anyDouble()); - verify(wd).onNext(0.0d); - verify(wd, never()).onError(any(Throwable.class)); - verify(wd, times(1)).onCompleted(); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationSwitch.java b/rxjava-core/src/main/java/rx/operators/OperationSwitch.java index c40864de84..25d94a714a 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSwitch.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSwitch.java @@ -15,29 +15,12 @@ */ package rx.operators; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -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.concurrent.TimeUnit; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.concurrency.TestScheduler; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.MultipleAssignmentSubscription; -import rx.subscriptions.Subscriptions; -import rx.util.functions.Action0; import rx.util.functions.Func1; /** @@ -182,355 +165,4 @@ public void onCompleted() { } } - - public static class UnitTest { - - private TestScheduler scheduler; - private Observer observer; - - @Before - @SuppressWarnings("unchecked") - public void before() { - scheduler = new TestScheduler(); - observer = mock(Observer.class); - } - - @Test - public void testSwitchWhenOuterCompleteBeforeInner() { - Observable> source = Observable.create(new OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 50, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 70, "one"); - publishNext(observer, 100, "two"); - publishCompleted(observer, 200); - return Subscriptions.empty(); - } - })); - publishCompleted(observer, 60); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(2)).onNext(anyString()); - inOrder.verify(observer, times(1)).onCompleted(); - } - - @Test - public void testSwitchWhenInnerCompleteBeforeOuter() { - Observable> source = Observable.create(new OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 10, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 0, "one"); - publishNext(observer, 10, "two"); - publishCompleted(observer, 20); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 100, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 0, "three"); - publishNext(observer, 10, "four"); - publishCompleted(observer, 20); - return Subscriptions.empty(); - } - })); - publishCompleted(observer, 200); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(150, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onCompleted(); - 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"); - - scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - inOrder.verify(observer, times(1)).onCompleted(); - } - - @Test - public void testSwitchWithComplete() { - Observable> source = Observable.create(new OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 50, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 60, "one"); - publishNext(observer, 100, "two"); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 200, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 0, "three"); - publishNext(observer, 100, "four"); - return Subscriptions.empty(); - } - })); - - publishCompleted(observer, 250); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("one"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(175, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("two"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(225, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("three"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("four"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - } - - @Test - public void testSwitchWithError() { - Observable> source = Observable.create(new OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 50, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 50, "one"); - publishNext(observer, 100, "two"); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 200, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 0, "three"); - publishNext(observer, 100, "four"); - return Subscriptions.empty(); - } - })); - - publishError(observer, 250, new TestException()); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("one"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(175, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("two"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(225, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("three"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - verify(observer, never()).onCompleted(); - verify(observer, times(1)).onError(any(TestException.class)); - } - - @Test - public void testSwitchWithSubsequenceComplete() { - Observable> source = Observable.create(new OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 50, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 50, "one"); - publishNext(observer, 100, "two"); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 130, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishCompleted(observer, 0); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 150, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 50, "three"); - return Subscriptions.empty(); - } - })); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("one"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("three"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - } - - @Test - public void testSwitchWithSubsequenceError() { - Observable> source = Observable.create(new OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 50, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 50, "one"); - publishNext(observer, 100, "two"); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 130, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishError(observer, 0, new TestException()); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 150, Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 50, "three"); - return Subscriptions.empty(); - } - })); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("one"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext("three"); - verify(observer, never()).onCompleted(); - verify(observer, times(1)).onError(any(TestException.class)); - } - - private void publishCompleted(final Observer observer, long delay) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onCompleted(); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void publishError(final Observer observer, long delay, final Throwable error) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onError(error); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void publishNext(final Observer observer, long delay, final T value) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onNext(value); - } - }, delay, TimeUnit.MILLISECONDS); - } - - @SuppressWarnings("serial") - private class TestException extends Throwable { - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java b/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java index b7cd689102..4ec205f156 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java @@ -15,12 +15,6 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import org.junit.Test; -import org.mockito.Mockito; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -102,197 +96,4 @@ public Subscription onSubscribe(Observer observer) { } } - - public static class UnitTest { - - /** - * Ensure onCompleted can not be called after an Unsubscribe - */ - @Test - public void testOnCompletedAfterUnSubscribe() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - ws.unsubscribe(); - t.sendOnCompleted(); - - verify(w, times(1)).onNext("one"); - verify(w, Mockito.never()).onCompleted(); - } - - /** - * Ensure onNext can not be called after an Unsubscribe - */ - @Test - public void testOnNextAfterUnSubscribe() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - ws.unsubscribe(); - t.sendOnNext("two"); - - verify(w, times(1)).onNext("one"); - verify(w, Mockito.never()).onNext("two"); - } - - /** - * Ensure onError can not be called after an Unsubscribe - */ - @Test - public void testOnErrorAfterUnSubscribe() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - ws.unsubscribe(); - t.sendOnError(new RuntimeException("bad")); - - verify(w, times(1)).onNext("one"); - verify(w, Mockito.never()).onError(any(Throwable.class)); - } - - /** - * Ensure onNext can not be called after onError - */ - @Test - public void testOnNextAfterOnError() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - @SuppressWarnings("unused") - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - t.sendOnError(new RuntimeException("bad")); - t.sendOnNext("two"); - - verify(w, times(1)).onNext("one"); - verify(w, times(1)).onError(any(Throwable.class)); - verify(w, Mockito.never()).onNext("two"); - } - - /** - * Ensure onCompleted can not be called after onError - */ - @Test - public void testOnCompletedAfterOnError() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - @SuppressWarnings("unused") - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - t.sendOnError(new RuntimeException("bad")); - t.sendOnCompleted(); - - verify(w, times(1)).onNext("one"); - verify(w, times(1)).onError(any(Throwable.class)); - verify(w, Mockito.never()).onCompleted(); - } - - /** - * Ensure onNext can not be called after onCompleted - */ - @Test - public void testOnNextAfterOnCompleted() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - @SuppressWarnings("unused") - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - t.sendOnCompleted(); - t.sendOnNext("two"); - - verify(w, times(1)).onNext("one"); - verify(w, Mockito.never()).onNext("two"); - verify(w, times(1)).onCompleted(); - verify(w, Mockito.never()).onError(any(Throwable.class)); - } - - /** - * Ensure onError can not be called after onCompleted - */ - @Test - public void testOnErrorAfterOnCompleted() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - @SuppressWarnings("unused") - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - t.sendOnCompleted(); - t.sendOnError(new RuntimeException("bad")); - - verify(w, times(1)).onNext("one"); - verify(w, times(1)).onCompleted(); - verify(w, Mockito.never()).onError(any(Throwable.class)); - } - - /** - * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. - */ - private static class TestObservable implements OnSubscribeFunc { - - Observer observer = null; - - public TestObservable(Subscription s) { - } - - /* used to simulate subscription */ - public void sendOnCompleted() { - observer.onCompleted(); - } - - /* used to simulate subscription */ - public void sendOnNext(String value) { - observer.onNext(value); - } - - /* used to simulate subscription */ - public void sendOnError(Throwable e) { - observer.onError(e); - } - - @Override - public Subscription onSubscribe(final Observer observer) { - this.observer = observer; - return new Subscription() { - - @Override - public void unsubscribe() { - // going to do nothing to pretend I'm a bad Observable that keeps allowing events to be sent - } - - }; - } - - } - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationTake.java b/rxjava-core/src/main/java/rx/operators/OperationTake.java index 061e930ff0..6448a628db 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTake.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTake.java @@ -15,30 +15,13 @@ */ package rx.operators; -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.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 static org.mockito.Mockito.verifyNoMoreInteractions; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.subscriptions.Subscriptions; -import rx.util.functions.Func1; + +import java.util.concurrent.atomic.AtomicInteger; /** * Returns an Observable that emits the first num items emitted by the source @@ -178,208 +161,4 @@ public void onNext(T args) { } } - - public static class UnitTest { - - @Test - public void testTake1() { - Observable w = Observable.from("one", "two", "three"); - Observable take = Observable.create(take(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTake2() { - Observable w = Observable.from("one", "two", "three"); - Observable take = Observable.create(take(w, 1)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test(expected = IllegalArgumentException.class) - public void testTakeWithError() { - Observable.from(1, 2, 3).take(1).map(new Func1() { - public Integer call(Integer t1) { - throw new IllegalArgumentException("some error"); - } - }).toBlockingObservable().single(); - } - - @Test - public void testTakeWithErrorHappeningInOnNext() { - Observable w = Observable.from(1, 2, 3).take(2).map(new Func1() { - public Integer call(Integer t1) { - throw new IllegalArgumentException("some error"); - } - }); - - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - w.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError(any(IllegalArgumentException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testTakeWithErrorHappeningInTheLastOnNext() { - Observable w = Observable.from(1, 2, 3).take(1).map(new Func1() { - public Integer call(Integer t1) { - throw new IllegalArgumentException("some error"); - } - }); - - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - w.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError(any(IllegalArgumentException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testTakeDoesntLeakErrors() { - Observable source = Observable.create(new OnSubscribeFunc() - { - @Override - public Subscription onSubscribe(Observer observer) - { - observer.onNext("one"); - observer.onError(new Throwable("test failed")); - return Subscriptions.empty(); - } - }); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - - Observable.create(take(source, 1)).subscribe(aObserver); - - verify(aObserver, times(1)).onNext("one"); - // even though onError is called we take(1) so shouldn't see it - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testTakeZeroDoesntLeakError() { - final AtomicBoolean subscribed = new AtomicBoolean(false); - final AtomicBoolean unSubscribed = new AtomicBoolean(false); - Observable source = Observable.create(new OnSubscribeFunc() - { - @Override - public Subscription onSubscribe(Observer observer) - { - subscribed.set(true); - observer.onError(new Throwable("test failed")); - return new Subscription() - { - @Override - public void unsubscribe() - { - unSubscribed.set(true); - } - }; - } - }); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - - Observable.create(take(source, 0)).subscribe(aObserver); - assertTrue("source subscribed", subscribed.get()); - assertTrue("source unsubscribed", unSubscribed.get()); - - verify(aObserver, never()).onNext(anyString()); - // even though onError is called we take(0) so shouldn't see it - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testUnsubscribeAfterTake() { - final Subscription s = mock(Subscription.class); - TestObservableFunc f = new TestObservableFunc(s, "one", "two", "three"); - Observable w = Observable.create(f); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - Observable take = Observable.create(take(w, 1)); - take.subscribe(aObserver); - - // wait for the Observable to complete - try { - f.t.join(); - } catch (Throwable e) { - e.printStackTrace(); - fail(e.getMessage()); - } - - System.out.println("TestObservable thread finished"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, times(1)).onCompleted(); - verify(s, times(1)).unsubscribe(); - verifyNoMoreInteractions(aObserver); - } - - private static class TestObservableFunc implements OnSubscribeFunc { - - final Subscription s; - final String[] values; - Thread t = null; - - public TestObservableFunc(Subscription s, String... values) { - this.s = s; - this.values = values; - } - - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { - - @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); - } - observer.onCompleted(); - } catch (Throwable e) { - throw new RuntimeException(e); - } - } - - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; - } - - } - - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java b/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java index e83f5dc432..d608552315 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java @@ -15,25 +15,15 @@ */ package rx.operators; -import static org.mockito.Matchers.any; -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.Deque; -import java.util.LinkedList; -import java.util.concurrent.locks.ReentrantLock; - -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; +import java.util.Deque; +import java.util.LinkedList; +import java.util.concurrent.locks.ReentrantLock; + /** * Returns an Observable that emits the last count items emitted by the source * Observable. @@ -129,93 +119,4 @@ public void onNext(T value) { } } - - public static class UnitTest { - - @Test - public void testTakeLastEmpty() { - Observable w = Observable.empty(); - Observable take = Observable.create(takeLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, never()).onNext(any(String.class)); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeLast1() { - Observable w = Observable.from("one", "two", "three"); - Observable take = Observable.create(takeLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - InOrder inOrder = inOrder(aObserver); - take.subscribe(aObserver); - inOrder.verify(aObserver, times(1)).onNext("two"); - inOrder.verify(aObserver, times(1)).onNext("three"); - verify(aObserver, never()).onNext("one"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeLast2() { - Observable w = Observable.from("one"); - Observable take = Observable.create(takeLast(w, 10)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeLastWithZeroCount() { - Observable w = Observable.from("one"); - Observable take = Observable.create(takeLast(w, 0)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, never()).onNext("one"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeLastWithNull() { - Observable w = Observable.from("one", null, "three"); - Observable take = Observable.create(takeLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, never()).onNext("one"); - verify(aObserver, times(1)).onNext(null); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeLastWithNegativeCount() { - Observable w = Observable.from("one"); - Observable take = Observable.create(takeLast(w, -1)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, never()).onNext("one"); - verify(aObserver, times(1)).onError( - any(IndexOutOfBoundsException.class)); - verify(aObserver, never()).onCompleted(); - } - - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationTakeUntil.java b/rxjava-core/src/main/java/rx/operators/OperationTakeUntil.java index 841190f023..4f344d72a1 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTakeUntil.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTakeUntil.java @@ -15,10 +15,6 @@ */ package rx.operators; -import static org.mockito.Mockito.*; - -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -139,160 +135,4 @@ public void onNext(E args) { }); } } - - public static class UnitTest { - - @Test - @SuppressWarnings("unchecked") - public void testTakeUntil() { - Subscription sSource = mock(Subscription.class); - Subscription sOther = mock(Subscription.class); - TestObservable source = new TestObservable(sSource); - TestObservable other = new TestObservable(sOther); - - Observer result = mock(Observer.class); - Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); - stringObservable.subscribe(result); - source.sendOnNext("one"); - source.sendOnNext("two"); - other.sendOnNext("three"); - source.sendOnNext("four"); - source.sendOnCompleted(); - other.sendOnCompleted(); - - verify(result, times(1)).onNext("one"); - verify(result, times(1)).onNext("two"); - verify(result, times(0)).onNext("three"); - verify(result, times(0)).onNext("four"); - verify(sSource, times(1)).unsubscribe(); - verify(sOther, times(1)).unsubscribe(); - - } - - @Test - @SuppressWarnings("unchecked") - public void testTakeUntilSourceCompleted() { - Subscription sSource = mock(Subscription.class); - Subscription sOther = mock(Subscription.class); - TestObservable source = new TestObservable(sSource); - TestObservable other = new TestObservable(sOther); - - Observer result = mock(Observer.class); - Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); - stringObservable.subscribe(result); - source.sendOnNext("one"); - source.sendOnNext("two"); - source.sendOnCompleted(); - - verify(result, times(1)).onNext("one"); - verify(result, times(1)).onNext("two"); - verify(sSource, times(1)).unsubscribe(); - verify(sOther, times(1)).unsubscribe(); - - } - - @Test - @SuppressWarnings("unchecked") - public void testTakeUntilSourceError() { - Subscription sSource = mock(Subscription.class); - Subscription sOther = mock(Subscription.class); - TestObservable source = new TestObservable(sSource); - TestObservable other = new TestObservable(sOther); - Throwable error = new Throwable(); - - Observer result = mock(Observer.class); - Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); - stringObservable.subscribe(result); - source.sendOnNext("one"); - source.sendOnNext("two"); - source.sendOnError(error); - - verify(result, times(1)).onNext("one"); - verify(result, times(1)).onNext("two"); - verify(result, times(1)).onError(error); - verify(sSource, times(1)).unsubscribe(); - verify(sOther, times(1)).unsubscribe(); - - } - - @Test - @SuppressWarnings("unchecked") - public void testTakeUntilOtherError() { - Subscription sSource = mock(Subscription.class); - Subscription sOther = mock(Subscription.class); - TestObservable source = new TestObservable(sSource); - TestObservable other = new TestObservable(sOther); - Throwable error = new Throwable(); - - Observer result = mock(Observer.class); - Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); - stringObservable.subscribe(result); - source.sendOnNext("one"); - source.sendOnNext("two"); - other.sendOnError(error); - - verify(result, times(1)).onNext("one"); - verify(result, times(1)).onNext("two"); - verify(result, times(1)).onError(error); - verify(result, times(0)).onCompleted(); - verify(sSource, times(1)).unsubscribe(); - verify(sOther, times(1)).unsubscribe(); - - } - - @Test - @SuppressWarnings("unchecked") - public void testTakeUntilOtherCompleted() { - Subscription sSource = mock(Subscription.class); - Subscription sOther = mock(Subscription.class); - TestObservable source = new TestObservable(sSource); - TestObservable other = new TestObservable(sOther); - - Observer result = mock(Observer.class); - Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); - stringObservable.subscribe(result); - source.sendOnNext("one"); - source.sendOnNext("two"); - other.sendOnCompleted(); - - verify(result, times(1)).onNext("one"); - verify(result, times(1)).onNext("two"); - verify(result, times(0)).onCompleted(); - verify(sSource, times(0)).unsubscribe(); - verify(sOther, times(0)).unsubscribe(); - - } - - private static class TestObservable implements OnSubscribeFunc { - - Observer observer = null; - Subscription s; - - public TestObservable(Subscription s) { - this.s = s; - } - - /* used to simulate subscription */ - public void sendOnCompleted() { - observer.onCompleted(); - } - - /* used to simulate subscription */ - public void sendOnNext(String value) { - observer.onNext(value); - } - - /* used to simulate subscription */ - public void sendOnError(Throwable e) { - observer.onError(e); - } - - @Override - public Subscription onSubscribe(final Observer observer) { - this.observer = observer; - return s; - } - } - - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationTakeWhile.java b/rxjava-core/src/main/java/rx/operators/OperationTakeWhile.java index 5b833b22a1..b56029bcae 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTakeWhile.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTakeWhile.java @@ -15,24 +15,15 @@ */ package rx.operators; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.subjects.PublishSubject; -import rx.subjects.Subject; -import rx.subscriptions.Subscriptions; import rx.util.functions.Func1; import rx.util.functions.Func2; +import java.util.concurrent.atomic.AtomicInteger; + /** * Returns an Observable that emits items emitted by the source Observable as long as a specified * condition is true. @@ -151,207 +142,4 @@ public void onNext(T args) { } } - - public static class UnitTest { - - @Test - public void testTakeWhile1() { - Observable w = Observable.from(1, 2, 3); - Observable take = Observable.create(takeWhile(w, new Func1() - { - @Override - public Boolean call(Integer input) - { - return input < 3; - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, times(1)).onNext(1); - verify(aObserver, times(1)).onNext(2); - verify(aObserver, never()).onNext(3); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeWhileOnSubject1() { - Subject s = PublishSubject.create(); - Observable take = Observable.create(takeWhile(s, new Func1() - { - @Override - public Boolean call(Integer input) - { - return input < 3; - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - - s.onNext(1); - s.onNext(2); - s.onNext(3); - s.onNext(4); - s.onNext(5); - s.onCompleted(); - - verify(aObserver, times(1)).onNext(1); - verify(aObserver, times(1)).onNext(2); - verify(aObserver, never()).onNext(3); - verify(aObserver, never()).onNext(4); - verify(aObserver, never()).onNext(5); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeWhile2() { - Observable w = Observable.from("one", "two", "three"); - Observable take = Observable.create(takeWhileWithIndex(w, new Func2() - { - @Override - public Boolean call(String input, Integer index) - { - return index < 2; - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeWhileDoesntLeakErrors() { - Observable source = Observable.create(new OnSubscribeFunc() - { - @Override - public Subscription onSubscribe(Observer observer) - { - observer.onNext("one"); - observer.onError(new Throwable("test failed")); - return Subscriptions.empty(); - } - }); - - Observable.create(takeWhile(source, new Func1() - { - @Override - public Boolean call(String s) - { - return false; - } - })).toBlockingObservable().last(); - } - - @Test - public void testTakeWhileProtectsPredicateCall() { - TestObservable source = new TestObservable(mock(Subscription.class), "one"); - final RuntimeException testException = new RuntimeException("test exception"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - Observable take = Observable.create(takeWhile(Observable.create(source), new Func1() - { - @Override - public Boolean call(String s) - { - throw testException; - } - })); - take.subscribe(aObserver); - - // wait for the Observable to complete - try { - source.t.join(); - } catch (Throwable e) { - e.printStackTrace(); - fail(e.getMessage()); - } - - verify(aObserver, never()).onNext(any(String.class)); - verify(aObserver, times(1)).onError(testException); - } - - @Test - public void testUnsubscribeAfterTake() { - Subscription s = mock(Subscription.class); - TestObservable w = new TestObservable(s, "one", "two", "three"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - Observable take = Observable.create(takeWhileWithIndex(Observable.create(w), new Func2() - { - @Override - public Boolean call(String s, Integer index) - { - return index < 1; - } - })); - take.subscribe(aObserver); - - // wait for the Observable to complete - try { - w.t.join(); - } catch (Throwable e) { - e.printStackTrace(); - fail(e.getMessage()); - } - - System.out.println("TestObservable thread finished"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(s, times(1)).unsubscribe(); - } - - private static class TestObservable implements OnSubscribeFunc { - - final Subscription s; - final String[] values; - Thread t = null; - - public TestObservable(Subscription s, String... values) { - this.s = s; - this.values = values; - } - - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { - - @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); - } - observer.onCompleted(); - } catch (Throwable e) { - throw new RuntimeException(e); - } - } - - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; - } - - } - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java b/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java index 4c3a1ea8d4..ebc6bf55d9 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java +++ b/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java @@ -15,27 +15,17 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; import rx.Subscription; import rx.concurrency.Schedulers; -import rx.concurrency.TestScheduler; -import rx.subscriptions.Subscriptions; -import rx.util.functions.Action0; import rx.util.functions.Func1; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + /** * Throttle by windowing a stream and returning the first value in each window. */ @@ -94,104 +84,4 @@ public Boolean call(T value) { } }; } - - public static class UnitTest { - - private TestScheduler scheduler; - private Observer observer; - - @Before - @SuppressWarnings("unchecked") - public void before() { - scheduler = new TestScheduler(); - observer = mock(Observer.class); - } - - @Test - public void testThrottlingWithCompleted() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 100, "one"); // publish as it's first - publishNext(observer, 300, "two"); // skip as it's last within the first 400 - publishNext(observer, 900, "three"); // publish - publishNext(observer, 905, "four"); // skip - publishCompleted(observer, 1000); // Should be published as soon as the timeout expires. - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationThrottleFirst.throttleFirst(source, 400, TimeUnit.MILLISECONDS, scheduler)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("one"); - inOrder.verify(observer, times(0)).onNext("two"); - inOrder.verify(observer, times(1)).onNext("three"); - inOrder.verify(observer, times(0)).onNext("four"); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testThrottlingWithError() { - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - Exception error = new TestException(); - publishNext(observer, 100, "one"); // Should be published since it is first - publishNext(observer, 200, "two"); // Should be skipped since onError will arrive before the timeout expires - publishError(observer, 300, error); // Should be published as soon as the timeout expires. - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationThrottleFirst.throttleFirst(source, 400, TimeUnit.MILLISECONDS, scheduler)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(400, TimeUnit.MILLISECONDS); - inOrder.verify(observer).onNext("one"); - inOrder.verify(observer).onError(any(TestException.class)); - inOrder.verifyNoMoreInteractions(); - } - - private void publishCompleted(final Observer observer, long delay) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onCompleted(); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void publishError(final Observer observer, long delay, final Exception error) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onError(error); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void publishNext(final Observer observer, long delay, final T value) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onNext(value); - } - }, delay, TimeUnit.MILLISECONDS); - } - - @SuppressWarnings("serial") - private class TestException extends Exception { - } - - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java b/rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java index 7b70818bc1..874cb9dd39 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java @@ -15,25 +15,12 @@ */ package rx.operators; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.times; - -import java.util.concurrent.TimeUnit; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; import rx.Subscription; import rx.concurrency.Schedulers; -import rx.concurrency.TestScheduler; -import rx.subjects.PublishSubject; import rx.util.TimeInterval; /** @@ -93,48 +80,4 @@ public void onError(Throwable e) { observer.onCompleted(); } } - - public static class UnitTest { - - private static final TimeUnit TIME_UNIT = TimeUnit.MILLISECONDS; - - @Mock - private Observer> observer; - - private TestScheduler testScheduler; - private PublishSubject subject; - private Observable> observable; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - testScheduler = new TestScheduler(); - subject = PublishSubject.create(); - observable = subject.timeInterval(testScheduler); - } - - @Test - public void testTimeInterval() { - InOrder inOrder = inOrder(observer); - observable.subscribe(observer); - - testScheduler.advanceTimeBy(1000, TIME_UNIT); - subject.onNext(1); - testScheduler.advanceTimeBy(2000, TIME_UNIT); - subject.onNext(2); - testScheduler.advanceTimeBy(3000, TIME_UNIT); - subject.onNext(3); - subject.onCompleted(); - - inOrder.verify(observer, times(1)).onNext( - new TimeInterval(1000, 1)); - inOrder.verify(observer, times(1)).onNext( - new TimeInterval(2000, 2)); - inOrder.verify(observer, times(1)).onNext( - new TimeInterval(3000, 3)); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationToObservableFuture.java b/rxjava-core/src/main/java/rx/operators/OperationToObservableFuture.java index 958aa9ad3f..af2f0b1a3f 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableFuture.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToObservableFuture.java @@ -15,18 +15,14 @@ */ package rx.operators; -import static org.mockito.Mockito.*; - -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import org.junit.Test; - import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.subscriptions.Subscriptions; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + /** * Converts a Future into an Observable. *

@@ -39,7 +35,7 @@ * Observable.subscribe(Observer) does nothing. */ public class OperationToObservableFuture { - private static class ToObservableFuture implements OnSubscribeFunc { + static class ToObservableFuture implements OnSubscribeFunc { private final Future that; private final Long time; private final TimeUnit unit; @@ -82,41 +78,4 @@ public static OnSubscribeFunc toObservableFuture(final Future OnSubscribeFunc toObservableFuture(final Future that, long time, TimeUnit unit) { return new ToObservableFuture(that, time, unit); } - - @SuppressWarnings("unchecked") - public static class UnitTest { - @Test - public void testSuccess() throws Exception { - Future future = mock(Future.class); - Object value = new Object(); - when(future.get()).thenReturn(value); - ToObservableFuture ob = new ToObservableFuture(future); - Observer o = mock(Observer.class); - - Subscription sub = ob.onSubscribe(o); - sub.unsubscribe(); - - verify(o, times(1)).onNext(value); - verify(o, times(1)).onCompleted(); - verify(o, never()).onError(null); - verify(future, never()).cancel(true); - } - - @Test - public void testFailure() throws Exception { - Future future = mock(Future.class); - RuntimeException e = new RuntimeException(); - when(future.get()).thenThrow(e); - ToObservableFuture ob = new ToObservableFuture(future); - Observer o = mock(Observer.class); - - Subscription sub = ob.onSubscribe(o); - sub.unsubscribe(); - - verify(o, never()).onNext(null); - verify(o, never()).onCompleted(); - verify(o, times(1)).onError(e); - verify(future, never()).cancel(true); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationToObservableIterable.java b/rxjava-core/src/main/java/rx/operators/OperationToObservableIterable.java index bebc31550a..a8a970bdd8 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableIterable.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToObservableIterable.java @@ -15,15 +15,6 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; - -import org.junit.Test; -import org.mockito.Mockito; - -import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; @@ -59,21 +50,4 @@ public Subscription onSubscribe(Observer observer) { return Subscriptions.empty(); } } - - public static class UnitTest { - - @Test - public void testIterable() { - Observable observable = Observable.create(toObservableIterable(Arrays. asList("one", "two", "three"))); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java b/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java index f6bc080211..c798cf8cc1 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java @@ -15,22 +15,15 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; - -import org.junit.Test; -import org.mockito.Mockito; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + /** * Returns an Observable that emits a single item, a list composed of all the items emitted by the * source Observable. @@ -93,44 +86,4 @@ public void onCompleted() { }); } } - - public static class UnitTest { - - @Test - public void testList() { - Observable w = Observable.from("one", "two", "three"); - Observable> observable = Observable.create(toObservableList(w)); - - @SuppressWarnings("unchecked") - Observer> aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(Arrays.asList("one", "two", "three")); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testListMultipleObservers() { - Observable w = Observable.from("one", "two", "three"); - Observable> observable = Observable.create(toObservableList(w)); - - @SuppressWarnings("unchecked") - Observer> o1 = mock(Observer.class); - observable.subscribe(o1); - - @SuppressWarnings("unchecked") - Observer> o2 = mock(Observer.class); - observable.subscribe(o2); - - List expected = Arrays.asList("one", "two", "three"); - - verify(o1, times(1)).onNext(expected); - verify(o1, Mockito.never()).onError(any(Throwable.class)); - verify(o1, times(1)).onCompleted(); - - verify(o2, times(1)).onNext(expected); - verify(o2, Mockito.never()).onError(any(Throwable.class)); - verify(o2, times(1)).onCompleted(); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java b/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java index 655f833a5d..cf3ffac636 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java @@ -15,25 +15,18 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Func2; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; -import org.junit.Test; -import org.mockito.Mockito; - -import rx.Observable; -import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscription; -import rx.util.functions.Func2; - /** * Return an Observable that emits the items emitted by the source Observable, in a sorted order * (each item emitted by the Observable must implement Comparable with respect to all other items @@ -142,41 +135,4 @@ public Integer call(Object t1, Object t2) { } } - - public static class UnitTest { - - @Test - public void testSortedList() { - Observable w = Observable.from(1, 3, 2, 5, 4); - Observable> observable = Observable.create(toSortedList(w)); - - @SuppressWarnings("unchecked") - Observer> aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(Arrays.asList(1, 2, 3, 4, 5)); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSortedListWithCustomFunction() { - Observable w = Observable.from(1, 3, 2, 5, 4); - Observable> observable = Observable.create(toSortedList(w, new Func2() { - - @Override - public Integer call(Integer t1, Integer t2) { - return t2 - t1; - } - - })); - - @SuppressWarnings("unchecked") - Observer> aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(Arrays.asList(5, 4, 3, 2, 1)); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationWindow.java b/rxjava-core/src/main/java/rx/operators/OperationWindow.java index 5cffda9661..61d1839f32 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationWindow.java +++ b/rxjava-core/src/main/java/rx/operators/OperationWindow.java @@ -15,32 +15,19 @@ */ package rx.operators; -import static org.junit.Assert.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.junit.Before; -import org.junit.Test; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; import rx.Subscription; import rx.concurrency.Schedulers; -import rx.concurrency.TestScheduler; -import rx.subscriptions.Subscriptions; import rx.util.Closing; -import rx.util.Closings; import rx.util.Opening; -import rx.util.Openings; -import rx.util.functions.Action0; -import rx.util.functions.Action1; import rx.util.functions.Func0; import rx.util.functions.Func1; +import java.util.concurrent.TimeUnit; + public final class OperationWindow extends ChunkedOperation { public static Func0> windowMaker() { @@ -369,295 +356,4 @@ public Observable getContents() { return Observable.from(contents); } } - - public static class UnitTest { - - private TestScheduler scheduler; - - @Before - public void before() { - scheduler = new TestScheduler(); - } - - private static List> toLists(Observable> observable) { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); - - observable.subscribe(new Action1>() { - @Override - public void call(Observable tObservable) { - tObservable.subscribe(new Action1() { - @Override - public void call(T t) { - list.add(t); - } - }); - lists.add(new ArrayList(list)); - list.clear(); - } - }); - return lists; - } - - @Test - public void testNonOverlappingWindows() { - Observable subject = Observable.from("one", "two", "three", "four", "five"); - Observable> windowed = Observable.create(window(subject, 3)); - - List> windows = toLists(windowed); - - assertEquals(2, windows.size()); - assertEquals(list("one", "two", "three"), windows.get(0)); - assertEquals(list("four", "five"), windows.get(1)); - } - - @Test - public void testSkipAndCountGaplessEindows() { - Observable subject = Observable.from("one", "two", "three", "four", "five"); - Observable> windowed = Observable.create(window(subject, 3, 3)); - - List> windows = toLists(windowed); - - assertEquals(2, windows.size()); - assertEquals(list("one", "two", "three"), windows.get(0)); - assertEquals(list("four", "five"), windows.get(1)); - } - - @Test - public void testOverlappingWindows() { - Observable subject = Observable.from("zero", "one", "two", "three", "four", "five"); - Observable> windowed = Observable.create(window(subject, 3, 1)); - - List> windows = toLists(windowed); - - assertEquals(6, windows.size()); - assertEquals(list("zero", "one", "two"), windows.get(0)); - assertEquals(list("one", "two", "three"), windows.get(1)); - assertEquals(list("two", "three", "four"), windows.get(2)); - assertEquals(list("three", "four", "five"), windows.get(3)); - assertEquals(list("four", "five"), windows.get(4)); - assertEquals(list("five"), windows.get(5)); - } - - @Test - public void testSkipAndCountWindowsWithGaps() { - Observable subject = Observable.from("one", "two", "three", "four", "five"); - Observable> windowed = Observable.create(window(subject, 2, 3)); - - List> windows = toLists(windowed); - - assertEquals(2, windows.size()); - assertEquals(list("one", "two"), windows.get(0)); - assertEquals(list("four", "five"), windows.get(1)); - } - - @Test - public void testTimedAndCount() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); - - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 10); - push(observer, "two", 90); - push(observer, "three", 110); - push(observer, "four", 190); - push(observer, "five", 210); - complete(observer, 250); - return Subscriptions.empty(); - } - }); - - Observable> windowed = Observable.create(window(source, 100, TimeUnit.MILLISECONDS, 2, scheduler)); - windowed.subscribe(observeWindow(list, lists)); - - scheduler.advanceTimeTo(100, TimeUnit.MILLISECONDS); - assertEquals(1, lists.size()); - assertEquals(lists.get(0), list("one", "two")); - - scheduler.advanceTimeTo(200, TimeUnit.MILLISECONDS); - assertEquals(2, lists.size()); - assertEquals(lists.get(1), list("three", "four")); - - scheduler.advanceTimeTo(300, TimeUnit.MILLISECONDS); - assertEquals(3, lists.size()); - assertEquals(lists.get(2), list("five")); - } - - @Test - public void testTimed() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); - - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 98); - push(observer, "two", 99); - push(observer, "three", 100); - push(observer, "four", 101); - push(observer, "five", 102); - complete(observer, 150); - return Subscriptions.empty(); - } - }); - - Observable> windowed = Observable.create(window(source, 100, TimeUnit.MILLISECONDS, scheduler)); - windowed.subscribe(observeWindow(list, lists)); - - scheduler.advanceTimeTo(101, TimeUnit.MILLISECONDS); - assertEquals(1, lists.size()); - assertEquals(lists.get(0), list("one", "two", "three")); - - scheduler.advanceTimeTo(201, TimeUnit.MILLISECONDS); - assertEquals(2, lists.size()); - assertEquals(lists.get(1), list("four", "five")); - } - - @Test - public void testObservableBasedOpenerAndCloser() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); - - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 10); - push(observer, "two", 60); - push(observer, "three", 110); - push(observer, "four", 160); - push(observer, "five", 210); - complete(observer, 500); - return Subscriptions.empty(); - } - }); - - Observable openings = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, Openings.create(), 50); - push(observer, Openings.create(), 200); - complete(observer, 250); - return Subscriptions.empty(); - } - }); - - Func1> closer = new Func1>() { - @Override - public Observable call(Opening opening) { - return Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, Closings.create(), 100); - complete(observer, 101); - return Subscriptions.empty(); - } - }); - } - }; - - Observable> windowed = Observable.create(window(source, openings, closer)); - windowed.subscribe(observeWindow(list, lists)); - - scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); - assertEquals(2, lists.size()); - assertEquals(lists.get(0), list("two", "three")); - assertEquals(lists.get(1), list("five")); - } - - @Test - public void testObservableBasedCloser() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); - - Observable source = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 10); - push(observer, "two", 60); - push(observer, "three", 110); - push(observer, "four", 160); - push(observer, "five", 210); - complete(observer, 250); - return Subscriptions.empty(); - } - }); - - Func0> closer = new Func0>() { - @Override - public Observable call() { - return Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, Closings.create(), 100); - complete(observer, 101); - return Subscriptions.empty(); - } - }); - } - }; - - Observable> windowed = Observable.create(window(source, closer)); - windowed.subscribe(observeWindow(list, lists)); - - scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); - assertEquals(3, lists.size()); - assertEquals(lists.get(0), list("one", "two")); - assertEquals(lists.get(1), list("three", "four")); - assertEquals(lists.get(2), list("five")); - } - - private List list(String... args) { - List list = new ArrayList(); - for (String arg : args) { - list.add(arg); - } - return list; - } - - private void push(final Observer observer, final T value, int delay) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onNext(value); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void complete(final Observer observer, int delay) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onCompleted(); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private Action1> observeWindow(final List list, final List> lists) { - return new Action1>() { - @Override - public void call(Observable stringObservable) { - stringObservable.subscribe(new Observer() { - @Override - public void onCompleted() { - lists.add(new ArrayList(list)); - list.clear(); - } - - @Override - public void onError(Throwable e) { - fail(e.getMessage()); - } - - @Override - public void onNext(String args) { - list.add(args); - } - }); - } - }; - } - - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationZip.java b/rxjava-core/src/main/java/rx/operators/OperationZip.java index 517a1faecc..eb07f87738 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationZip.java +++ b/rxjava-core/src/main/java/rx/operators/OperationZip.java @@ -15,33 +15,15 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.subscriptions.Subscriptions; -import rx.util.functions.Func2; -import rx.util.functions.Func3; -import rx.util.functions.Func4; -import rx.util.functions.Func5; -import rx.util.functions.Func6; -import rx.util.functions.Func7; -import rx.util.functions.Func8; -import rx.util.functions.Func9; -import rx.util.functions.FuncN; -import rx.util.functions.Functions; +import rx.util.functions.*; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; /** * Returns an Observable that emits the results of a function applied to sets of items emitted, in @@ -159,7 +141,7 @@ public static OnSubscribeFunc zip(Iterable> ws, F /* * ThreadSafe */ - private static class ZipObserver implements Observer { + static class ZipObserver implements Observer { final Observable w; final Aggregator a; private final SafeObservableSubscription subscription = new SafeObservableSubscription(); @@ -204,7 +186,7 @@ public void onNext(T args) { * * @param */ - private static class Aggregator implements OnSubscribeFunc { + static class Aggregator implements OnSubscribeFunc { private volatile SynchronizedObserver observer; private final FuncN zipFunction; @@ -228,7 +210,7 @@ public Aggregator(FuncN zipFunction) { * * @param w */ - private void addObserver(ZipObserver w) { + void addObserver(ZipObserver w) { // initialize this ZipObserver observers.add(w); receivedValuesPerObserver.put(w, new ConcurrentLinkedQueue()); @@ -361,566 +343,4 @@ private void stop() { } } - - public static class UnitTest { - - @SuppressWarnings("unchecked") - @Test - public void testCollectionSizeDifferentThanFunction() { - FuncN zipr = Functions.fromFunc(getConcatStringIntegerIntArrayZipr()); - //Func3 - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - - @SuppressWarnings("rawtypes") - Collection ws = java.util.Collections.singleton(Observable.from("one", "two")); - Observable w = Observable.create(zip(ws, zipr)); - w.subscribe(aObserver); - - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, never()).onNext(any(String.class)); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testZippingDifferentLengthObservableSequences1() { - Observer w = mock(Observer.class); - - TestObservable w1 = new TestObservable(); - TestObservable w2 = new TestObservable(); - TestObservable w3 = new TestObservable(); - - Observable zipW = Observable.create(zip(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsZipr())); - zipW.subscribe(w); - - /* simulate sending data */ - // once for w1 - w1.observer.onNext("1a"); - w1.observer.onCompleted(); - // twice for w2 - w2.observer.onNext("2a"); - w2.observer.onNext("2b"); - w2.observer.onCompleted(); - // 4 times for w3 - w3.observer.onNext("3a"); - w3.observer.onNext("3b"); - w3.observer.onNext("3c"); - w3.observer.onNext("3d"); - w3.observer.onCompleted(); - - /* we should have been called 1 time on the Observer */ - InOrder inOrder = inOrder(w); - inOrder.verify(w).onNext("1a2a3a"); - - inOrder.verify(w, times(1)).onCompleted(); - } - - @Test - public void testZippingDifferentLengthObservableSequences2() { - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - - TestObservable w1 = new TestObservable(); - TestObservable w2 = new TestObservable(); - TestObservable w3 = new TestObservable(); - - Observable zipW = Observable.create(zip(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsZipr())); - zipW.subscribe(w); - - /* simulate sending data */ - // 4 times for w1 - w1.observer.onNext("1a"); - w1.observer.onNext("1b"); - w1.observer.onNext("1c"); - w1.observer.onNext("1d"); - w1.observer.onCompleted(); - // twice for w2 - w2.observer.onNext("2a"); - w2.observer.onNext("2b"); - w2.observer.onCompleted(); - // 1 times for w3 - w3.observer.onNext("3a"); - w3.observer.onCompleted(); - - /* we should have been called 1 time on the Observer */ - InOrder inOrder = inOrder(w); - inOrder.verify(w).onNext("1a2a3a"); - - inOrder.verify(w, times(1)).onCompleted(); - - } - - /** - * Testing internal private logic due to the complexity so I want to use TDD to test as a I build it rather than relying purely on the overall functionality expected by the public methods. - */ - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorSimple() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - - InOrder inOrder = inOrder(aObserver); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("helloworld"); - - a.next(r1, "hello "); - a.next(r2, "again"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("hello again"); - - a.complete(r1); - a.complete(r2); - - inOrder.verify(aObserver, never()).onNext(anyString()); - verify(aObserver, times(1)).onCompleted(); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorDifferentSizedResultsWithOnComplete() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - a.complete(r2); - - InOrder inOrder = inOrder(aObserver); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("helloworld"); - - a.next(r1, "hi"); - a.complete(r1); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, times(1)).onCompleted(); - inOrder.verify(aObserver, never()).onNext(anyString()); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregateMultipleTypes() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - a.complete(r2); - - InOrder inOrder = inOrder(aObserver); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("helloworld"); - - a.next(r1, "hi"); - a.complete(r1); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, times(1)).onCompleted(); - inOrder.verify(aObserver, never()).onNext(anyString()); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregate3Types() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - ZipObserver r3 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - a.addObserver(r3); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, 2); - a.next(r3, new int[] { 5, 6, 7 }); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("hello2[5, 6, 7]"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorsWithDifferentSizesAndTiming() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "one"); - a.next(r1, "two"); - a.next(r1, "three"); - a.next(r2, "A"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("oneA"); - - a.next(r1, "four"); - a.complete(r1); - a.next(r2, "B"); - verify(aObserver, times(1)).onNext("twoB"); - a.next(r2, "C"); - verify(aObserver, times(1)).onNext("threeC"); - a.next(r2, "D"); - verify(aObserver, times(1)).onNext("fourD"); - a.next(r2, "E"); - verify(aObserver, never()).onNext("E"); - a.complete(r2); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorError() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("helloworld"); - - a.error(r1, new RuntimeException("")); - a.next(r1, "hello"); - a.next(r2, "again"); - - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - // we don't want to be called again after an error - verify(aObserver, times(0)).onNext("helloagain"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorUnsubscribe() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - Subscription subscription = a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("helloworld"); - - subscription.unsubscribe(); - a.next(r1, "hello"); - a.next(r2, "again"); - - verify(aObserver, times(0)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - // we don't want to be called again after an error - verify(aObserver, times(0)).onNext("helloagain"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorEarlyCompletion() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "one"); - a.next(r1, "two"); - a.complete(r1); - a.next(r2, "A"); - - InOrder inOrder = inOrder(aObserver); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("oneA"); - - a.complete(r2); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, times(1)).onCompleted(); - inOrder.verify(aObserver, never()).onNext(anyString()); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testZip2Types() { - Func2 zipr = getConcatStringIntegerZipr(); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - - Observable w = Observable.create(zip(Observable.from("one", "two"), Observable.from(2, 3, 4), zipr)); - w.subscribe(aObserver); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one2"); - verify(aObserver, times(1)).onNext("two3"); - verify(aObserver, never()).onNext("4"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testZip3Types() { - Func3 zipr = getConcatStringIntegerIntArrayZipr(); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - - Observable w = Observable.create(zip(Observable.from("one", "two"), Observable.from(2), Observable.from(new int[] { 4, 5, 6 }), zipr)); - w.subscribe(aObserver); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one2[4, 5, 6]"); - verify(aObserver, never()).onNext("two"); - } - - @Test - public void testOnNextExceptionInvokesOnError() { - Func2 zipr = getDivideZipr(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - - Observable w = Observable.create(zip(Observable.from(10, 20, 30), Observable.from(0, 1, 2), zipr)); - w.subscribe(aObserver); - - verify(aObserver, times(1)).onError(any(Throwable.class)); - } - - private Func2 getDivideZipr() { - Func2 zipr = new Func2() { - - @Override - public Integer call(Integer i1, Integer i2) { - return i1 / i2; - } - - }; - return zipr; - } - - private Func3 getConcat3StringsZipr() { - Func3 zipr = new Func3() { - - @Override - public String call(String a1, String a2, String a3) { - if (a1 == null) { - a1 = ""; - } - if (a2 == null) { - a2 = ""; - } - if (a3 == null) { - a3 = ""; - } - return a1 + a2 + a3; - } - - }; - return zipr; - } - - private FuncN getConcatZipr() { - FuncN zipr = new FuncN() { - - @Override - public String call(Object... args) { - String returnValue = ""; - for (Object o : args) { - if (o != null) { - returnValue += getStringValue(o); - } - } - System.out.println("returning: " + returnValue); - return returnValue; - } - - }; - return zipr; - } - - private Func2 getConcatStringIntegerZipr() { - Func2 zipr = new Func2() { - - @Override - public String call(String s, Integer i) { - return getStringValue(s) + getStringValue(i); - } - - }; - return zipr; - } - - private Func3 getConcatStringIntegerIntArrayZipr() { - Func3 zipr = new Func3() { - - @Override - public String call(String s, Integer i, int[] iArray) { - return getStringValue(s) + getStringValue(i) + getStringValue(iArray); - } - - }; - return zipr; - } - - private static String getStringValue(Object o) { - if (o == null) { - return ""; - } else { - if (o instanceof int[]) { - return Arrays.toString((int[]) o); - } else { - return String.valueOf(o); - } - } - } - - private static class TestObservable implements OnSubscribeFunc { - - Observer observer; - - @Override - public Subscription onSubscribe(Observer Observer) { - // just store the variable where it can be accessed so we can manually trigger it - this.observer = Observer; - return Subscriptions.empty(); - } - - } - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperatorTester.java b/rxjava-core/src/main/java/rx/operators/OperatorTester.java deleted file mode 100644 index 62b41a0567..0000000000 --- a/rxjava-core/src/main/java/rx/operators/OperatorTester.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright 2013 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.operators; - -import java.util.concurrent.TimeUnit; - -import rx.Scheduler; -import rx.Subscription; -import rx.util.functions.Action0; -import rx.util.functions.Func2; - -/** - * Common utility functions for testing operator implementations. - */ -/* package */class OperatorTester { - /* - * This is purposefully package-only so it does not leak into the public API outside of this package. - * - * This package is implementation details and not part of the Javadocs and thus can change without breaking backwards compatibility. - * - * benjchristensen => I'm procrastinating the decision of where and how these types of classes (see rx.subjects.UnsubscribeTester) should exist. - * If they are only for internal implementations then I don't want them as part of the API. - * If they are truly useful for everyone to use then an "rx.testing" package may make sense. - */ - - private OperatorTester() { - } - - public static class UnitTest { - - /** - * Used for mocking of Schedulers since many Scheduler implementations are static/final. - * - * @param underlying - * @return - */ - public static Scheduler forwardingScheduler(Scheduler underlying) { - return new ForwardingScheduler(underlying); - } - - public static class ForwardingScheduler extends Scheduler { - private final Scheduler underlying; - - public ForwardingScheduler(Scheduler underlying) { - this.underlying = underlying; - } - - @Override - public Subscription schedule(Action0 action) { - return underlying.schedule(action); - } - - @Override - public Subscription schedule(T state, Func2 action) { - return underlying.schedule(state, action); - } - - @Override - public Subscription schedule(Action0 action, long dueTime, TimeUnit unit) { - return underlying.schedule(action, dueTime, unit); - } - - @Override - public Subscription schedule(T state, Func2 action, long dueTime, TimeUnit unit) { - return underlying.schedule(state, action, dueTime, unit); - } - - @Override - public Subscription schedulePeriodically(Action0 action, long initialDelay, long period, TimeUnit unit) { - return underlying.schedulePeriodically(action, initialDelay, period, unit); - } - - @Override - public Subscription schedulePeriodically(T state, Func2 action, long initialDelay, long period, TimeUnit unit) { - return underlying.schedulePeriodically(state, action, initialDelay, period, unit); - } - - @Override - public long now() { - return underlying.now(); - } - } - } -} \ No newline at end of file diff --git a/rxjava-core/src/main/java/rx/operators/SafeObservableSubscription.java b/rxjava-core/src/main/java/rx/operators/SafeObservableSubscription.java index a94f658708..69e22def56 100644 --- a/rxjava-core/src/main/java/rx/operators/SafeObservableSubscription.java +++ b/rxjava-core/src/main/java/rx/operators/SafeObservableSubscription.java @@ -15,14 +15,10 @@ */ package rx.operators; -import static org.mockito.Mockito.*; +import rx.Subscription; import java.util.concurrent.atomic.AtomicReference; -import org.junit.Test; - -import rx.Subscription; - /** * Thread-safe wrapper around Observable Subscription that ensures unsubscribe can be called only once. *

@@ -82,15 +78,4 @@ public void unsubscribe() { public boolean isUnsubscribed() { return actualSubscription.get() == UNSUBSCRIBED; } - - public static class UnitTest { - @Test - public void testWrapAfterUnsubscribe() { - SafeObservableSubscription atomicObservableSubscription = new SafeObservableSubscription(); - atomicObservableSubscription.unsubscribe(); - Subscription innerSubscription = mock(Subscription.class); - atomicObservableSubscription.wrap(innerSubscription); - verify(innerSubscription, times(1)).unsubscribe(); - } - } } diff --git a/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java b/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java index 9a6b14d09f..c8b4fac812 100644 --- a/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java +++ b/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java @@ -15,27 +15,7 @@ */ package rx.operators; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.Random; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import rx.Observable; -import rx.Observable.OnSubscribeFunc; import rx.Observer; -import rx.Subscription; /** * A thread-safe Observer for transitioning states in operators. @@ -138,740 +118,4 @@ public void onCompleted() { finished = true; } } - - public static class UnitTest { - @Mock - Observer aObserver; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testSingleThreadedBasic() { - Subscription s = mock(Subscription.class); - TestSingleThreadedObservable onSubscribe = new TestSingleThreadedObservable(s, "one", "two", "three"); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - SynchronizedObserver aw = new SynchronizedObserver(aObserver, as); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - // verify(s, times(1)).unsubscribe(); - } - - @Test - public void testMultiThreadedBasic() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three"); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - assertEquals(3, busyObserver.onNextCount.get()); - assertFalse(busyObserver.onError); - assertTrue(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - // verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - @Test - public void testMultiThreadedBasicWithLock() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three"); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - - Object lock = new Object(); - ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); - - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); - - externalBusyThread.start(); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - try { - externalBusyThread.join(10000); - assertFalse(externalBusyThread.isAlive()); - assertFalse(externalBusyThread.fail); - } catch (InterruptedException e) { - // ignore - } - - assertEquals(3, busyObserver.onNextCount.get()); - assertFalse(busyObserver.onError); - assertTrue(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - // verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - @Test - public void testMultiThreadedWithNPE() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); - - // we can't know how many onNext calls will occur since they each run on a separate thread - // that depends on thread scheduling so 0, 1, 2 and 3 are all valid options - // assertEquals(3, busyObserver.onNextCount.get()); - assertTrue(busyObserver.onNextCount.get() < 4); - assertTrue(busyObserver.onError); - // no onCompleted because onError was invoked - assertFalse(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - //verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - @Test - public void testMultiThreadedWithNPEAndLock() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - - Object lock = new Object(); - ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); - - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); - - externalBusyThread.start(); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - try { - externalBusyThread.join(10000); - assertFalse(externalBusyThread.isAlive()); - assertFalse(externalBusyThread.fail); - } catch (InterruptedException e) { - // ignore - } - - System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); - - // we can't know how many onNext calls will occur since they each run on a separate thread - // that depends on thread scheduling so 0, 1, 2 and 3 are all valid options - // assertEquals(3, busyObserver.onNextCount.get()); - assertTrue(busyObserver.onNextCount.get() < 4); - assertTrue(busyObserver.onError); - // no onCompleted because onError was invoked - assertFalse(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - //verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - @Test - public void testMultiThreadedWithNPEinMiddle() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); - // this should not be the full number of items since the error should stop it before it completes all 9 - System.out.println("onNext count: " + busyObserver.onNextCount.get()); - assertTrue(busyObserver.onNextCount.get() < 9); - assertTrue(busyObserver.onError); - // no onCompleted because onError was invoked - assertFalse(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - // verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - @Test - public void testMultiThreadedWithNPEinMiddleAndLock() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - - Object lock = new Object(); - ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); - - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); - - externalBusyThread.start(); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - try { - externalBusyThread.join(10000); - assertFalse(externalBusyThread.isAlive()); - assertFalse(externalBusyThread.fail); - } catch (InterruptedException e) { - // ignore - } - - System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); - // this should not be the full number of items since the error should stop it before it completes all 9 - System.out.println("onNext count: " + busyObserver.onNextCount.get()); - assertTrue(busyObserver.onNextCount.get() < 9); - assertTrue(busyObserver.onError); - // no onCompleted because onError was invoked - assertFalse(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - // verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - /** - * A non-realistic use case that tries to expose thread-safety issues by throwing lots of out-of-order - * events on many threads. - * - * @param w - * @param tw - */ - @Test - public void runConcurrencyTest() { - ExecutorService tp = Executors.newFixedThreadPool(20); - try { - TestConcurrencyObserver tw = new TestConcurrencyObserver(); - SafeObservableSubscription s = new SafeObservableSubscription(); - SynchronizedObserver w = new SynchronizedObserver(tw, s); - - Future f1 = tp.submit(new OnNextThread(w, 12000)); - Future f2 = tp.submit(new OnNextThread(w, 5000)); - Future f3 = tp.submit(new OnNextThread(w, 75000)); - Future f4 = tp.submit(new OnNextThread(w, 13500)); - Future f5 = tp.submit(new OnNextThread(w, 22000)); - Future f6 = tp.submit(new OnNextThread(w, 15000)); - Future f7 = tp.submit(new OnNextThread(w, 7500)); - Future f8 = tp.submit(new OnNextThread(w, 23500)); - - Future f10 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f1, f2, f3, f4)); - try { - Thread.sleep(1); - } catch (InterruptedException e) { - // ignore - } - Future f11 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); - Future f12 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); - Future f13 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); - Future f14 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); - // // the next 4 onError events should wait on same as f10 - Future f15 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); - Future f16 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); - Future f17 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); - Future f18 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); - - waitOnThreads(f1, f2, f3, f4, f5, f6, f7, f8, f10, f11, f12, f13, f14, f15, f16, f17, f18); - @SuppressWarnings("unused") - int numNextEvents = tw.assertEvents(null); // no check of type since we don't want to test barging results here, just interleaving behavior - // System.out.println("Number of events executed: " + numNextEvents); - } catch (Throwable e) { - fail("Concurrency test failed: " + e.getMessage()); - e.printStackTrace(); - } finally { - tp.shutdown(); - try { - tp.awaitTermination(5000, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - private static void waitOnThreads(Future... futures) { - for (Future f : futures) { - try { - f.get(10, TimeUnit.SECONDS); - } catch (Throwable e) { - System.err.println("Failed while waiting on future."); - e.printStackTrace(); - } - } - } - - /** - * A thread that will pass data to onNext - */ - public static class OnNextThread implements Runnable { - - private final Observer Observer; - private final int numStringsToSend; - - OnNextThread(Observer Observer, int numStringsToSend) { - this.Observer = Observer; - this.numStringsToSend = numStringsToSend; - } - - @Override - public void run() { - for (int i = 0; i < numStringsToSend; i++) { - Observer.onNext("aString"); - } - } - } - - /** - * A thread that will call onError or onNext - */ - public static class CompletionThread implements Runnable { - - private final Observer Observer; - private final TestConcurrencyObserverEvent event; - private final Future[] waitOnThese; - - CompletionThread(Observer Observer, TestConcurrencyObserverEvent event, Future... waitOnThese) { - this.Observer = Observer; - this.event = event; - this.waitOnThese = waitOnThese; - } - - @Override - public void run() { - /* if we have 'waitOnThese' futures, we'll wait on them before proceeding */ - if (waitOnThese != null) { - for (Future f : waitOnThese) { - try { - f.get(); - } catch (Throwable e) { - System.err.println("Error while waiting on future in CompletionThread"); - } - } - } - - /* send the event */ - if (event == TestConcurrencyObserverEvent.onError) { - Observer.onError(new RuntimeException("mocked exception")); - } else if (event == TestConcurrencyObserverEvent.onCompleted) { - Observer.onCompleted(); - - } else { - throw new IllegalArgumentException("Expecting either onError or onCompleted"); - } - } - } - - private static enum TestConcurrencyObserverEvent { - onCompleted, onError, onNext - } - - private static class TestConcurrencyObserver implements Observer { - - /** used to store the order and number of events received */ - private final LinkedBlockingQueue events = new LinkedBlockingQueue(); - private final int waitTime; - - @SuppressWarnings("unused") - public TestConcurrencyObserver(int waitTimeInNext) { - this.waitTime = waitTimeInNext; - } - - public TestConcurrencyObserver() { - this.waitTime = 0; - } - - @Override - public void onCompleted() { - events.add(TestConcurrencyObserverEvent.onCompleted); - } - - @Override - public void onError(Throwable e) { - events.add(TestConcurrencyObserverEvent.onError); - } - - @Override - public void onNext(String args) { - events.add(TestConcurrencyObserverEvent.onNext); - // do some artificial work to make the thread scheduling/timing vary - int s = 0; - for (int i = 0; i < 20; i++) { - s += s * i; - } - - if (waitTime > 0) { - try { - Thread.sleep(waitTime); - } catch (InterruptedException e) { - // ignore - } - } - } - - /** - * Assert the order of events is correct and return the number of onNext executions. - * - * @param expectedEndingEvent - * @return int count of onNext calls - * @throws IllegalStateException - * If order of events was invalid. - */ - public int assertEvents(TestConcurrencyObserverEvent expectedEndingEvent) throws IllegalStateException { - int nextCount = 0; - boolean finished = false; - for (TestConcurrencyObserverEvent e : events) { - if (e == TestConcurrencyObserverEvent.onNext) { - if (finished) { - // already finished, we shouldn't get this again - throw new IllegalStateException("Received onNext but we're already finished."); - } - nextCount++; - } else if (e == TestConcurrencyObserverEvent.onError) { - if (finished) { - // already finished, we shouldn't get this again - throw new IllegalStateException("Received onError but we're already finished."); - } - if (expectedEndingEvent != null && TestConcurrencyObserverEvent.onError != expectedEndingEvent) { - throw new IllegalStateException("Received onError ending event but expected " + expectedEndingEvent); - } - finished = true; - } else if (e == TestConcurrencyObserverEvent.onCompleted) { - if (finished) { - // already finished, we shouldn't get this again - throw new IllegalStateException("Received onCompleted but we're already finished."); - } - if (expectedEndingEvent != null && TestConcurrencyObserverEvent.onCompleted != expectedEndingEvent) { - throw new IllegalStateException("Received onCompleted ending event but expected " + expectedEndingEvent); - } - finished = true; - } - } - - return nextCount; - } - - } - - /** - * This spawns a single thread for the subscribe execution - * - */ - private static class TestSingleThreadedObservable implements OnSubscribeFunc { - - final Subscription s; - final String[] values; - private Thread t = null; - - public TestSingleThreadedObservable(final Subscription s, final String... values) { - this.s = s; - this.values = values; - - } - - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestSingleThreadedObservable subscribed to ..."); - t = new Thread(new Runnable() { - - @Override - public void run() { - try { - System.out.println("running TestSingleThreadedObservable thread"); - for (String s : values) { - System.out.println("TestSingleThreadedObservable onNext: " + s); - observer.onNext(s); - } - observer.onCompleted(); - } catch (Throwable e) { - throw new RuntimeException(e); - } - } - - }); - System.out.println("starting TestSingleThreadedObservable thread"); - t.start(); - System.out.println("done starting TestSingleThreadedObservable thread"); - return s; - } - - public void waitToFinish() { - try { - t.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - } - - /** - * This spawns a thread for the subscription, then a separate thread for each onNext call. - * - */ - private static class TestMultiThreadedObservable implements OnSubscribeFunc { - - final Subscription s; - final String[] values; - Thread t = null; - AtomicInteger threadsRunning = new AtomicInteger(); - AtomicInteger maxConcurrentThreads = new AtomicInteger(); - ExecutorService threadPool; - - public TestMultiThreadedObservable(Subscription s, String... values) { - this.s = s; - this.values = values; - this.threadPool = Executors.newCachedThreadPool(); - } - - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestMultiThreadedObservable subscribed to ..."); - t = new Thread(new Runnable() { - - @Override - public void run() { - try { - System.out.println("running TestMultiThreadedObservable thread"); - for (final String s : values) { - threadPool.execute(new Runnable() { - - @Override - public void run() { - threadsRunning.incrementAndGet(); - try { - // perform onNext call - System.out.println("TestMultiThreadedObservable onNext: " + s); - if (s == null) { - // force an error - throw new NullPointerException(); - } - observer.onNext(s); - // capture 'maxThreads' - int concurrentThreads = threadsRunning.get(); - int maxThreads = maxConcurrentThreads.get(); - if (concurrentThreads > maxThreads) { - maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); - } - } catch (Throwable e) { - observer.onError(e); - } finally { - threadsRunning.decrementAndGet(); - } - } - }); - } - // we are done spawning threads - threadPool.shutdown(); - } catch (Throwable e) { - throw new RuntimeException(e); - } - - // wait until all threads are done, then mark it as COMPLETED - try { - // wait for all the threads to finish - threadPool.awaitTermination(2, TimeUnit.SECONDS); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - observer.onCompleted(); - } - }); - System.out.println("starting TestMultiThreadedObservable thread"); - t.start(); - System.out.println("done starting TestMultiThreadedObservable thread"); - return s; - } - - public void waitToFinish() { - try { - t.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - } - - private static class BusyObserver implements Observer { - volatile boolean onCompleted = false; - volatile boolean onError = false; - AtomicInteger onNextCount = new AtomicInteger(); - AtomicInteger threadsRunning = new AtomicInteger(); - AtomicInteger maxConcurrentThreads = new AtomicInteger(); - - @Override - public void onCompleted() { - threadsRunning.incrementAndGet(); - - System.out.println(">>> BusyObserver received onCompleted"); - onCompleted = true; - - int concurrentThreads = threadsRunning.get(); - int maxThreads = maxConcurrentThreads.get(); - if (concurrentThreads > maxThreads) { - maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); - } - threadsRunning.decrementAndGet(); - } - - @Override - public void onError(Throwable e) { - threadsRunning.incrementAndGet(); - - System.out.println(">>> BusyObserver received onError: " + e.getMessage()); - onError = true; - - int concurrentThreads = threadsRunning.get(); - int maxThreads = maxConcurrentThreads.get(); - if (concurrentThreads > maxThreads) { - maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); - } - threadsRunning.decrementAndGet(); - } - - @Override - public void onNext(String args) { - threadsRunning.incrementAndGet(); - try { - onNextCount.incrementAndGet(); - System.out.println(">>> BusyObserver received onNext: " + args); - try { - // simulate doing something computational - Thread.sleep(200); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } finally { - // capture 'maxThreads' - int concurrentThreads = threadsRunning.get(); - int maxThreads = maxConcurrentThreads.get(); - if (concurrentThreads > maxThreads) { - maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); - } - threadsRunning.decrementAndGet(); - } - } - - } - - private static class ExternalBusyThread extends Thread { - - private BusyObserver observer; - private Object lock; - private int lockTimes; - private int waitTime; - public volatile boolean fail; - - public ExternalBusyThread(BusyObserver observer, Object lock, int lockTimes, int waitTime) { - this.observer = observer; - this.lock = lock; - this.lockTimes = lockTimes; - this.waitTime = waitTime; - this.fail = false; - } - - @Override - public void run() { - Random r = new Random(); - for (int i = 0; i < lockTimes; i++) { - synchronized (lock) { - int oldOnNextCount = observer.onNextCount.get(); - boolean oldOnCompleted = observer.onCompleted; - boolean oldOnError = observer.onError; - try { - Thread.sleep(r.nextInt(waitTime)); - } catch (InterruptedException e) { - // ignore - } - // Since we own the lock, onNextCount, onCompleted and - // onError must not be changed. - int newOnNextCount = observer.onNextCount.get(); - boolean newOnCompleted = observer.onCompleted; - boolean newOnError = observer.onError; - if (oldOnNextCount != newOnNextCount) { - System.out.println(">>> ExternalBusyThread received different onNextCount: " - + oldOnNextCount - + " -> " - + newOnNextCount); - fail = true; - break; - } - if (oldOnCompleted != newOnCompleted) { - System.out.println(">>> ExternalBusyThread received different onCompleted: " - + oldOnCompleted - + " -> " - + newOnCompleted); - fail = true; - break; - } - if (oldOnError != newOnError) { - System.out.println(">>> ExternalBusyThread received different onError: " - + oldOnError - + " -> " - + newOnError); - fail = true; - break; - } - } - } - } - - } - - } - } \ No newline at end of file diff --git a/rxjava-core/src/main/java/rx/plugins/RxJavaPlugins.java b/rxjava-core/src/main/java/rx/plugins/RxJavaPlugins.java index e27c343815..1e049732ff 100644 --- a/rxjava-core/src/main/java/rx/plugins/RxJavaPlugins.java +++ b/rxjava-core/src/main/java/rx/plugins/RxJavaPlugins.java @@ -15,12 +15,8 @@ */ package rx.plugins; -import static org.junit.Assert.*; - import java.util.concurrent.atomic.AtomicReference; -import org.junit.Test; - /** * Registry for plugin implementations that allows global override and handles the retrieval of correct implementation based on order of precedence: *

    @@ -36,7 +32,7 @@ public class RxJavaPlugins { private final AtomicReference errorHandler = new AtomicReference(); private final AtomicReference observableExecutionHook = new AtomicReference(); - private RxJavaPlugins() { + RxJavaPlugins() { } @@ -148,77 +144,4 @@ private static Object getPluginImplementationViaProperty(Class pluginClass) { return null; } } - - public static class UnitTest { - - @Test - public void testErrorHandlerDefaultImpl() { - RxJavaErrorHandler impl = new RxJavaPlugins().getErrorHandler(); - assertTrue(impl instanceof RxJavaErrorHandlerDefault); - } - - @Test - public void testErrorHandlerViaRegisterMethod() { - RxJavaPlugins p = new RxJavaPlugins(); - p.registerErrorHandler(new RxJavaErrorHandlerTestImpl()); - RxJavaErrorHandler impl = p.getErrorHandler(); - assertTrue(impl instanceof RxJavaErrorHandlerTestImpl); - } - - @Test - public void testErrorHandlerViaProperty() { - try { - RxJavaPlugins p = new RxJavaPlugins(); - String fullClass = getFullClassNameForTestClass(RxJavaErrorHandlerTestImpl.class); - System.setProperty("rxjava.plugin.RxJavaErrorHandler.implementation", fullClass); - RxJavaErrorHandler impl = p.getErrorHandler(); - assertTrue(impl instanceof RxJavaErrorHandlerTestImpl); - } finally { - System.clearProperty("rxjava.plugin.RxJavaErrorHandler.implementation"); - } - } - - // inside UnitTest so it is stripped from Javadocs - public static class RxJavaErrorHandlerTestImpl extends RxJavaErrorHandler { - // just use defaults - } - - @Test - public void testObservableExecutionHookDefaultImpl() { - RxJavaPlugins p = new RxJavaPlugins(); - RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); - assertTrue(impl instanceof RxJavaObservableExecutionHookDefault); - } - - @Test - public void testObservableExecutionHookViaRegisterMethod() { - RxJavaPlugins p = new RxJavaPlugins(); - p.registerObservableExecutionHook(new RxJavaObservableExecutionHookTestImpl()); - RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); - assertTrue(impl instanceof RxJavaObservableExecutionHookTestImpl); - } - - @Test - public void testObservableExecutionHookViaProperty() { - try { - RxJavaPlugins p = new RxJavaPlugins(); - String fullClass = getFullClassNameForTestClass(RxJavaObservableExecutionHookTestImpl.class); - System.setProperty("rxjava.plugin.RxJavaObservableExecutionHook.implementation", fullClass); - RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); - assertTrue(impl instanceof RxJavaObservableExecutionHookTestImpl); - } finally { - System.clearProperty("rxjava.plugin.RxJavaObservableExecutionHook.implementation"); - } - } - - // inside UnitTest so it is stripped from Javadocs - public static class RxJavaObservableExecutionHookTestImpl extends RxJavaObservableExecutionHook { - // just use defaults - } - - private static String getFullClassNameForTestClass(Class cls) { - return RxJavaPlugins.class.getPackage().getName() + "." + RxJavaPlugins.class.getSimpleName() + "$UnitTest$" + cls.getSimpleName(); - } - } - } diff --git a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java index f50ff21322..7e4357c696 100644 --- a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java @@ -15,20 +15,12 @@ */ package rx.subjects; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; -import org.mockito.Mockito; - import rx.Observer; import rx.Subscription; import rx.operators.SafeObservableSubscription; -import rx.util.functions.Action1; -import rx.util.functions.Func0; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; /** * Subject that publishes only the last event to each {@link Observer} that has subscribed when the @@ -123,135 +115,4 @@ public void onError(Throwable e) { public void onNext(T args) { currentValue.set(args); } - - public static class UnitTest { - - private final Throwable testException = new Throwable(); - - @Test - public void testNeverCompleted() { - AsyncSubject subject = AsyncSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - - assertNeverCompletedObserver(aObserver); - } - - private void assertNeverCompletedObserver(Observer aObserver) - { - verify(aObserver, Mockito.never()).onNext(anyString()); - verify(aObserver, Mockito.never()).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testCompleted() { - AsyncSubject subject = AsyncSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onCompleted(); - - assertCompletedObserver(aObserver); - } - - private void assertCompletedObserver(Observer aObserver) - { - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testError() { - AsyncSubject subject = AsyncSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onError(testException); - subject.onNext("four"); - subject.onError(new Throwable()); - subject.onCompleted(); - - assertErrorObserver(aObserver); - } - - private void assertErrorObserver(Observer aObserver) - { - verify(aObserver, Mockito.never()).onNext(anyString()); - verify(aObserver, times(1)).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testUnsubscribeBeforeCompleted() { - AsyncSubject subject = AsyncSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - Subscription subscription = subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - - subscription.unsubscribe(); - assertNoOnNextEventsReceived(aObserver); - - subject.onNext("three"); - subject.onCompleted(); - - assertNoOnNextEventsReceived(aObserver); - } - - private void assertNoOnNextEventsReceived(Observer aObserver) - { - verify(aObserver, Mockito.never()).onNext(anyString()); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testUnsubscribe() - { - UnsubscribeTester.test(new Func0>() - { - @Override - public AsyncSubject call() - { - return AsyncSubject.create(); - } - }, new Action1>() - { - @Override - public void call(AsyncSubject DefaultSubject) - { - DefaultSubject.onCompleted(); - } - }, new Action1>() - { - @Override - public void call(AsyncSubject DefaultSubject) - { - DefaultSubject.onError(new Throwable()); - } - }, - null); - } - } } diff --git a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java index 1efe3571bd..5ea3c52146 100644 --- a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java @@ -15,19 +15,12 @@ */ package rx.subjects; -import static org.mockito.Mockito.*; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; -import org.mockito.Mockito; - import rx.Observer; import rx.Subscription; import rx.operators.SafeObservableSubscription; -import rx.util.functions.Action1; -import rx.util.functions.Func0; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; /** * Subject that publishes the most recent and all subsequent events to each subscribed {@link Observer}. @@ -127,137 +120,4 @@ public void onNext(T args) { observer.onNext(args); } } - - public static class UnitTest { - - private final Throwable testException = new Throwable(); - - @Test - public void testThatObserverReceivesDefaultValueIfNothingWasPublished() { - BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - - assertReceivedAllEvents(aObserver); - } - - private void assertReceivedAllEvents(Observer aObserver) { - verify(aObserver, times(1)).onNext("default"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testThatObserverDoesNotReceiveDefaultValueIfSomethingWasPublished() { - BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); - - subject.onNext("one"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("two"); - subject.onNext("three"); - - assertDidNotReceiveTheDefaultValue(aObserver); - } - - private void assertDidNotReceiveTheDefaultValue(Observer aObserver) { - verify(aObserver, Mockito.never()).onNext("default"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testCompleted() { - BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onCompleted(); - - assertCompletedObserver(aObserver); - } - - private void assertCompletedObserver(Observer aObserver) - { - verify(aObserver, times(1)).onNext("default"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testCompletedAfterError() { - BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onError(testException); - subject.onNext("two"); - subject.onCompleted(); - - assertErrorObserver(aObserver); - } - - private void assertErrorObserver(Observer aObserver) - { - verify(aObserver, times(1)).onNext("default"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onError(testException); - } - - @Test - public void testUnsubscribe() - { - UnsubscribeTester.test(new Func0>() - { - @Override - public BehaviorSubject call() - { - return BehaviorSubject.createWithDefaultValue("default"); - } - }, new Action1>() - { - @Override - public void call(BehaviorSubject DefaultSubject) - { - DefaultSubject.onCompleted(); - } - }, new Action1>() - { - @Override - public void call(BehaviorSubject DefaultSubject) - { - DefaultSubject.onError(new Throwable()); - } - }, new Action1>() - { - @Override - public void call(BehaviorSubject DefaultSubject) - { - DefaultSubject.onNext("one"); - } - }); - } - } } diff --git a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java index 5588dd485b..ca248ea231 100644 --- a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java @@ -15,32 +15,13 @@ */ package rx.subjects; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; +import rx.Observer; +import rx.Subscription; +import rx.operators.SafeObservableSubscription; import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import junit.framework.Assert; - -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mockito; - -import rx.Notification; -import rx.Observable; -import rx.Observer; -import rx.Subscription; -import rx.operators.SafeObservableSubscription; -import rx.subscriptions.Subscriptions; -import rx.util.functions.Action1; -import rx.util.functions.Func0; -import rx.util.functions.Func1; /** * Subject that, once and {@link Observer} has subscribed, publishes all subsequent events to the subscriber. @@ -136,356 +117,4 @@ public void onNext(T args) { private Collection> snapshotOfValues() { return new ArrayList>(observers.values()); } - - public static class UnitTest { - @Test - public void test() { - PublishSubject subject = PublishSubject.create(); - final AtomicReference>> actualRef = new AtomicReference>>(); - - Observable>> wNotificationsList = subject.materialize().toList(); - wNotificationsList.subscribe(new Action1>>() { - @Override - public void call(List> actual) { - actualRef.set(actual); - } - }); - - Subscription sub = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(final Observer observer) { - final AtomicBoolean stop = new AtomicBoolean(false); - new Thread() { - @Override - public void run() { - int i = 1; - while (!stop.get()) { - observer.onNext(i++); - } - observer.onCompleted(); - } - }.start(); - return new Subscription() { - @Override - public void unsubscribe() { - stop.set(true); - } - }; - } - }).subscribe(subject); - // the subject has received an onComplete from the first subscribe because - // it is synchronous and the next subscribe won't do anything. - Observable.from(-1, -2, -3).subscribe(subject); - - List> expected = new ArrayList>(); - expected.add(new Notification(-1)); - expected.add(new Notification(-2)); - expected.add(new Notification(-3)); - expected.add(new Notification()); - Assert.assertTrue(actualRef.get().containsAll(expected)); - - sub.unsubscribe(); - } - - private final Throwable testException = new Throwable(); - - @Test - public void testCompleted() { - PublishSubject subject = PublishSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onCompleted(); - - @SuppressWarnings("unchecked") - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - - subject.onNext("four"); - subject.onCompleted(); - subject.onError(new Throwable()); - - assertCompletedObserver(aObserver); - // todo bug? assertNeverObserver(anotherObserver); - } - - private void assertCompletedObserver(Observer aObserver) - { - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testError() { - PublishSubject subject = PublishSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onError(testException); - - @SuppressWarnings("unchecked") - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - - subject.onNext("four"); - subject.onError(new Throwable()); - subject.onCompleted(); - - assertErrorObserver(aObserver); - // todo bug? assertNeverObserver(anotherObserver); - } - - private void assertErrorObserver(Observer aObserver) - { - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, times(1)).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testSubscribeMidSequence() { - PublishSubject subject = PublishSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - - assertObservedUntilTwo(aObserver); - - @SuppressWarnings("unchecked") - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - - subject.onNext("three"); - subject.onCompleted(); - - assertCompletedObserver(aObserver); - assertCompletedStartingWithThreeObserver(anotherObserver); - } - - private void assertCompletedStartingWithThreeObserver(Observer aObserver) - { - verify(aObserver, Mockito.never()).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testUnsubscribeFirstObserver() { - PublishSubject subject = PublishSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - Subscription subscription = subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - - subscription.unsubscribe(); - assertObservedUntilTwo(aObserver); - - @SuppressWarnings("unchecked") - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - - subject.onNext("three"); - subject.onCompleted(); - - assertObservedUntilTwo(aObserver); - assertCompletedStartingWithThreeObserver(anotherObserver); - } - - private void assertObservedUntilTwo(Observer aObserver) - { - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testUnsubscribe() - { - UnsubscribeTester.test(new Func0>() - { - @Override - public PublishSubject call() - { - return PublishSubject.create(); - } - }, new Action1>() - { - @Override - public void call(PublishSubject DefaultSubject) - { - DefaultSubject.onCompleted(); - } - }, new Action1>() - { - @Override - public void call(PublishSubject DefaultSubject) - { - DefaultSubject.onError(new Throwable()); - } - }, new Action1>() - { - @Override - public void call(PublishSubject DefaultSubject) - { - DefaultSubject.onNext("one"); - } - }); - } - - @Test - public void testNestedSubscribe() { - final PublishSubject s = PublishSubject.create(); - - final AtomicInteger countParent = new AtomicInteger(); - final AtomicInteger countChildren = new AtomicInteger(); - final AtomicInteger countTotal = new AtomicInteger(); - - final ArrayList list = new ArrayList(); - - s.mapMany(new Func1>() { - - @Override - public Observable call(final Integer v) { - countParent.incrementAndGet(); - - // then subscribe to subject again (it will not receive the previous value) - return s.map(new Func1() { - - @Override - public String call(Integer v2) { - countChildren.incrementAndGet(); - return "Parent: " + v + " Child: " + v2; - } - - }); - } - - }).subscribe(new Action1() { - - @Override - public void call(String v) { - countTotal.incrementAndGet(); - list.add(v); - } - - }); - - for (int i = 0; i < 10; i++) { - s.onNext(i); - } - s.onCompleted(); - - // System.out.println("countParent: " + countParent.get()); - // System.out.println("countChildren: " + countChildren.get()); - // System.out.println("countTotal: " + countTotal.get()); - - // 9+8+7+6+5+4+3+2+1+0 == 45 - assertEquals(45, list.size()); - } - - /** - * Should be able to unsubscribe all Observers, have it stop emitting, then subscribe new ones and it start emitting again. - */ - @Test - public void testReSubscribe() { - final PublishSubject ps = PublishSubject.create(); - - Observer o1 = mock(Observer.class); - Subscription s1 = ps.subscribe(o1); - - // emit - ps.onNext(1); - - // validate we got it - InOrder inOrder1 = inOrder(o1); - inOrder1.verify(o1, times(1)).onNext(1); - inOrder1.verifyNoMoreInteractions(); - - // unsubscribe - s1.unsubscribe(); - - // emit again but nothing will be there to receive it - ps.onNext(2); - - Observer o2 = mock(Observer.class); - Subscription s2 = ps.subscribe(o2); - - // emit - ps.onNext(3); - - // validate we got it - InOrder inOrder2 = inOrder(o2); - inOrder2.verify(o2, times(1)).onNext(3); - inOrder2.verifyNoMoreInteractions(); - - s2.unsubscribe(); - } - - /** - * Even if subject received an onError/onCompleted, new subscriptions should be able to restart it. - */ - @Test - public void testReSubscribeAfterTerminalState() { - final PublishSubject ps = PublishSubject.create(); - - Observer o1 = mock(Observer.class); - Subscription s1 = ps.subscribe(o1); - - // emit - ps.onNext(1); - - // validate we got it - InOrder inOrder1 = inOrder(o1); - inOrder1.verify(o1, times(1)).onNext(1); - inOrder1.verifyNoMoreInteractions(); - - // unsubscribe - s1.unsubscribe(); - - ps.onCompleted(); - - // emit again but nothing will be there to receive it - ps.onNext(2); - - Observer o2 = mock(Observer.class); - Subscription s2 = ps.subscribe(o2); - - // emit - ps.onNext(3); - - // validate we got it - InOrder inOrder2 = inOrder(o2); - inOrder2.verify(o2, times(1)).onNext(3); - inOrder2.verifyNoMoreInteractions(); - - s2.unsubscribe(); - } - - } } diff --git a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java index 3c106e30bc..e103e5fe3b 100644 --- a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java +++ b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java @@ -15,25 +15,13 @@ */ package rx.subjects; -import static org.mockito.Mockito.*; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mockito; - import rx.Observer; import rx.Subscription; import rx.subscriptions.Subscriptions; -import rx.util.functions.Action1; -import rx.util.functions.Func0; import rx.util.functions.Func1; +import java.util.*; + /** * Subject that retains all events and will replay them to an {@link Observer} that subscribes. *

    @@ -178,172 +166,4 @@ public void onNext(T args) } } } - - public static class UnitTest { - - private final Throwable testException = new Throwable(); - - @SuppressWarnings("unchecked") - @Test - public void testCompleted() { - ReplaySubject subject = ReplaySubject.create(); - - Observer o1 = mock(Observer.class); - subject.subscribe(o1); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onCompleted(); - - subject.onNext("four"); - subject.onCompleted(); - subject.onError(new Throwable()); - - assertCompletedObserver(o1); - - // assert that subscribing a 2nd time gets the same data - Observer o2 = mock(Observer.class); - subject.subscribe(o2); - assertCompletedObserver(o2); - } - - private void assertCompletedObserver(Observer aObserver) - { - InOrder inOrder = inOrder(aObserver); - - inOrder.verify(aObserver, times(1)).onNext("one"); - inOrder.verify(aObserver, times(1)).onNext("two"); - inOrder.verify(aObserver, times(1)).onNext("three"); - inOrder.verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @SuppressWarnings("unchecked") - @Test - public void testError() { - ReplaySubject subject = ReplaySubject.create(); - - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onError(testException); - - subject.onNext("four"); - subject.onError(new Throwable()); - subject.onCompleted(); - - assertErrorObserver(aObserver); - - aObserver = mock(Observer.class); - subject.subscribe(aObserver); - assertErrorObserver(aObserver); - } - - private void assertErrorObserver(Observer aObserver) - { - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, times(1)).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @SuppressWarnings("unchecked") - @Test - public void testSubscribeMidSequence() { - ReplaySubject subject = ReplaySubject.create(); - - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - - assertObservedUntilTwo(aObserver); - - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - assertObservedUntilTwo(anotherObserver); - - subject.onNext("three"); - subject.onCompleted(); - - assertCompletedObserver(aObserver); - assertCompletedObserver(anotherObserver); - } - - @SuppressWarnings("unchecked") - @Test - public void testUnsubscribeFirstObserver() { - ReplaySubject subject = ReplaySubject.create(); - - Observer aObserver = mock(Observer.class); - Subscription subscription = subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - - subscription.unsubscribe(); - assertObservedUntilTwo(aObserver); - - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - assertObservedUntilTwo(anotherObserver); - - subject.onNext("three"); - subject.onCompleted(); - - assertObservedUntilTwo(aObserver); - assertCompletedObserver(anotherObserver); - } - - private void assertObservedUntilTwo(Observer aObserver) - { - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testUnsubscribe() - { - UnsubscribeTester.test(new Func0>() - { - @Override - public ReplaySubject call() - { - return ReplaySubject.create(); - } - }, new Action1>() - { - @Override - public void call(ReplaySubject repeatSubject) - { - repeatSubject.onCompleted(); - } - }, new Action1>() - { - @Override - public void call(ReplaySubject repeatSubject) - { - repeatSubject.onError(new Throwable()); - } - }, new Action1>() - { - @Override - public void call(ReplaySubject repeatSubject) - { - repeatSubject.onNext("one"); - } - } - ); - } - } } diff --git a/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java b/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java index 6ca5c8a699..480da47178 100644 --- a/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java +++ b/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java @@ -15,19 +15,14 @@ */ package rx.subscriptions; -import static org.junit.Assert.*; +import rx.Subscription; +import rx.util.CompositeException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; - -import rx.Subscription; -import rx.util.CompositeException; /** * Subscription that represents a group of Subscriptions that are unsubscribed together. @@ -91,65 +86,4 @@ public synchronized void unsubscribe() { } } } - - public static class UnitTest { - - @Test - public void testSuccess() { - final AtomicInteger counter = new AtomicInteger(); - CompositeSubscription s = new CompositeSubscription(); - s.add(new Subscription() { - - @Override - public void unsubscribe() { - counter.incrementAndGet(); - } - }); - - s.add(new Subscription() { - - @Override - public void unsubscribe() { - counter.incrementAndGet(); - } - }); - - s.unsubscribe(); - - assertEquals(2, counter.get()); - } - - @Test - public void testException() { - final AtomicInteger counter = new AtomicInteger(); - CompositeSubscription s = new CompositeSubscription(); - s.add(new Subscription() { - - @Override - public void unsubscribe() { - throw new RuntimeException("failed on first one"); - } - }); - - s.add(new Subscription() { - - @Override - public void unsubscribe() { - counter.incrementAndGet(); - } - }); - - try { - s.unsubscribe(); - fail("Expecting an exception"); - } catch (CompositeException e) { - // we expect this - assertEquals(1, e.getExceptions().size()); - } - - // we should still have unsubscribed to the second one - assertEquals(1, counter.get()); - } - } - } diff --git a/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java b/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java index 5febe4f46a..bb85ed7a15 100644 --- a/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java +++ b/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java @@ -15,18 +15,12 @@ */ package rx.subscriptions; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import java.util.concurrent.Future; - -import org.junit.Test; - import rx.Subscription; import rx.operators.SafeObservableSubscription; import rx.util.functions.Action0; +import java.util.concurrent.Future; + /** * Helper methods and utilities for creating and working with {@link Subscription} objects */ @@ -129,15 +123,4 @@ public static CompositeSubscription create(Subscription... subscriptions) { public void unsubscribe() { } }; - - public static class UnitTest { - @Test - public void testUnsubscribeOnlyOnce() { - Action0 unsubscribe = mock(Action0.class); - Subscription subscription = create(unsubscribe); - subscription.unsubscribe(); - subscription.unsubscribe(); - verify(unsubscribe, times(1)).call(); - } - } } diff --git a/rxjava-core/src/main/java/rx/util/Range.java b/rxjava-core/src/main/java/rx/util/Range.java index c263c53471..5f7418b218 100644 --- a/rxjava-core/src/main/java/rx/util/Range.java +++ b/rxjava-core/src/main/java/rx/util/Range.java @@ -15,16 +15,9 @@ */ package rx.util; -import static org.junit.Assert.*; - -import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; -import java.util.List; import java.util.NoSuchElementException; -import org.junit.Test; - public final class Range implements Iterable { private final int start; private final int end; @@ -79,46 +72,4 @@ public void remove() { public String toString() { return "Range (" + start + ", " + end + "), step " + step; } - - public static class UnitTest { - - @Test - public void testSimpleRange() { - assertEquals(Arrays.asList(1, 2, 3, 4), toList(Range.create(1, 5))); - } - - @Test - public void testRangeWithStep() { - assertEquals(Arrays.asList(1, 3, 5, 7, 9), toList(Range.createWithStep(1, 10, 2))); - } - - @Test - public void testRangeWithCount() { - assertEquals(Arrays.asList(1, 2, 3, 4, 5), toList(Range.createWithCount(1, 5))); - } - - @Test - public void testRangeWithCount2() { - assertEquals(Arrays.asList(2, 3, 4, 5), toList(Range.createWithCount(2, 4))); - } - - @Test - public void testRangeWithCount3() { - assertEquals(Arrays.asList(0, 1, 2, 3), toList(Range.createWithCount(0, 4))); - } - - @Test - public void testRangeWithCount4() { - assertEquals(Arrays.asList(10, 11, 12, 13, 14), toList(Range.createWithCount(10, 5))); - } - - private static List toList(Iterable iterable) { - List result = new ArrayList(); - for (T element : iterable) { - result.add(element); - } - return result; - } - - } } \ No newline at end of file diff --git a/rxjava-core/src/test/java/rx/SchedulersTest.java b/rxjava-core/src/test/java/rx/SchedulersTest.java index d2ac58e61f..4150686d74 100644 --- a/rxjava-core/src/test/java/rx/SchedulersTest.java +++ b/rxjava-core/src/test/java/rx/SchedulersTest.java @@ -16,6 +16,8 @@ package rx; import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; import rx.Observable.OnSubscribeFunc; import rx.concurrency.Schedulers; import rx.concurrency.TestScheduler; @@ -34,505 +36,547 @@ import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.*; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.*; public class SchedulersTest { - @Test - public void testComputationThreadPool1() { + @SuppressWarnings("unchecked") + // mocking is unchecked, unfortunately + @Test + public void testPeriodicScheduling() { + final Func1 calledOp = mock(Func1.class); - Observable o1 = Observable. from(1, 2, 3, 4, 5); - Observable o2 = Observable. from(6, 7, 8, 9, 10); - Observable o = Observable. merge(o1, o2).map(new Func1() { + final TestScheduler scheduler = new TestScheduler(); + Subscription subscription = scheduler.schedulePeriodically(new Action0() { + @Override + public void call() { + System.out.println(scheduler.now()); + calledOp.call(scheduler.now()); + } + }, 1, 2, TimeUnit.SECONDS); + + verify(calledOp, never()).call(anyLong()); - @Override - public String call(Integer t) { - assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool")); - return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); - } - }); - - o.subscribeOn(Schedulers.threadPoolForComputation()).toBlockingObservable().forEach(new Action1() { + InOrder inOrder = Mockito.inOrder(calledOp); + + scheduler.advanceTimeBy(999L, TimeUnit.MILLISECONDS); + inOrder.verify(calledOp, never()).call(anyLong()); - @Override - public void call(String t) { - System.out.println("t: " + t); - } - }); - } + scheduler.advanceTimeBy(1L, TimeUnit.MILLISECONDS); + inOrder.verify(calledOp, times(1)).call(1000L); + + scheduler.advanceTimeBy(1999L, TimeUnit.MILLISECONDS); + inOrder.verify(calledOp, never()).call(3000L); + + scheduler.advanceTimeBy(1L, TimeUnit.MILLISECONDS); + inOrder.verify(calledOp, times(1)).call(3000L); + + scheduler.advanceTimeBy(5L, TimeUnit.SECONDS); + inOrder.verify(calledOp, times(1)).call(5000L); + inOrder.verify(calledOp, times(1)).call(7000L); + + subscription.unsubscribe(); + scheduler.advanceTimeBy(11L, TimeUnit.SECONDS); + inOrder.verify(calledOp, never()).call(anyLong()); + } + + @Test + public void testComputationThreadPool1() { + + Observable o1 = Observable.from(1, 2, 3, 4, 5); + Observable o2 = Observable.from(6, 7, 8, 9, 10); + Observable o = Observable.merge(o1, o2).map(new Func1() { + + @Override + public String call(Integer t) { + assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool")); + return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); + } + }); - @Test - public void testIOThreadPool1() { + o.subscribeOn(Schedulers.threadPoolForComputation()).toBlockingObservable().forEach(new Action1() { + + @Override + public void call(String t) { + System.out.println("t: " + t); + } + }); + } + + @Test + public void testIOThreadPool1() { - Observable o1 = Observable. from(1, 2, 3, 4, 5); - Observable o2 = Observable. from(6, 7, 8, 9, 10); - Observable o = Observable. merge(o1, o2).map(new Func1() { + Observable o1 = Observable.from(1, 2, 3, 4, 5); + Observable o2 = Observable.from(6, 7, 8, 9, 10); + Observable o = Observable.merge(o1, o2).map(new Func1() { - @Override - public String call(Integer t) { - assertTrue(Thread.currentThread().getName().startsWith("RxIOThreadPool")); - return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); - } - }); + @Override + public String call(Integer t) { + assertTrue(Thread.currentThread().getName().startsWith("RxIOThreadPool")); + return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); + } + }); + + o.subscribeOn(Schedulers.threadPoolForIO()).toBlockingObservable().forEach(new Action1() { + + @Override + public void call(String t) { + System.out.println("t: " + t); + } + }); + } + + @Test + public void testMergeWithoutScheduler1() { + + final String currentThreadName = Thread.currentThread().getName(); + + Observable o1 = Observable.from(1, 2, 3, 4, 5); + Observable o2 = Observable.from(6, 7, 8, 9, 10); + Observable o = Observable.merge(o1, o2).map(new Func1() { + + @Override + public String call(Integer t) { + assertTrue(Thread.currentThread().getName().equals(currentThreadName)); + return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); + } + }); + + o.toBlockingObservable().forEach(new Action1() { + + @Override + public void call(String t) { + System.out.println("t: " + t); + } + }); + } - o.subscribeOn(Schedulers.threadPoolForIO()).toBlockingObservable().forEach(new Action1() { + @Test + public void testMergeWithImmediateScheduler1() { - @Override - public void call(String t) { - System.out.println("t: " + t); - } - }); - } + final String currentThreadName = Thread.currentThread().getName(); - @Test - public void testMergeWithoutScheduler1() { + Observable o1 = Observable.from(1, 2, 3, 4, 5); + Observable o2 = Observable.from(6, 7, 8, 9, 10); + Observable o = Observable.merge(o1, o2).subscribeOn(Schedulers.immediate()).map(new Func1() { - final String currentThreadName = Thread.currentThread().getName(); + @Override + public String call(Integer t) { + assertTrue(Thread.currentThread().getName().equals(currentThreadName)); + return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); + } + }); + + o.toBlockingObservable().forEach(new Action1() { + + @Override + public void call(String t) { + System.out.println("t: " + t); + } + }); + } + + @Test + public void testMergeWithCurrentThreadScheduler1() { + + final String currentThreadName = Thread.currentThread().getName(); + + Observable o1 = Observable.from(1, 2, 3, 4, 5); + Observable o2 = Observable.from(6, 7, 8, 9, 10); + Observable o = Observable.merge(o1, o2).subscribeOn(Schedulers.currentThread()).map(new Func1() { + + @Override + public String call(Integer t) { + assertTrue(Thread.currentThread().getName().equals(currentThreadName)); + return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); + } + }); - Observable o1 = Observable. from(1, 2, 3, 4, 5); - Observable o2 = Observable. from(6, 7, 8, 9, 10); - Observable o = Observable. merge(o1, o2).map(new Func1() { + o.toBlockingObservable().forEach(new Action1() { - @Override - public String call(Integer t) { - assertTrue(Thread.currentThread().getName().equals(currentThreadName)); - return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); - } - }); + @Override + public void call(String t) { + System.out.println("t: " + t); + } + }); + } - o.toBlockingObservable().forEach(new Action1() { + @Test + public void testMergeWithScheduler1() { - @Override - public void call(String t) { - System.out.println("t: " + t); - } - }); - } + final String currentThreadName = Thread.currentThread().getName(); - @Test - public void testMergeWithImmediateScheduler1() { + Observable o1 = Observable.from(1, 2, 3, 4, 5); + Observable o2 = Observable.from(6, 7, 8, 9, 10); + Observable o = Observable.merge(o1, o2).subscribeOn(Schedulers.threadPoolForComputation()).map(new Func1() { - final String currentThreadName = Thread.currentThread().getName(); + @Override + public String call(Integer t) { + assertFalse(Thread.currentThread().getName().equals(currentThreadName)); + assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool")); + return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); + } + }); - Observable o1 = Observable. from(1, 2, 3, 4, 5); - Observable o2 = Observable. from(6, 7, 8, 9, 10); - Observable o = Observable. merge(o1, o2).subscribeOn(Schedulers.immediate()).map(new Func1() { + o.toBlockingObservable().forEach(new Action1() { - @Override - public String call(Integer t) { - assertTrue(Thread.currentThread().getName().equals(currentThreadName)); - return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); - } - }); + @Override + public void call(String t) { + System.out.println("t: " + t); + } + }); + } - o.toBlockingObservable().forEach(new Action1() { + @Test + public void testSubscribeWithScheduler1() throws InterruptedException { - @Override - public void call(String t) { - System.out.println("t: " + t); - } - }); - } + final AtomicInteger count = new AtomicInteger(); - @Test - public void testMergeWithCurrentThreadScheduler1() { + Observable o1 = Observable.from(1, 2, 3, 4, 5); - final String currentThreadName = Thread.currentThread().getName(); + o1.subscribe(new Action1() { - Observable o1 = Observable. from(1, 2, 3, 4, 5); - Observable o2 = Observable. from(6, 7, 8, 9, 10); - Observable o = Observable. merge(o1, o2).subscribeOn(Schedulers.currentThread()).map(new Func1() { + @Override + public void call(Integer t) { + System.out.println("Thread: " + Thread.currentThread().getName()); + System.out.println("t: " + t); + count.incrementAndGet(); + } + }); - @Override - public String call(Integer t) { - assertTrue(Thread.currentThread().getName().equals(currentThreadName)); - return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); - } - }); + // the above should be blocking so we should see a count of 5 + assertEquals(5, count.get()); - o.toBlockingObservable().forEach(new Action1() { + count.set(0); - @Override - public void call(String t) { - System.out.println("t: " + t); - } - }); - } + // now we'll subscribe with a scheduler and it should be async - @Test - public void testMergeWithScheduler1() { + final String currentThreadName = Thread.currentThread().getName(); - final String currentThreadName = Thread.currentThread().getName(); + // latches for deterministically controlling the test below across threads + final CountDownLatch latch = new CountDownLatch(5); + final CountDownLatch first = new CountDownLatch(1); - Observable o1 = Observable. from(1, 2, 3, 4, 5); - Observable o2 = Observable. from(6, 7, 8, 9, 10); - Observable o = Observable. merge(o1, o2).subscribeOn(Schedulers.threadPoolForComputation()).map(new Func1() { + o1.subscribe(new Action1() { - @Override - public String call(Integer t) { - assertFalse(Thread.currentThread().getName().equals(currentThreadName)); - assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool")); - return "Value_" + t + "_Thread_" + Thread.currentThread().getName(); + @Override + public void call(Integer t) { + try { + // we block the first one so we can assert this executes asynchronously with a count + first.await(1000, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new RuntimeException("The latch should have released if we are async.", e); + } + assertFalse(Thread.currentThread().getName().equals(currentThreadName)); + assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool")); + System.out.println("Thread: " + Thread.currentThread().getName()); + System.out.println("t: " + t); + count.incrementAndGet(); + latch.countDown(); + } + }, Schedulers.threadPoolForComputation()); + + // assert we are async + assertEquals(0, count.get()); + // release the latch so it can go forward + first.countDown(); + + // wait for all 5 responses + latch.await(); + assertEquals(5, count.get()); + } + + @Test + public void testRecursiveScheduler1() { + Observable obs = Observable.create(new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(final Observer observer) { + return Schedulers.currentThread().schedule(0, new Func2() { + @Override + public Subscription call(Scheduler scheduler, Integer i) { + if (i > 42) { + observer.onCompleted(); + return Subscriptions.empty(); } - }); - o.toBlockingObservable().forEach(new Action1() { + observer.onNext(i); - @Override - public void call(String t) { - System.out.println("t: " + t); - } + return scheduler.schedule(i + 1, this); + } }); - } - - @Test - public void testSubscribeWithScheduler1() throws InterruptedException { - - final AtomicInteger count = new AtomicInteger(); + } + }); + + final AtomicInteger lastValue = new AtomicInteger(); + obs.toBlockingObservable().forEach(new Action1() { + + @Override + public void call(Integer v) { + System.out.println("Value: " + v); + lastValue.set(v); + } + }); + + assertEquals(42, lastValue.get()); + } + + @Test + public void testRecursiveScheduler2() throws InterruptedException { + // use latches instead of Thread.sleep + final CountDownLatch latch = new CountDownLatch(10); + final CountDownLatch completionLatch = new CountDownLatch(1); + + Observable obs = Observable.create(new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(final Observer observer) { + + return Schedulers.threadPoolForComputation().schedule(new BooleanSubscription(), new Func2() { + @Override + public Subscription call(Scheduler scheduler, BooleanSubscription cancel) { + if (cancel.isUnsubscribed()) { + observer.onCompleted(); + completionLatch.countDown(); + return Subscriptions.empty(); + } - Observable o1 = Observable. from(1, 2, 3, 4, 5); + observer.onNext(42); + latch.countDown(); - o1.subscribe(new Action1() { + // this will recursively schedule this task for execution again + scheduler.schedule(cancel, this); - @Override - public void call(Integer t) { - System.out.println("Thread: " + Thread.currentThread().getName()); - System.out.println("t: " + t); - count.incrementAndGet(); - } + return cancel; + } }); + } + }); + + final AtomicInteger count = new AtomicInteger(); + final AtomicBoolean completed = new AtomicBoolean(false); + Subscription subscribe = obs.subscribe(new Observer() { + @Override + public void onCompleted() { + System.out.println("Completed"); + completed.set(true); + } + + @Override + public void onError(Throwable e) { + System.out.println("Error"); + } + + @Override + public void onNext(Integer args) { + count.incrementAndGet(); + System.out.println(args); + } + }); + + if (!latch.await(5000, TimeUnit.MILLISECONDS)) { + fail("Timed out waiting on onNext latch"); + } - // the above should be blocking so we should see a count of 5 - assertEquals(5, count.get()); + // now unsubscribe and ensure it stops the recursive loop + subscribe.unsubscribe(); + System.out.println("unsubscribe"); - count.set(0); + if (!completionLatch.await(5000, TimeUnit.MILLISECONDS)) { + fail("Timed out waiting on completion latch"); + } - // now we'll subscribe with a scheduler and it should be async + // the count can be 10 or higher due to thread scheduling of the unsubscribe vs the scheduler looping to emit the count + assertTrue(count.get() >= 10); + assertTrue(completed.get()); + } - final String currentThreadName = Thread.currentThread().getName(); + @Test + public void testSchedulingWithDueTime() throws InterruptedException { - // latches for deterministically controlling the test below across threads - final CountDownLatch latch = new CountDownLatch(5); - final CountDownLatch first = new CountDownLatch(1); + final CountDownLatch latch = new CountDownLatch(5); + final AtomicInteger counter = new AtomicInteger(); - o1.subscribe(new Action1() { + long start = System.currentTimeMillis(); - @Override - public void call(Integer t) { - try { - // we block the first one so we can assert this executes asynchronously with a count - first.await(1000, TimeUnit.SECONDS); - } catch (InterruptedException e) { - throw new RuntimeException("The latch should have released if we are async.", e); - } - assertFalse(Thread.currentThread().getName().equals(currentThreadName)); - assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool")); - System.out.println("Thread: " + Thread.currentThread().getName()); - System.out.println("t: " + t); - count.incrementAndGet(); - latch.countDown(); - } - }, Schedulers.threadPoolForComputation()); + Schedulers.threadPoolForComputation().schedule(null, new Func2() { - // assert we are async - assertEquals(0, count.get()); - // release the latch so it can go forward - first.countDown(); + @Override + public Subscription call(Scheduler scheduler, String state) { + System.out.println("doing work"); + counter.incrementAndGet(); + latch.countDown(); + if (latch.getCount() == 0) { + return Subscriptions.empty(); + } else { + return scheduler.schedule(state, this, new Date(System.currentTimeMillis() + 50)); + } + } + }, new Date(System.currentTimeMillis() + 100)); - // wait for all 5 responses - latch.await(); - assertEquals(5, count.get()); + if (!latch.await(3000, TimeUnit.MILLISECONDS)) { + fail("didn't execute ... timed out"); } - @Test - public void testRecursiveScheduler1() { - Observable obs = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(final Observer observer) { - return Schedulers.currentThread().schedule(0, new Func2() { - @Override - public Subscription call(Scheduler scheduler, Integer i) { - if (i > 42) { - observer.onCompleted(); - return Subscriptions.empty(); - } - - observer.onNext(i); - - return scheduler.schedule(i + 1, this); - } - }); - } - }); - - final AtomicInteger lastValue = new AtomicInteger(); - obs.toBlockingObservable().forEach(new Action1() { + long end = System.currentTimeMillis(); - @Override - public void call(Integer v) { - System.out.println("Value: " + v); - lastValue.set(v); - } - }); - - assertEquals(42, lastValue.get()); + assertEquals(5, counter.get()); + if ((end - start) < 250) { + fail("it should have taken over 250ms since each step was scheduled 50ms in the future"); } + } - @Test - public void testRecursiveScheduler2() throws InterruptedException { - // use latches instead of Thread.sleep - final CountDownLatch latch = new CountDownLatch(10); - final CountDownLatch completionLatch = new CountDownLatch(1); + @Test + public void testConcurrentOnNextFailsValidation() throws InterruptedException { - Observable obs = Observable.create(new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(final Observer observer) { - - return Schedulers.threadPoolForComputation().schedule(new BooleanSubscription(), new Func2() { - @Override - public Subscription call(Scheduler scheduler, BooleanSubscription cancel) { - if (cancel.isUnsubscribed()) { - observer.onCompleted(); - completionLatch.countDown(); - return Subscriptions.empty(); - } - - observer.onNext(42); - latch.countDown(); - - // this will recursively schedule this task for execution again - scheduler.schedule(cancel, this); - - return cancel; - } - }); - } - }); + final int count = 10; + final CountDownLatch latch = new CountDownLatch(count); + Observable o = Observable.create(new OnSubscribeFunc() { - final AtomicInteger count = new AtomicInteger(); - final AtomicBoolean completed = new AtomicBoolean(false); - Subscription subscribe = obs.subscribe(new Observer() { - @Override - public void onCompleted() { - System.out.println("Completed"); - completed.set(true); - } + @Override + public Subscription onSubscribe(final Observer observer) { + for (int i = 0; i < count; i++) { + final int v = i; + new Thread(new Runnable() { @Override - public void onError(Throwable e) { - System.out.println("Error"); - } + public void run() { + observer.onNext("v: " + v); - @Override - public void onNext(Integer args) { - count.incrementAndGet(); - System.out.println(args); + latch.countDown(); } - }); - - if (!latch.await(5000, TimeUnit.MILLISECONDS)) { - fail("Timed out waiting on onNext latch"); + }).start(); } + return Subscriptions.empty(); + } + }); - // now unsubscribe and ensure it stops the recursive loop - subscribe.unsubscribe(); - System.out.println("unsubscribe"); - - if (!completionLatch.await(5000, TimeUnit.MILLISECONDS)) { - fail("Timed out waiting on completion latch"); - } + ConcurrentObserverValidator observer = new ConcurrentObserverValidator(); + // this should call onNext concurrently + o.subscribe(observer); - // the count can be 10 or higher due to thread scheduling of the unsubscribe vs the scheduler looping to emit the count - assertTrue(count.get() >= 10); - assertTrue(completed.get()); + if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) { + fail("timed out"); } - @Test - public void testSchedulingWithDueTime() throws InterruptedException { + if (observer.error.get() == null) { + fail("We expected error messages due to concurrency"); + } + } - final CountDownLatch latch = new CountDownLatch(5); - final AtomicInteger counter = new AtomicInteger(); + @Test + public void testObserveOn() throws InterruptedException { - long start = System.currentTimeMillis(); + Observable o = Observable.from("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"); - Schedulers.threadPoolForComputation().schedule(null, new Func2() { + ConcurrentObserverValidator observer = new ConcurrentObserverValidator(); - @Override - public Subscription call(Scheduler scheduler, String state) { - System.out.println("doing work"); - counter.incrementAndGet(); - latch.countDown(); - if (latch.getCount() == 0) { - return Subscriptions.empty(); - } else { - return scheduler.schedule(state, this, new Date(System.currentTimeMillis() + 50)); - } - } - }, new Date(System.currentTimeMillis() + 100)); + o.observeOn(Schedulers.threadPoolForComputation()).subscribe(observer); - if (!latch.await(3000, TimeUnit.MILLISECONDS)) { - fail("didn't execute ... timed out"); - } - - long end = System.currentTimeMillis(); + if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) { + fail("timed out"); + } - assertEquals(5, counter.get()); - if ((end - start) < 250) { - fail("it should have taken over 250ms since each step was scheduled 50ms in the future"); - } + if (observer.error.get() != null) { + observer.error.get().printStackTrace(); + fail("Error: " + observer.error.get().getMessage()); } + } - @Test - public void testConcurrentOnNextFailsValidation() throws InterruptedException { + @Test + public void testSubscribeOnNestedConcurrency() throws InterruptedException { - final int count = 10; - final CountDownLatch latch = new CountDownLatch(count); - Observable o = Observable.create(new OnSubscribeFunc() { + Observable o = Observable.from("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten") + .mapMany(new Func1>() { - @Override - public Subscription onSubscribe(final Observer observer) { - for (int i = 0; i < count; i++) { - final int v = i; - new Thread(new Runnable() { - - @Override - public void run() { - observer.onNext("v: " + v); - - latch.countDown(); - } - }).start(); - } + @Override + public Observable call(final String v) { + return Observable.create(new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(final Observer observer) { + observer.onNext("value_after_map-" + v); + observer.onCompleted(); return Subscriptions.empty(); - } + } + }).subscribeOn(Schedulers.newThread()); // subscribe on a new thread + } }); - ConcurrentObserverValidator observer = new ConcurrentObserverValidator(); - // this should call onNext concurrently - o.subscribe(observer); + ConcurrentObserverValidator observer = new ConcurrentObserverValidator(); - if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) { - fail("timed out"); - } + o.subscribe(observer); - if (observer.error.get() == null) { - fail("We expected error messages due to concurrency"); - } + if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) { + fail("timed out"); } - @Test - public void testObserveOn() throws InterruptedException { - - Observable o = Observable.from("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"); - - ConcurrentObserverValidator observer = new ConcurrentObserverValidator(); - - o.observeOn(Schedulers.threadPoolForComputation()).subscribe(observer); - - if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) { - fail("timed out"); - } - - if (observer.error.get() != null) { - observer.error.get().printStackTrace(); - fail("Error: " + observer.error.get().getMessage()); - } + if (observer.error.get() != null) { + observer.error.get().printStackTrace(); + fail("Error: " + observer.error.get().getMessage()); } + } - @Test - public void testSubscribeOnNestedConcurrency() throws InterruptedException { - - Observable o = Observable.from("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten") - .mapMany(new Func1>() { + @Test + public void testRecursion() { + TestScheduler s = new TestScheduler(); - @Override - public Observable call(final String v) { - return Observable.create(new OnSubscribeFunc() { + final AtomicInteger counter = new AtomicInteger(0); - @Override - public Subscription onSubscribe(final Observer observer) { - observer.onNext("value_after_map-" + v); - observer.onCompleted(); - return Subscriptions.empty(); - } - }).subscribeOn(Schedulers.newThread()); // subscribe on a new thread - } - }); + Subscription subscription = s.schedule(new Action1() { - ConcurrentObserverValidator observer = new ConcurrentObserverValidator(); + @Override + public void call(Action0 self) { + counter.incrementAndGet(); + System.out.println("counter: " + counter.get()); + self.call(); + } - o.subscribe(observer); + }); + subscription.unsubscribe(); + assertEquals(0, counter.get()); + } - if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) { - fail("timed out"); - } - - if (observer.error.get() != null) { - observer.error.get().printStackTrace(); - fail("Error: " + observer.error.get().getMessage()); - } - } - @Test - public void testRecursion() { - TestScheduler s = new TestScheduler(); + /** + * Used to determine if onNext is being invoked concurrently. + * + * @param + */ + private static class ConcurrentObserverValidator implements Observer { - final AtomicInteger counter = new AtomicInteger(0); + final AtomicInteger concurrentCounter = new AtomicInteger(); + final AtomicReference error = new AtomicReference(); + final CountDownLatch completed = new CountDownLatch(1); - Subscription subscription = s.schedule(new Action1() { - - @Override - public void call(Action0 self) { - counter.incrementAndGet(); - System.out.println("counter: " + counter.get()); - self.call(); - } - - }); - subscription.unsubscribe(); - assertEquals(0, counter.get()); + @Override + public void onCompleted() { + completed.countDown(); } + @Override + public void onError(Throwable e) { + completed.countDown(); + error.set(e); + } - /** - * Used to determine if onNext is being invoked concurrently. - * - * @param - */ - private static class ConcurrentObserverValidator implements Observer { - - final AtomicInteger concurrentCounter = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); - final CountDownLatch completed = new CountDownLatch(1); - - @Override - public void onCompleted() { - completed.countDown(); - } - - @Override - public void onError(Throwable e) { - completed.countDown(); - error.set(e); - } - - @Override - public void onNext(T args) { - int count = concurrentCounter.incrementAndGet(); - System.out.println("ConcurrentObserverValidator.onNext: " + args); - if (count > 1) { - onError(new RuntimeException("we should not have concurrent execution of onNext")); - } - try { - try { - // take some time so other onNext calls could pile up (I haven't yet thought of a way to do this without sleeping) - Thread.sleep(50); - } catch (InterruptedException e) { - // ignore - } - } finally { - concurrentCounter.decrementAndGet(); - } + @Override + public void onNext(T args) { + int count = concurrentCounter.incrementAndGet(); + System.out.println("ConcurrentObserverValidator.onNext: " + args); + if (count > 1) { + onError(new RuntimeException("we should not have concurrent execution of onNext")); + } + try { + try { + // take some time so other onNext calls could pile up (I haven't yet thought of a way to do this without sleeping) + Thread.sleep(50); + } catch (InterruptedException e) { + // ignore } - + } finally { + concurrentCounter.decrementAndGet(); + } } + + } } diff --git a/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java b/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java index b9a9b1a1c2..de34142a93 100644 --- a/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java +++ b/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java @@ -1,7 +1,129 @@ package rx.concurrency; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import rx.util.functions.Action0; + +import java.util.concurrent.TimeUnit; + +import static org.mockito.Mockito.*; -@Ignore("WIP") public class CurrentThreadSchedulerTest { + + @Test + public void testNestedActions() { + final CurrentThreadScheduler scheduler = new CurrentThreadScheduler(); + + final Action0 firstStepStart = mock(Action0.class); + final Action0 firstStepEnd = mock(Action0.class); + + final Action0 secondStepStart = mock(Action0.class); + final Action0 secondStepEnd = mock(Action0.class); + + final Action0 thirdStepStart = mock(Action0.class); + final Action0 thirdStepEnd = mock(Action0.class); + + final Action0 firstAction = new Action0() { + @Override + public void call() { + firstStepStart.call(); + firstStepEnd.call(); + } + }; + final Action0 secondAction = new Action0() { + @Override + public void call() { + secondStepStart.call(); + scheduler.schedule(firstAction); + secondStepEnd.call(); + + } + }; + final Action0 thirdAction = new Action0() { + @Override + public void call() { + thirdStepStart.call(); + scheduler.schedule(secondAction); + thirdStepEnd.call(); + } + }; + + InOrder inOrder = inOrder(firstStepStart, firstStepEnd, secondStepStart, secondStepEnd, thirdStepStart, thirdStepEnd); + + scheduler.schedule(thirdAction); + + inOrder.verify(thirdStepStart, times(1)).call(); + inOrder.verify(thirdStepEnd, times(1)).call(); + inOrder.verify(secondStepStart, times(1)).call(); + inOrder.verify(secondStepEnd, times(1)).call(); + inOrder.verify(firstStepStart, times(1)).call(); + inOrder.verify(firstStepEnd, times(1)).call(); + } + + @Test + public void testSequenceOfActions() { + final CurrentThreadScheduler scheduler = new CurrentThreadScheduler(); + + final Action0 first = mock(Action0.class); + final Action0 second = mock(Action0.class); + + scheduler.schedule(first); + scheduler.schedule(second); + + verify(first, times(1)).call(); + verify(second, times(1)).call(); + + } + + @Test + public void testSequenceOfDelayedActions() { + final CurrentThreadScheduler scheduler = new CurrentThreadScheduler(); + + final Action0 first = mock(Action0.class); + final Action0 second = mock(Action0.class); + + scheduler.schedule(new Action0() { + @Override + public void call() { + scheduler.schedule(first, 30, TimeUnit.MILLISECONDS); + scheduler.schedule(second, 10, TimeUnit.MILLISECONDS); + } + }); + + InOrder inOrder = inOrder(first, second); + + inOrder.verify(second, times(1)).call(); + inOrder.verify(first, times(1)).call(); + + + } + + @Test + public void testMixOfDelayedAndNonDelayedActions() { + final CurrentThreadScheduler scheduler = new CurrentThreadScheduler(); + + final Action0 first = mock(Action0.class); + final Action0 second = mock(Action0.class); + final Action0 third = mock(Action0.class); + final Action0 fourth = mock(Action0.class); + + scheduler.schedule(new Action0() { + @Override + public void call() { + scheduler.schedule(first); + scheduler.schedule(second, 300, TimeUnit.MILLISECONDS); + scheduler.schedule(third, 100, TimeUnit.MILLISECONDS); + scheduler.schedule(fourth); + } + }); + + InOrder inOrder = inOrder(first, second, third, fourth); + + inOrder.verify(first, times(1)).call(); + inOrder.verify(fourth, times(1)).call(); + inOrder.verify(third, times(1)).call(); + inOrder.verify(second, times(1)).call(); + + + } } diff --git a/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java b/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java index 036103e540..df08b8aa31 100644 --- a/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java +++ b/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java @@ -1,7 +1,59 @@ package rx.concurrency; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import rx.util.functions.Action0; + +import static org.mockito.Mockito.*; -@Ignore("WIP") public class ImmediateSchedulerTest { + @Test + public void testNestedActions() { + final ImmediateScheduler scheduler = new ImmediateScheduler(); + + final Action0 firstStepStart = mock(Action0.class); + final Action0 firstStepEnd = mock(Action0.class); + + final Action0 secondStepStart = mock(Action0.class); + final Action0 secondStepEnd = mock(Action0.class); + + final Action0 thirdStepStart = mock(Action0.class); + final Action0 thirdStepEnd = mock(Action0.class); + + final Action0 firstAction = new Action0() { + @Override + public void call() { + firstStepStart.call(); + firstStepEnd.call(); + } + }; + final Action0 secondAction = new Action0() { + @Override + public void call() { + secondStepStart.call(); + scheduler.schedule(firstAction); + secondStepEnd.call(); + + } + }; + final Action0 thirdAction = new Action0() { + @Override + public void call() { + thirdStepStart.call(); + scheduler.schedule(secondAction); + thirdStepEnd.call(); + } + }; + + InOrder inOrder = inOrder(firstStepStart, firstStepEnd, secondStepStart, secondStepEnd, thirdStepStart, thirdStepEnd); + + scheduler.schedule(thirdAction); + + inOrder.verify(thirdStepStart, times(1)).call(); + inOrder.verify(secondStepStart, times(1)).call(); + inOrder.verify(firstStepStart, times(1)).call(); + inOrder.verify(firstStepEnd, times(1)).call(); + inOrder.verify(secondStepEnd, times(1)).call(); + inOrder.verify(thirdStepEnd, times(1)).call(); + } } diff --git a/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java b/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java index aa38f711ad..0c6f885de9 100644 --- a/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java +++ b/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java @@ -1,7 +1,248 @@ package rx.observables; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.BooleanSubscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action1; +import rx.util.functions.Func1; + +import java.util.Iterator; + +import static org.junit.Assert.*; -@Ignore("WIP") public class BlockingObservableTest { + + @Mock + Observer w; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testLast() { + BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three")); + + assertEquals("three", obs.last()); + } + + @Test + public void testLastEmptyObservable() { + BlockingObservable obs = BlockingObservable.from(Observable.empty()); + + assertNull(obs.last()); + } + + @Test + public void testLastOrDefault() { + BlockingObservable observable = BlockingObservable.from(Observable.from(1, 0, -1)); + int last = observable.lastOrDefault(-100, new Func1() { + @Override + public Boolean call(Integer args) { + return args >= 0; + } + }); + assertEquals(0, last); + } + + @Test + public void testLastOrDefault1() { + BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "three")); + assertEquals("three", observable.lastOrDefault("default")); + } + + @Test + public void testLastOrDefault2() { + BlockingObservable observable = BlockingObservable.from(Observable.empty()); + assertEquals("default", observable.lastOrDefault("default")); + } + + @Test + public void testLastOrDefaultWithPredicate() { + BlockingObservable observable = BlockingObservable.from(Observable.from(1, 0, -1)); + int last = observable.lastOrDefault(0, new Func1() { + @Override + public Boolean call(Integer args) { + return args < 0; + } + }); + + assertEquals(-1, last); + } + + @Test + public void testLastOrDefaultWrongPredicate() { + BlockingObservable observable = BlockingObservable.from(Observable.from(-1, -2, -3)); + int last = observable.lastOrDefault(0, new Func1() { + @Override + public Boolean call(Integer args) { + return args >= 0; + } + }); + assertEquals(0, last); + } + + @Test + public void testLastWithPredicate() { + BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three")); + + assertEquals("two", obs.last(new Func1() { + @Override + public Boolean call(String s) { + return s.length() == 3; + } + })); + } + + public void testSingle() { + BlockingObservable observable = BlockingObservable.from(Observable.from("one")); + assertEquals("one", observable.single()); + } + + @Test + public void testSingleDefault() { + BlockingObservable observable = BlockingObservable.from(Observable.empty()); + assertEquals("default", observable.singleOrDefault("default")); + } + + @Test(expected = IllegalStateException.class) + public void testSingleDefaultPredicateMatchesMoreThanOne() { + BlockingObservable.from(Observable.from("one", "two")).singleOrDefault("default", new Func1() { + @Override + public Boolean call(String args) { + return args.length() == 3; + } + }); + } + + @Test + public void testSingleDefaultPredicateMatchesNothing() { + BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two")); + String result = observable.singleOrDefault("default", new Func1() { + @Override + public Boolean call(String args) { + return args.length() == 4; + } + }); + assertEquals("default", result); + } + + @Test(expected = IllegalStateException.class) + public void testSingleDefaultWithMoreThanOne() { + BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "three")); + observable.singleOrDefault("default"); + } + + @Test + public void testSingleWithPredicateDefault() { + BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "four")); + assertEquals("four", observable.single(new Func1() { + @Override + public Boolean call(String s) { + return s.length() == 4; + } + })); + } + + @Test(expected = IllegalStateException.class) + public void testSingleWrong() { + BlockingObservable observable = BlockingObservable.from(Observable.from(1, 2)); + observable.single(); + } + + @Test(expected = IllegalStateException.class) + public void testSingleWrongPredicate() { + BlockingObservable observable = BlockingObservable.from(Observable.from(-1)); + observable.single(new Func1() { + @Override + public Boolean call(Integer args) { + return args > 0; + } + }); + } + + @Test + public void testToIterable() { + BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three")); + + Iterator it = obs.toIterable().iterator(); + + assertEquals(true, it.hasNext()); + assertEquals("one", it.next()); + + assertEquals(true, it.hasNext()); + assertEquals("two", it.next()); + + assertEquals(true, it.hasNext()); + assertEquals("three", it.next()); + + assertEquals(false, it.hasNext()); + + } + + @Test(expected = TestException.class) + public void testToIterableWithException() { + BlockingObservable obs = BlockingObservable.from(Observable.create(new Observable.OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onError(new TestException()); + return Subscriptions.empty(); + } + })); + + Iterator it = obs.toIterable().iterator(); + + assertEquals(true, it.hasNext()); + assertEquals("one", it.next()); + + assertEquals(true, it.hasNext()); + it.next(); + + } + + @Test + public void testForEachWithError() { + try { + BlockingObservable.from(Observable.create(new Observable.OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(final Observer observer) { + final BooleanSubscription subscription = new BooleanSubscription(); + new Thread(new Runnable() { + + @Override + public void run() { + observer.onNext("one"); + observer.onNext("two"); + observer.onNext("three"); + observer.onCompleted(); + } + }).start(); + return subscription; + } + })).forEach(new Action1() { + + @Override + public void call(String t1) { + throw new RuntimeException("fail"); + } + }); + fail("we expect an exception to be thrown"); + } catch (Throwable e) { + // do nothing as we expect this + } + } + + private static class TestException extends RuntimeException { + private static final long serialVersionUID = 1L; + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationAllTest.java b/rxjava-core/src/test/java/rx/operators/OperationAllTest.java index ffb0b56798..23bde39e59 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationAllTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationAllTest.java @@ -1,7 +1,84 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; + +import static org.mockito.Mockito.*; +import static rx.operators.OperationAll.all; -@Ignore("WIP") public class OperationAllTest { + + @Test + @SuppressWarnings("unchecked") + public void testAll() { + Observable obs = Observable.from("one", "two", "six"); + + Observer observer = mock(Observer.class); + Observable.create(all(obs, new Func1() { + @Override + public Boolean call(String s) { + return s.length() == 3; + } + })).subscribe(observer); + + verify(observer).onNext(true); + verify(observer).onCompleted(); + verifyNoMoreInteractions(observer); + } + + @Test + @SuppressWarnings("unchecked") + public void testNotAll() { + Observable obs = Observable.from("one", "two", "three", "six"); + + Observer observer = mock(Observer.class); + Observable.create(all(obs, new Func1() { + @Override + public Boolean call(String s) { + return s.length() == 3; + } + })).subscribe(observer); + + verify(observer).onNext(false); + verify(observer).onCompleted(); + verifyNoMoreInteractions(observer); + } + + @Test + @SuppressWarnings("unchecked") + public void testEmpty() { + Observable obs = Observable.empty(); + + Observer observer = mock(Observer.class); + Observable.create(all(obs, new Func1() { + @Override + public Boolean call(String s) { + return s.length() == 3; + } + })).subscribe(observer); + + verify(observer).onNext(true); + verify(observer).onCompleted(); + verifyNoMoreInteractions(observer); + } + + @Test + @SuppressWarnings("unchecked") + public void testError() { + Throwable error = new Throwable(); + Observable obs = Observable.error(error); + + Observer observer = mock(Observer.class); + Observable.create(all(obs, new Func1() { + @Override + public Boolean call(String s) { + return s.length() == 3; + } + })).subscribe(observer); + + verify(observer).onError(error); + verifyNoMoreInteractions(observer); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java b/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java index 98d6e72fa5..801a35d0c3 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java @@ -1,7 +1,182 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; + +import static org.mockito.Mockito.*; +import static rx.operators.OperationAny.any; +import static rx.operators.OperationAny.*; -@Ignore("WIP") public class OperationAnyTest { + + @Test + public void testAnyWithTwoItems() { + Observable w = Observable.from(1, 2); + Observable observable = Observable.create(any(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(false); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testIsEmptyWithTwoItems() { + Observable w = Observable.from(1, 2); + Observable observable = Observable.create(isEmpty(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(true); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testAnyWithOneItem() { + Observable w = Observable.from(1); + Observable observable = Observable.create(any(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(false); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testIsEmptyWithOneItem() { + Observable w = Observable.from(1); + Observable observable = Observable.create(isEmpty(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(true); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testAnyWithEmpty() { + Observable w = Observable.empty(); + Observable observable = Observable.create(any(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onNext(true); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testIsEmptyWithEmpty() { + Observable w = Observable.empty(); + Observable observable = Observable.create(isEmpty(w)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onNext(false); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testAnyWithPredicate1() { + Observable w = Observable.from(1, 2, 3); + Observable observable = Observable.create(any(w, + new Func1() { + + @Override + public Boolean call(Integer t1) { + return t1 < 2; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(false); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testExists1() { + Observable w = Observable.from(1, 2, 3); + Observable observable = Observable.create(exists(w, + new Func1() { + + @Override + public Boolean call(Integer t1) { + return t1 < 2; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(false); + verify(aObserver, times(1)).onNext(true); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testAnyWithPredicate2() { + Observable w = Observable.from(1, 2, 3); + Observable observable = Observable.create(any(w, + new Func1() { + + @Override + public Boolean call(Integer t1) { + return t1 < 1; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onNext(true); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testAnyWithEmptyAndPredicate() { + // If the source is empty, always output false. + Observable w = Observable.empty(); + Observable observable = Observable.create(any(w, + new Func1() { + + @Override + public Boolean call(Integer t1) { + return true; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(false); + verify(aObserver, never()).onNext(true); + verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java b/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java index 6fa01dfc92..45e23bf2b9 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java @@ -1,7 +1,111 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.anyFloat; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.*; +import static rx.operators.OperationAverage.*; + -@Ignore("WIP") public class OperationAverageTest { + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer wl = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer wf = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer wd = mock(Observer.class); + + @Test + public void testAverageOfAFewInts() throws Throwable { + Observable src = Observable.from(1, 2, 3, 4, 6); + average(src).subscribe(w); + + verify(w, times(1)).onNext(anyInt()); + verify(w).onNext(3); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testEmptyAverage() throws Throwable { + Observable src = Observable.empty(); + average(src).subscribe(w); + + verify(w, never()).onNext(anyInt()); + verify(w, times(1)).onError(any(ArithmeticException.class)); + verify(w, never()).onCompleted(); + } + + @Test + public void testAverageOfAFewLongs() throws Throwable { + Observable src = Observable.from(1L, 2L, 3L, 4L, 6L); + averageLongs(src).subscribe(wl); + + verify(wl, times(1)).onNext(anyLong()); + verify(wl).onNext(3L); + verify(wl, never()).onError(any(Throwable.class)); + verify(wl, times(1)).onCompleted(); + } + + @Test + public void testEmptyAverageLongs() throws Throwable { + Observable src = Observable.empty(); + averageLongs(src).subscribe(wl); + + verify(wl, never()).onNext(anyLong()); + verify(wl, times(1)).onError(any(ArithmeticException.class)); + verify(wl, never()).onCompleted(); + } + + @Test + public void testAverageOfAFewFloats() throws Throwable { + Observable src = Observable.from(1.0f, 2.0f); + averageFloats(src).subscribe(wf); + + verify(wf, times(1)).onNext(anyFloat()); + verify(wf).onNext(1.5f); + verify(wf, never()).onError(any(Throwable.class)); + verify(wf, times(1)).onCompleted(); + } + + + @Test + public void testEmptyAverageFloats() throws Throwable { + Observable src = Observable.empty(); + averageFloats(src).subscribe(wf); + + verify(wf, never()).onNext(anyFloat()); + verify(wf, times(1)).onError(any(ArithmeticException.class)); + verify(wf, never()).onCompleted(); + } + + @Test + public void testAverageOfAFewDoubles() throws Throwable { + Observable src = Observable.from(1.0d, 2.0d); + averageDoubles(src).subscribe(wd); + + verify(wd, times(1)).onNext(anyDouble()); + verify(wd).onNext(1.5d); + verify(wd, never()).onError(any(Throwable.class)); + verify(wd, times(1)).onCompleted(); + } + + @Test + public void testEmptyAverageDoubles() throws Throwable { + Observable src = Observable.empty(); + averageDoubles(src).subscribe(wd); + + verify(wd, never()).onNext(anyDouble()); + verify(wd, times(1)).onError(any(ArithmeticException.class)); + verify(wd, never()).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java b/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java index d096cd666a..fdc44e4014 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java @@ -1,7 +1,350 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.concurrency.TestScheduler; +import rx.subscriptions.Subscriptions; +import rx.util.Closing; +import rx.util.Closings; +import rx.util.Opening; +import rx.util.Openings; +import rx.util.functions.Action0; +import rx.util.functions.Action1; +import rx.util.functions.Func0; +import rx.util.functions.Func1; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertFalse; +import static rx.operators.OperationBuffer.buffer; -@Ignore("WIP") public class OperationBufferTest { + + private Observer> observer; + private TestScheduler scheduler; + + @Before + @SuppressWarnings("unchecked") + public void before() { + observer = Mockito.mock(Observer.class); + scheduler = new TestScheduler(); + } + + @Test + public void testComplete() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + observer.onCompleted(); + return Subscriptions.empty(); + } + }); + + Observable> buffered = Observable.create(buffer(source, 3, 3)); + buffered.subscribe(observer); + + Mockito.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); + Mockito.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); + Mockito.verify(observer, Mockito.times(1)).onCompleted(); + } + + @Test + public void testSkipAndCountOverlappingBuffers() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onNext("two"); + observer.onNext("three"); + observer.onNext("four"); + observer.onNext("five"); + return Subscriptions.empty(); + } + }); + + Observable> buffered = Observable.create(buffer(source, 3, 1)); + buffered.subscribe(observer); + + InOrder inOrder = Mockito.inOrder(observer); + inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three")); + inOrder.verify(observer, Mockito.times(1)).onNext(list("two", "three", "four")); + inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four", "five")); + inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); + inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); + inOrder.verify(observer, Mockito.never()).onCompleted(); + } + + @Test + public void testSkipAndCountGaplessBuffers() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onNext("two"); + observer.onNext("three"); + observer.onNext("four"); + observer.onNext("five"); + observer.onCompleted(); + return Subscriptions.empty(); + } + }); + + Observable> buffered = Observable.create(buffer(source, 3, 3)); + buffered.subscribe(observer); + + InOrder inOrder = Mockito.inOrder(observer); + inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three")); + inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five")); + inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); + inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); + inOrder.verify(observer, Mockito.times(1)).onCompleted(); + } + + @Test + public void testSkipAndCountBuffersWithGaps() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onNext("two"); + observer.onNext("three"); + observer.onNext("four"); + observer.onNext("five"); + observer.onCompleted(); + return Subscriptions.empty(); + } + }); + + Observable> buffered = Observable.create(buffer(source, 2, 3)); + buffered.subscribe(observer); + + InOrder inOrder = Mockito.inOrder(observer); + inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two")); + inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five")); + inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); + inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); + inOrder.verify(observer, Mockito.times(1)).onCompleted(); + } + + @Test + public void testTimedAndCount() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 10); + push(observer, "two", 90); + push(observer, "three", 110); + push(observer, "four", 190); + push(observer, "five", 210); + complete(observer, 250); + return Subscriptions.empty(); + } + }); + + Observable> buffered = Observable.create(buffer(source, 100, TimeUnit.MILLISECONDS, 2, scheduler)); + buffered.subscribe(observer); + + InOrder inOrder = Mockito.inOrder(observer); + scheduler.advanceTimeTo(100, TimeUnit.MILLISECONDS); + inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two")); + + scheduler.advanceTimeTo(200, TimeUnit.MILLISECONDS); + inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four")); + + scheduler.advanceTimeTo(300, TimeUnit.MILLISECONDS); + inOrder.verify(observer, Mockito.times(1)).onNext(list("five")); + inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); + inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); + inOrder.verify(observer, Mockito.times(1)).onCompleted(); + } + + @Test + public void testTimed() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 98); + push(observer, "two", 99); + push(observer, "three", 100); + push(observer, "four", 101); + push(observer, "five", 102); + complete(observer, 150); + return Subscriptions.empty(); + } + }); + + Observable> buffered = Observable.create(buffer(source, 100, TimeUnit.MILLISECONDS, scheduler)); + buffered.subscribe(observer); + + InOrder inOrder = Mockito.inOrder(observer); + scheduler.advanceTimeTo(101, TimeUnit.MILLISECONDS); + inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three")); + + scheduler.advanceTimeTo(201, TimeUnit.MILLISECONDS); + inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five")); + inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); + inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); + inOrder.verify(observer, Mockito.times(1)).onCompleted(); + } + + @Test + public void testObservableBasedOpenerAndCloser() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 10); + push(observer, "two", 60); + push(observer, "three", 110); + push(observer, "four", 160); + push(observer, "five", 210); + complete(observer, 500); + return Subscriptions.empty(); + } + }); + + Observable openings = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, Openings.create(), 50); + push(observer, Openings.create(), 200); + complete(observer, 250); + return Subscriptions.empty(); + } + }); + + Func1> closer = new Func1>() { + @Override + public Observable call(Opening opening) { + return Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, Closings.create(), 100); + complete(observer, 101); + return Subscriptions.empty(); + } + }); + } + }; + + Observable> buffered = Observable.create(buffer(source, openings, closer)); + buffered.subscribe(observer); + + InOrder inOrder = Mockito.inOrder(observer); + scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); + inOrder.verify(observer, Mockito.times(1)).onNext(list("two", "three")); + inOrder.verify(observer, Mockito.times(1)).onNext(list("five")); + inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); + inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); + inOrder.verify(observer, Mockito.times(1)).onCompleted(); + } + + @Test + public void testObservableBasedCloser() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 10); + push(observer, "two", 60); + push(observer, "three", 110); + push(observer, "four", 160); + push(observer, "five", 210); + complete(observer, 250); + return Subscriptions.empty(); + } + }); + + Func0> closer = new Func0>() { + @Override + public Observable call() { + return Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, Closings.create(), 100); + complete(observer, 101); + return Subscriptions.empty(); + } + }); + } + }; + + Observable> buffered = Observable.create(buffer(source, closer)); + buffered.subscribe(observer); + + InOrder inOrder = Mockito.inOrder(observer); + scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); + inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two")); + inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four")); + inOrder.verify(observer, Mockito.times(1)).onNext(list("five")); + inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class)); + inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class)); + inOrder.verify(observer, Mockito.times(1)).onCompleted(); + } + + @Test + public void testLongTimeAction() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + LongTimeAction action = new LongTimeAction(latch); + Observable.from(1).buffer(10, TimeUnit.MILLISECONDS, 10) + .subscribe(action); + latch.await(); + assertFalse(action.fail); + } + + private static class LongTimeAction implements Action1> { + + CountDownLatch latch; + boolean fail = false; + + public LongTimeAction(CountDownLatch latch) { + this.latch = latch; + } + + @Override + public void call(List t1) { + try { + if (fail) { + return; + } + Thread.sleep(200); + } catch (InterruptedException e) { + fail = true; + } finally { + latch.countDown(); + } + } + } + + private List list(String... args) { + List list = new ArrayList(); + for (String arg : args) { + list.add(arg); + } + return list; + } + + private void push(final Observer observer, final T value, int delay) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onNext(value); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void complete(final Observer observer, int delay) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onCompleted(); + } + }, delay, TimeUnit.MILLISECONDS); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java b/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java index 4a754528e5..9f9ab051a1 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java @@ -1,7 +1,72 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.BooleanSubscription; +import rx.util.functions.Action1; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static rx.operators.OperationCache.cache; -@Ignore("WIP") public class OperationCacheTest { + + @Test + public void testCache() throws InterruptedException { + final AtomicInteger counter = new AtomicInteger(); + Observable o = Observable.create(cache(Observable.create(new Observable.OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(final Observer observer) { + final BooleanSubscription subscription = new BooleanSubscription(); + new Thread(new Runnable() { + + @Override + public void run() { + counter.incrementAndGet(); + System.out.println("published observable being executed"); + observer.onNext("one"); + observer.onCompleted(); + } + }).start(); + return subscription; + } + }))); + + // we then expect the following 2 subscriptions to get that same value + final CountDownLatch latch = new CountDownLatch(2); + + // subscribe once + o.subscribe(new Action1() { + + @Override + public void call(String v) { + assertEquals("one", v); + System.out.println("v: " + v); + latch.countDown(); + } + }); + + // subscribe again + o.subscribe(new Action1() { + + @Override + public void call(String v) { + assertEquals("one", v); + System.out.println("v: " + v); + latch.countDown(); + } + }); + + if (!latch.await(1000, TimeUnit.MILLISECONDS)) { + fail("subscriptions did not receive values"); + } + assertEquals(1, counter.get()); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationCastTest.java b/rxjava-core/src/test/java/rx/operators/OperationCastTest.java index 36bab268ff..1a87c915fc 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationCastTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationCastTest.java @@ -1,7 +1,40 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; + +import static org.mockito.Mockito.*; +import static rx.operators.OperationCast.cast; -@Ignore("WIP") public class OperationCastTest { + + @Test + public void testCast() { + Observable source = Observable.from(1, 2); + Observable observable = Observable.create(cast(source, + Integer.class)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(1); + verify(aObserver, times(1)).onNext(1); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testCastWithWrongType() { + Observable source = Observable.from(1, 2); + Observable observable = Observable.create(cast(source, + Boolean.class)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onError( + org.mockito.Matchers.any(ClassCastException.class)); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java b/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java index b154ce1223..b7ea62a8fd 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java @@ -1,7 +1,619 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Matchers; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Func2; +import rx.util.functions.Func3; +import rx.util.functions.FuncN; + +import java.util.Arrays; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; +import static rx.operators.OperationCombineLatest.Aggregator; +import static rx.operators.OperationCombineLatest.CombineObserver; +import static rx.operators.OperationCombineLatest.combineLatest; -@Ignore("WIP") public class OperationCombineLatestTest { + + @Test + public void testCombineLatestWithFunctionThatThrowsAnException() { + @SuppressWarnings("unchecked") // mock calls don't do generics + Observer w = mock(Observer.class); + + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + + Observable combined = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), new Func2() { + @Override + public String call(String v1, String v2) { + throw new RuntimeException("I don't work."); + } + })); + combined.subscribe(w); + + w1.observer.onNext("first value of w1"); + w2.observer.onNext("first value of w2"); + + verify(w, never()).onNext(anyString()); + verify(w, never()).onCompleted(); + verify(w, times(1)).onError(Matchers.any()); + } + + @Test + public void testCombineLatestDifferentLengthObservableSequences1() { + @SuppressWarnings("unchecked") // mock calls don't do generics + Observer w = mock(Observer.class); + + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + TestObservable w3 = new TestObservable(); + + Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction())); + combineLatestW.subscribe(w); + + /* simulate sending data */ + // once for w1 + w1.observer.onNext("1a"); + w2.observer.onNext("2a"); + w3.observer.onNext("3a"); + w1.observer.onCompleted(); + // twice for w2 + w2.observer.onNext("2b"); + w2.observer.onCompleted(); + // 4 times for w3 + w3.observer.onNext("3b"); + w3.observer.onNext("3c"); + w3.observer.onNext("3d"); + w3.observer.onCompleted(); + + /* we should have been called 4 times on the Observer */ + InOrder inOrder = inOrder(w); + inOrder.verify(w).onNext("1a2a3a"); + inOrder.verify(w).onNext("1a2b3a"); + inOrder.verify(w).onNext("1a2b3b"); + inOrder.verify(w).onNext("1a2b3c"); + inOrder.verify(w).onNext("1a2b3d"); + inOrder.verify(w, never()).onNext(anyString()); + inOrder.verify(w, times(1)).onCompleted(); + } + + @Test + public void testCombineLatestDifferentLengthObservableSequences2() { + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + TestObservable w3 = new TestObservable(); + + Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction())); + combineLatestW.subscribe(w); + + /* simulate sending data */ + // 4 times for w1 + w1.observer.onNext("1a"); + w1.observer.onNext("1b"); + w1.observer.onNext("1c"); + w1.observer.onNext("1d"); + w1.observer.onCompleted(); + // twice for w2 + w2.observer.onNext("2a"); + w2.observer.onNext("2b"); + w2.observer.onCompleted(); + // 1 times for w3 + w3.observer.onNext("3a"); + w3.observer.onCompleted(); + + /* we should have been called 1 time only on the Observer since we only combine the "latest" we don't go back and loop through others once completed */ + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("1d2b3a"); + inOrder.verify(w, never()).onNext(anyString()); + + inOrder.verify(w, times(1)).onCompleted(); + + } + + @Test + public void testCombineLatestWithInterleavingSequences() { + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + TestObservable w3 = new TestObservable(); + + Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction())); + combineLatestW.subscribe(w); + + /* simulate sending data */ + w1.observer.onNext("1a"); + w2.observer.onNext("2a"); + w2.observer.onNext("2b"); + w3.observer.onNext("3a"); + + w1.observer.onNext("1b"); + w2.observer.onNext("2c"); + w2.observer.onNext("2d"); + w3.observer.onNext("3b"); + + w1.observer.onCompleted(); + w2.observer.onCompleted(); + w3.observer.onCompleted(); + + /* we should have been called 5 times on the Observer */ + InOrder inOrder = inOrder(w); + inOrder.verify(w).onNext("1a2b3a"); + inOrder.verify(w).onNext("1b2b3a"); + inOrder.verify(w).onNext("1b2c3a"); + inOrder.verify(w).onNext("1b2d3a"); + inOrder.verify(w).onNext("1b2d3b"); + + inOrder.verify(w, never()).onNext(anyString()); + inOrder.verify(w, times(1)).onCompleted(); + } + + /** + * Testing internal private logic due to the complexity so I want to use TDD to test as a I build it rather than relying purely on the overall functionality expected by the public methods. + */ + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorSimple() { + FuncN combineLatestFunction = getConcatCombineLatestFunction(); + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ + Aggregator a = new Aggregator(combineLatestFunction); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Observable.create(a).subscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + + InOrder inOrder = inOrder(aObserver); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("helloworld"); + + a.next(r1, "hello "); + a.next(r2, "again"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("hello again"); + + a.complete(r1); + a.complete(r2); + + inOrder.verify(aObserver, never()).onNext(anyString()); + verify(aObserver, times(1)).onCompleted(); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorDifferentSizedResultsWithOnComplete() { + FuncN combineLatestFunction = getConcatCombineLatestFunction(); + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ + Aggregator a = new Aggregator(combineLatestFunction); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Observable.create(a).subscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + a.complete(r2); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); + + a.next(r1, "hi"); + a.complete(r1); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("hiworld"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregateMultipleTypes() { + FuncN combineLatestFunction = getConcatCombineLatestFunction(); + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ + Aggregator a = new Aggregator(combineLatestFunction); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Observable.create(a).subscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + a.complete(r2); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); + + a.next(r1, "hi"); + a.complete(r1); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("hiworld"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregate3Types() { + FuncN combineLatestFunction = getConcatCombineLatestFunction(); + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ + Aggregator a = new Aggregator(combineLatestFunction); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Observable.create(a).subscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); + CombineObserver r3 = mock(CombineObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + a.addObserver(r3); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, 2); + a.next(r3, new int[]{5, 6, 7}); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("hello2[5, 6, 7]"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorsWithDifferentSizesAndTiming() { + FuncN combineLatestFunction = getConcatCombineLatestFunction(); + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ + Aggregator a = new Aggregator(combineLatestFunction); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Observable.create(a).subscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "one"); + a.next(r1, "two"); + a.next(r1, "three"); + a.next(r2, "A"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("threeA"); + + a.next(r1, "four"); + a.complete(r1); + a.next(r2, "B"); + verify(aObserver, times(1)).onNext("fourB"); + a.next(r2, "C"); + verify(aObserver, times(1)).onNext("fourC"); + a.next(r2, "D"); + verify(aObserver, times(1)).onNext("fourD"); + a.next(r2, "E"); + verify(aObserver, times(1)).onNext("fourE"); + a.complete(r2); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorError() { + FuncN combineLatestFunction = getConcatCombineLatestFunction(); + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ + Aggregator a = new Aggregator(combineLatestFunction); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Observable.create(a).subscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); + + a.error(new RuntimeException("")); + a.next(r1, "hello"); + a.next(r2, "again"); + + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + // we don't want to be called again after an error + verify(aObserver, times(0)).onNext("helloagain"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorUnsubscribe() { + FuncN combineLatestFunction = getConcatCombineLatestFunction(); + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ + Aggregator a = new Aggregator(combineLatestFunction); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Subscription subscription = Observable.create(a).subscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); + + subscription.unsubscribe(); + a.next(r1, "hello"); + a.next(r2, "again"); + + verify(aObserver, times(0)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + // we don't want to be called again after an error + verify(aObserver, times(0)).onNext("helloagain"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorEarlyCompletion() { + FuncN combineLatestFunction = getConcatCombineLatestFunction(); + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ + Aggregator a = new Aggregator(combineLatestFunction); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Observable.create(a).subscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "one"); + a.next(r1, "two"); + a.complete(r1); + a.next(r2, "A"); + + InOrder inOrder = inOrder(aObserver); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("twoA"); + + a.complete(r2); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + // we shouldn't get this since completed is called before any other onNext calls could trigger this + inOrder.verify(aObserver, never()).onNext(anyString()); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testCombineLatest2Types() { + Func2 combineLatestFunction = getConcatStringIntegerCombineLatestFunction(); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + + Observable w = Observable.create(combineLatest(Observable.from("one", "two"), Observable.from(2, 3, 4), combineLatestFunction)); + w.subscribe(aObserver); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("two2"); + verify(aObserver, times(1)).onNext("two3"); + verify(aObserver, times(1)).onNext("two4"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testCombineLatest3TypesA() { + Func3 combineLatestFunction = getConcatStringIntegerIntArrayCombineLatestFunction(); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + + Observable w = Observable.create(combineLatest(Observable.from("one", "two"), Observable.from(2), Observable.from(new int[]{4, 5, 6}), combineLatestFunction)); + w.subscribe(aObserver); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("two2[4, 5, 6]"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testCombineLatest3TypesB() { + Func3 combineLatestFunction = getConcatStringIntegerIntArrayCombineLatestFunction(); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + + Observable w = Observable.create(combineLatest(Observable.from("one"), Observable.from(2), Observable.from(new int[]{4, 5, 6}, new int[]{7, 8}), combineLatestFunction)); + w.subscribe(aObserver); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one2[4, 5, 6]"); + verify(aObserver, times(1)).onNext("one2[7, 8]"); + } + + private Func3 getConcat3StringsCombineLatestFunction() { + Func3 combineLatestFunction = new Func3() { + + @Override + public String call(String a1, String a2, String a3) { + if (a1 == null) { + a1 = ""; + } + if (a2 == null) { + a2 = ""; + } + if (a3 == null) { + a3 = ""; + } + return a1 + a2 + a3; + } + + }; + return combineLatestFunction; + } + + private FuncN getConcatCombineLatestFunction() { + FuncN combineLatestFunction = new FuncN() { + + @Override + public String call(Object... args) { + String returnValue = ""; + for (Object o : args) { + if (o != null) { + returnValue += getStringValue(o); + } + } + System.out.println("returning: " + returnValue); + return returnValue; + } + + }; + return combineLatestFunction; + } + + private Func2 getConcatStringIntegerCombineLatestFunction() { + Func2 combineLatestFunction = new Func2() { + + @Override + public String call(String s, Integer i) { + return getStringValue(s) + getStringValue(i); + } + + }; + return combineLatestFunction; + } + + private Func3 getConcatStringIntegerIntArrayCombineLatestFunction() { + Func3 combineLatestFunction = new Func3() { + + @Override + public String call(String s, Integer i, int[] iArray) { + return getStringValue(s) + getStringValue(i) + getStringValue(iArray); + } + + }; + return combineLatestFunction; + } + + private static String getStringValue(Object o) { + if (o == null) { + return ""; + } else { + if (o instanceof int[]) { + return Arrays.toString((int[]) o); + } else { + return String.valueOf(o); + } + } + } + + private static class TestObservable implements Observable.OnSubscribeFunc { + + Observer observer; + + @Override + public Subscription onSubscribe(Observer observer) { + // just store the variable where it can be accessed so we can manually trigger it + this.observer = observer; + return Subscriptions.empty(); + } + + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java b/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java index 1380ffcaef..35fc22bcc2 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java @@ -1,7 +1,544 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.BooleanSubscription; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; +import static rx.operators.OperationConcat.concat; -@Ignore("WIP") public class OperationConcatTest { + + @Test + public void testConcat() { + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + + final String[] o = {"1", "3", "5", "7"}; + final String[] e = {"2", "4", "6"}; + + final Observable odds = Observable.from(o); + final Observable even = Observable.from(e); + + @SuppressWarnings("unchecked") + Observable concat = Observable.create(concat(odds, even)); + concat.subscribe(observer); + + verify(observer, times(7)).onNext(anyString()); + } + + @Test + public void testConcatWithList() { + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + + final String[] o = {"1", "3", "5", "7"}; + final String[] e = {"2", "4", "6"}; + + final Observable odds = Observable.from(o); + final Observable even = Observable.from(e); + final List> list = new ArrayList>(); + list.add(odds); + list.add(even); + Observable concat = Observable.create(concat(list)); + concat.subscribe(observer); + + verify(observer, times(7)).onNext(anyString()); + } + + @Test + public void testConcatObservableOfObservables() { + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + + final String[] o = {"1", "3", "5", "7"}; + final String[] e = {"2", "4", "6"}; + + final Observable odds = Observable.from(o); + final Observable even = Observable.from(e); + + Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() { + + @Override + public Subscription onSubscribe(Observer> observer) { + // simulate what would happen in an observable + observer.onNext(odds); + observer.onNext(even); + observer.onCompleted(); + + return new Subscription() { + + @Override + public void unsubscribe() { + // unregister ... will never be called here since we are executing synchronously + } + + }; + } + + }); + Observable concat = Observable.create(concat(observableOfObservables)); + + concat.subscribe(observer); + + verify(observer, times(7)).onNext(anyString()); + } + + /** + * Simple concat of 2 asynchronous observables ensuring it emits in correct order. + */ + @SuppressWarnings("unchecked") + @Test + public void testSimpleAsyncConcat() { + Observer observer = mock(Observer.class); + + TestObservable o1 = new TestObservable("one", "two", "three"); + TestObservable o2 = new TestObservable("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 an async Observable that emits more async Observables + */ + @SuppressWarnings("unchecked") + @Test + public void testNestedAsyncConcat() throws Throwable { + Observer observer = mock(Observer.class); + + final TestObservable o1 = new TestObservable("one", "two", "three"); + final TestObservable o2 = new TestObservable("four", "five", "six"); + final TestObservable o3 = new TestObservable("seven", "eight", "nine"); + final CountDownLatch allowThird = new CountDownLatch(1); + + final AtomicReference parent = new AtomicReference(); + Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() { + + @Override + public Subscription onSubscribe(final Observer> 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(); + return s; + } + }); + + Observable.create(concat(observableOfObservables)).subscribe(observer); + + // wait for parent to start + while (parent.get() == null) { + Thread.sleep(1); + } + + try { + // wait for first 2 async observables to complete + while (o1.t == null) { + Thread.sleep(1); + } + System.out.println("Thread1 started ... waiting for it to complete ..."); + o1.t.join(); + while (o2.t == null) { + Thread.sleep(1); + } + System.out.println("Thread2 started ... waiting for it to complete ..."); + o2.t.join(); + } 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 { + while (o3.t == null) { + Thread.sleep(1); + } + // wait for 3rd to complete + o3.t.join(); + } 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)); + } + + @SuppressWarnings("unchecked") + @Test + public void testBlockedObservableOfObservables() { + Observer observer = mock(Observer.class); + + final String[] o = {"1", "3", "5", "7"}; + final String[] e = {"2", "4", "6"}; + final Observable odds = Observable.from(o); + final Observable even = Observable.from(e); + final CountDownLatch callOnce = new CountDownLatch(1); + final CountDownLatch okToContinue = new CountDownLatch(1); + TestObservable> observableOfObservables = new TestObservable>(callOnce, okToContinue, odds, even); + Observable.OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables)); + Observable concat = Observable.create(concatF); + concat.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 testConcatConcurrentWithInfinity() { + final TestObservable w1 = new TestObservable("one", "two", "three"); + //This observable will send "hello" MAX_VALUE time. + final TestObservable w2 = new TestObservable("hello", Integer.MAX_VALUE); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + @SuppressWarnings("unchecked") + TestObservable> observableOfObservables = new TestObservable>(Observable.create(w1), Observable.create(w2)); + Observable.OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables)); + + Observable concat = Observable.create(concatF); + + concat.take(50).subscribe(aObserver); + + //Wait for the thread to start up. + try { + Thread.sleep(25); + w1.t.join(); + w2.t.join(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + InOrder inOrder = inOrder(aObserver); + inOrder.verify(aObserver, times(1)).onNext("one"); + inOrder.verify(aObserver, times(1)).onNext("two"); + inOrder.verify(aObserver, times(1)).onNext("three"); + inOrder.verify(aObserver, times(47)).onNext("hello"); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, never()).onError(any(Throwable.class)); + } + + @Test + public void testConcatNonBlockingObservables() { + + final CountDownLatch okToContinueW1 = new CountDownLatch(1); + final CountDownLatch okToContinueW2 = new CountDownLatch(1); + + final TestObservable w1 = new TestObservable(null, okToContinueW1, "one", "two", "three"); + final TestObservable w2 = new TestObservable(null, okToContinueW2, "four", "five", "six"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() { + + @Override + public Subscription onSubscribe(Observer> observer) { + // simulate what would happen in an observable + observer.onNext(Observable.create(w1)); + observer.onNext(Observable.create(w2)); + observer.onCompleted(); + + return new Subscription() { + + @Override + public void unsubscribe() { + } + + }; + } + + }); + Observable concat = Observable.create(concat(observableOfObservables)); + concat.subscribe(aObserver); + + verify(aObserver, 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) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + InOrder inOrder = inOrder(aObserver); + inOrder.verify(aObserver, times(1)).onNext("one"); + inOrder.verify(aObserver, times(1)).onNext("two"); + inOrder.verify(aObserver, times(1)).onNext("three"); + inOrder.verify(aObserver, times(1)).onNext("four"); + inOrder.verify(aObserver, times(1)).onNext("five"); + inOrder.verify(aObserver, times(1)).onNext("six"); + verify(aObserver, times(1)).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 w1 = new TestObservable("one", "two", "three"); + final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six"); + + @SuppressWarnings("unchecked") + final Observer aObserver = mock(Observer.class); + @SuppressWarnings("unchecked") + final Observable concat = Observable.create(concat(Observable.create(w1), Observable.create(w2))); + final SafeObservableSubscription s1 = new SafeObservableSubscription(); + + try { + // Subscribe + s1.wrap(concat.subscribe(aObserver)); + //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(aObserver); + inOrder.verify(aObserver, times(1)).onNext("one"); + inOrder.verify(aObserver, times(1)).onNext("two"); + inOrder.verify(aObserver, times(1)).onNext("three"); + inOrder.verify(aObserver, times(1)).onNext("four"); + inOrder.verify(aObserver, never()).onNext("five"); + inOrder.verify(aObserver, never()).onNext("six"); + inOrder.verify(aObserver, never()).onCompleted(); + + } + + /** + * 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 w1 = new TestObservable("one", "two", "three"); + final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + @SuppressWarnings("unchecked") + TestObservable> observableOfObservables = new TestObservable>(Observable.create(w1), Observable.create(w2)); + Observable.OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables)); + + Observable concat = Observable.create(concatF); + + Subscription s1 = concat.subscribe(aObserver); + + 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(aObserver); + inOrder.verify(aObserver, times(1)).onNext("one"); + inOrder.verify(aObserver, times(1)).onNext("two"); + inOrder.verify(aObserver, times(1)).onNext("three"); + inOrder.verify(aObserver, times(1)).onNext("four"); + inOrder.verify(aObserver, never()).onNext("five"); + inOrder.verify(aObserver, never()).onNext("six"); + verify(aObserver, never()).onCompleted(); + verify(aObserver, never()).onError(any(Throwable.class)); + } + + private static class TestObservable implements Observable.OnSubscribeFunc { + + private final Subscription s = new Subscription() { + + @Override + public void unsubscribe() { + subscribed = false; + } + + }; + private final List values; + private Thread t = null; + private int count = 0; + private boolean subscribed = true; + private final CountDownLatch once; + private final CountDownLatch okToContinue; + private final T seed; + private final int size; + + public TestObservable(T... values) { + this(null, null, values); + } + + public TestObservable(CountDownLatch once, CountDownLatch okToContinue, T... values) { + this.values = Arrays.asList(values); + this.size = this.values.size(); + this.once = once; + this.okToContinue = okToContinue; + this.seed = null; + } + + public TestObservable(T seed, int size) { + values = null; + once = null; + okToContinue = null; + this.seed = seed; + this.size = size; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + while (count < size && subscribed) { + if (null != values) + observer.onNext(values.get(count)); + else + observer.onNext(seed); + count++; + //Unblock the main thread to call unsubscribe. + if (null != once) + once.countDown(); + //Block until the main thread has called unsubscribe. + if (null != okToContinue) + okToContinue.await(5, TimeUnit.SECONDS); + } + if (subscribed) + observer.onCompleted(); + } catch (InterruptedException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + }); + t.start(); + return s; + } + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java b/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java index a1f6d4c237..2e907d8f6b 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java @@ -1,7 +1,146 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.concurrency.TestScheduler; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; + +import java.util.concurrent.TimeUnit; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; -@Ignore("WIP") public class OperationDebounceTest { + + private TestScheduler scheduler; + private Observer observer; + + @Before + @SuppressWarnings("unchecked") + public void before() { + scheduler = new TestScheduler(); + observer = mock(Observer.class); + } + + @Test + public void testDebounceWithCompleted() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 100, "one"); // Should be skipped since "two" will arrive before the timeout expires. + publishNext(observer, 400, "two"); // Should be published since "three" will arrive after the timeout expires. + publishNext(observer, 900, "three"); // Should be skipped since onCompleted will arrive before the timeout expires. + publishCompleted(observer, 1000); // Should be published as soon as the timeout expires. + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationDebounce.debounce(source, 400, TimeUnit.MILLISECONDS, scheduler)); + sampled.subscribe(observer); + + scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); + InOrder inOrder = inOrder(observer); + // must go to 800 since it must be 400 after when two is sent, which is at 400 + scheduler.advanceTimeTo(800, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("two"); + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testDebounceNeverEmits() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + // all should be skipped since they are happening faster than the 200ms timeout + publishNext(observer, 100, "a"); // Should be skipped + publishNext(observer, 200, "b"); // Should be skipped + publishNext(observer, 300, "c"); // Should be skipped + publishNext(observer, 400, "d"); // Should be skipped + publishNext(observer, 500, "e"); // Should be skipped + publishNext(observer, 600, "f"); // Should be skipped + publishNext(observer, 700, "g"); // Should be skipped + publishNext(observer, 800, "h"); // Should be skipped + publishCompleted(observer, 900); // Should be published as soon as the timeout expires. + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationDebounce.debounce(source, 200, TimeUnit.MILLISECONDS, scheduler)); + sampled.subscribe(observer); + + scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(0)).onNext(anyString()); + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testDebounceWithError() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + Exception error = new TestException(); + publishNext(observer, 100, "one"); // Should be published since "two" will arrive after the timeout expires. + publishNext(observer, 600, "two"); // Should be skipped since onError will arrive before the timeout expires. + publishError(observer, 700, error); // Should be published as soon as the timeout expires. + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationDebounce.debounce(source, 400, TimeUnit.MILLISECONDS, scheduler)); + sampled.subscribe(observer); + + scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); + InOrder inOrder = inOrder(observer); + // 100 + 400 means it triggers at 500 + scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); + inOrder.verify(observer).onNext("one"); + scheduler.advanceTimeTo(701, TimeUnit.MILLISECONDS); + inOrder.verify(observer).onError(any(TestException.class)); + inOrder.verifyNoMoreInteractions(); + } + + private void publishCompleted(final Observer observer, long delay) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onCompleted(); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void publishError(final Observer observer, long delay, final Exception error) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onError(error); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void publishNext(final Observer observer, final long delay, final T value) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onNext(value); + } + }, delay, TimeUnit.MILLISECONDS); + } + + @SuppressWarnings("serial") + private class TestException extends Exception { + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java b/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java index 241c8790c3..fb2166dfc1 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java @@ -1,7 +1,44 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; + +import static org.mockito.Mockito.*; +import static rx.operators.OperationDefaultIfEmpty.defaultIfEmpty; -@Ignore("WIP") public class OperationDefaultIfEmptyTest { + + @Test + public void testDefaultIfEmpty() { + Observable source = Observable.from(1, 2, 3); + Observable observable = Observable.create(defaultIfEmpty( + source, 10)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(10); + verify(aObserver, times(1)).onNext(1); + verify(aObserver, times(1)).onNext(2); + verify(aObserver, times(1)).onNext(3); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testDefaultIfEmptyWithEmpty() { + Observable source = Observable.empty(); + Observable observable = Observable.create(defaultIfEmpty( + source, 10)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(10); + verify(aObserver, never()).onError( + org.mockito.Matchers.any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java b/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java index 344cbee450..8e02b17647 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java @@ -1,7 +1,47 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func0; + +import static org.mockito.Mockito.*; -@Ignore("WIP") public class OperationDeferTest { + + @Test + @SuppressWarnings("unchecked") + public void testDefer() throws Throwable { + + Func0> factory = mock(Func0.class); + + Observable firstObservable = Observable.from("one", "two"); + Observable secondObservable = Observable.from("three", "four"); + when(factory.call()).thenReturn(firstObservable, secondObservable); + + Observable deferred = Observable.defer(factory); + + verifyZeroInteractions(factory); + + Observer firstObserver = mock(Observer.class); + deferred.subscribe(firstObserver); + + verify(factory, times(1)).call(); + verify(firstObserver, times(1)).onNext("one"); + verify(firstObserver, times(1)).onNext("two"); + verify(firstObserver, times(0)).onNext("three"); + verify(firstObserver, times(0)).onNext("four"); + verify(firstObserver, times(1)).onCompleted(); + + Observer secondObserver = mock(Observer.class); + deferred.subscribe(secondObserver); + + verify(factory, times(2)).call(); + verify(secondObserver, times(0)).onNext("one"); + verify(secondObserver, times(0)).onNext("two"); + verify(secondObserver, times(1)).onNext("three"); + verify(secondObserver, times(1)).onNext("four"); + verify(secondObserver, times(1)).onCompleted(); + + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java b/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java index f0433032fb..fcc1898d4a 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java @@ -1,7 +1,58 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Notification; +import rx.Observable; +import rx.Observer; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationDematerialize.dematerialize; -@Ignore("WIP") public class OperationDematerializeTest { + + @Test + @SuppressWarnings("unchecked") + public void testDematerialize1() { + Observable> notifications = Observable.from(1, 2).materialize(); + Observable dematerialize = notifications.dematerialize(); + + Observer aObserver = mock(Observer.class); + dematerialize.subscribe(aObserver); + + verify(aObserver, times(1)).onNext(1); + verify(aObserver, times(1)).onNext(2); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, never()).onError(any(Throwable.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void testDematerialize2() { + Throwable exception = new Throwable("test"); + Observable observable = Observable.error(exception); + Observable dematerialize = Observable.create(dematerialize(observable.materialize())); + + Observer aObserver = mock(Observer.class); + dematerialize.subscribe(aObserver); + + verify(aObserver, times(1)).onError(exception); + verify(aObserver, times(0)).onCompleted(); + verify(aObserver, times(0)).onNext(any(Integer.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void testDematerialize3() { + Exception exception = new Exception("test"); + Observable observable = Observable.error(exception); + Observable dematerialize = Observable.create(dematerialize(observable.materialize())); + + Observer aObserver = mock(Observer.class); + dematerialize.subscribe(aObserver); + + verify(aObserver, times(1)).onError(exception); + verify(aObserver, times(0)).onCompleted(); + verify(aObserver, times(0)).onNext(any(Integer.class)); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java b/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java index 1704b644fc..740ba9429a 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java @@ -1,7 +1,182 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; + +import java.util.Comparator; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.never; +import static org.mockito.MockitoAnnotations.initMocks; +import static rx.Observable.*; +import static rx.operators.OperationDistinct.distinct; -@Ignore("WIP") public class OperationDistinctTest { + + @Mock + Observer w; + @Mock + Observer w2; + + // nulls lead to exceptions + final Func1 TO_UPPER_WITH_EXCEPTION = new Func1() { + @Override + public String call(String s) { + if (s.equals("x")) { + return "XX"; + } + return s.toUpperCase(); + } + }; + + final Comparator COMPARE_LENGTH = new Comparator() { + @Override + public int compare(String s1, String s2) { + return s1.length() - s2.length(); + } + }; + + @Before + public void before() { + initMocks(this); + } + + @Test + public void testDistinctOfNone() { + Observable src = empty(); + create(distinct(src)).subscribe(w); + + verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testDistinctOfNoneWithKeySelector() { + Observable src = empty(); + create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + + verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testDistinctOfNormalSource() { + Observable src = from("a", "b", "c", "c", "c", "b", "b", "a", "e"); + create(distinct(src)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("b"); + inOrder.verify(w, times(1)).onNext("c"); + inOrder.verify(w, times(1)).onNext("e"); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testDistinctOfNormalSourceWithKeySelector() { + Observable src = from("a", "B", "c", "C", "c", "B", "b", "a", "E"); + create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("B"); + inOrder.verify(w, times(1)).onNext("c"); + inOrder.verify(w, times(1)).onNext("E"); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testDistinctOfNormalSourceWithComparator() { + Observable src = from("1", "12", "123", "aaa", "321", "12", "21", "1", "12345"); + create(distinct(src, COMPARE_LENGTH)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("1"); + inOrder.verify(w, times(1)).onNext("12"); + inOrder.verify(w, times(1)).onNext("123"); + inOrder.verify(w, times(1)).onNext("12345"); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testDistinctOfNormalSourceWithKeySelectorAndComparator() { + Observable src = from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd"); + create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("x"); + inOrder.verify(w, times(1)).onNext("abc"); + inOrder.verify(w, times(1)).onNext("abcd"); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testDistinctOfNormalSourceWithKeySelectorAndComparatorAndTwoSubscriptions() { + Observable src = from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd"); + create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("x"); + create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2); + inOrder.verify(w, times(1)).onNext("abc"); + inOrder.verify(w, times(1)).onNext("abcd"); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + + InOrder inOrder2 = inOrder(w2); + inOrder2.verify(w2, times(1)).onNext("a"); + inOrder2.verify(w2, times(1)).onNext("x"); + inOrder2.verify(w2, times(1)).onNext("abc"); + inOrder2.verify(w2, times(1)).onNext("abcd"); + inOrder2.verify(w2, times(1)).onCompleted(); + inOrder2.verify(w2, never()).onNext(anyString()); + verify(w2, never()).onError(any(Throwable.class)); + } + + @Test + public void testDistinctOfSourceWithNulls() { + Observable src = from(null, "a", "a", null, null, "b", null); + create(distinct(src)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext(null); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("b"); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testDistinctOfSourceWithExceptionsFromKeySelector() { + Observable src = from("a", "b", null, "c"); + create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("b"); + inOrder.verify(w, times(1)).onError(any(NullPointerException.class)); + inOrder.verify(w, never()).onNext(anyString()); + inOrder.verify(w, never()).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java b/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java index cdb4eb7203..e39a123f3d 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java @@ -1,7 +1,185 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; + +import java.util.Comparator; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.never; +import static org.mockito.MockitoAnnotations.initMocks; +import static rx.Observable.*; +import static rx.operators.OperationDistinctUntilChanged.distinctUntilChanged; -@Ignore("WIP") public class OperationDistinctUntilChangedTest { + + @Mock + Observer w; + @Mock + Observer w2; + + // nulls lead to exceptions + final Func1 TO_UPPER_WITH_EXCEPTION = new Func1() { + @Override + public String call(String s) { + if (s.equals("x")) { + return "xx"; + } + return s.toUpperCase(); + } + }; + + final Comparator COMPARE_LENGTH = new Comparator() { + @Override + public int compare(String s1, String s2) { + return s1.length() - s2.length(); + } + }; + + @Before + public void before() { + initMocks(this); + } + + @Test + public void testDistinctUntilChangedOfNone() { + Observable src = empty(); + create(distinctUntilChanged(src)).subscribe(w); + + verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testDistinctUntilChangedOfNoneWithKeySelector() { + Observable src = empty(); + create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + + verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testDistinctUntilChangedOfNormalSource() { + Observable src = from("a", "b", "c", "c", "c", "b", "b", "a", "e"); + create(distinctUntilChanged(src)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("b"); + inOrder.verify(w, times(1)).onNext("c"); + inOrder.verify(w, times(1)).onNext("b"); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("e"); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testDistinctUntilChangedOfNormalSourceWithKeySelector() { + Observable src = from("a", "b", "c", "C", "c", "B", "b", "a", "e"); + create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("b"); + inOrder.verify(w, times(1)).onNext("c"); + inOrder.verify(w, times(1)).onNext("B"); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("e"); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testDistinctUntilChangedOfSourceWithNulls() { + Observable src = from(null, "a", "a", null, null, "b", null, null); + create(distinctUntilChanged(src)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext(null); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext(null); + inOrder.verify(w, times(1)).onNext("b"); + inOrder.verify(w, times(1)).onNext(null); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testDistinctUntilChangedOfSourceWithExceptionsFromKeySelector() { + Observable src = from("a", "b", null, "c"); + create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("b"); + verify(w, times(1)).onError(any(NullPointerException.class)); + inOrder.verify(w, never()).onNext(anyString()); + inOrder.verify(w, never()).onCompleted(); + } + + @Test + public void testDistinctUntilChangedWithComparator() { + Observable src = from("a", "b", "c", "aa", "bb", "c", "ddd"); + create(distinctUntilChanged(src, COMPARE_LENGTH)).subscribe(w); + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("aa"); + inOrder.verify(w, times(1)).onNext("c"); + inOrder.verify(w, times(1)).onNext("ddd"); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testDistinctUntilChangedWithComparatorAndKeySelector() { + Observable src = from("a", "b", "x", "aa", "bb", "c", "ddd"); + create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("x"); + inOrder.verify(w, times(1)).onNext("c"); + inOrder.verify(w, times(1)).onNext("ddd"); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testDistinctUntilChangedWithComparatorAndKeySelectorandTwoSubscriptions() { + Observable src = from("a", "b", "x", "aa", "bb", "c", "ddd"); + create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext("a"); + inOrder.verify(w, times(1)).onNext("x"); + create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2); + inOrder.verify(w, times(1)).onNext("c"); + inOrder.verify(w, times(1)).onNext("ddd"); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onNext(anyString()); + verify(w, never()).onError(any(Throwable.class)); + + InOrder inOrder2 = inOrder(w2); + inOrder2.verify(w2, times(1)).onNext("a"); + inOrder2.verify(w2, times(1)).onNext("x"); + inOrder2.verify(w2, times(1)).onNext("c"); + inOrder2.verify(w2, times(1)).onNext("ddd"); + inOrder2.verify(w2, times(1)).onCompleted(); + inOrder2.verify(w2, never()).onNext(anyString()); + verify(w2, never()).onError(any(Throwable.class)); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java b/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java index b17efa3cd3..4f1326df06 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java @@ -1,7 +1,113 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; + +import java.util.Iterator; +import java.util.concurrent.ExecutionException; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationElementAt.elementAt; +import static rx.operators.OperationElementAt.elementAtOrDefault; -@Ignore("WIP") public class OperationElementAtTest { + + @Test + public void testElementAt() { + Observable w = Observable.from(1, 2); + Observable observable = Observable.create(elementAt(w, 1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(1); + verify(aObserver, times(1)).onNext(2); + verify(aObserver, never()).onError( + any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testElementAtWithMinusIndex() { + Observable w = Observable.from(1, 2); + Observable observable = Observable + .create(elementAt(w, -1)); + + try { + Iterator iter = OperationToIterator + .toIterator(observable); + assertTrue(iter.hasNext()); + iter.next(); + fail("expect an IndexOutOfBoundsException when index is out of bounds"); + } catch (IndexOutOfBoundsException e) { + } + } + + @Test + public void testElementAtWithIndexOutOfBounds() + throws InterruptedException, ExecutionException { + Observable w = Observable.from(1, 2); + Observable observable = Observable.create(elementAt(w, 2)); + try { + Iterator iter = OperationToIterator + .toIterator(observable); + assertTrue(iter.hasNext()); + iter.next(); + fail("expect an IndexOutOfBoundsException when index is out of bounds"); + } catch (IndexOutOfBoundsException e) { + } + } + + @Test + public void testElementAtOrDefault() throws InterruptedException, + ExecutionException { + Observable w = Observable.from(1, 2); + Observable observable = Observable + .create(elementAtOrDefault(w, 1, 0)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(1); + verify(aObserver, times(1)).onNext(2); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testElementAtOrDefaultWithIndexOutOfBounds() + throws InterruptedException, ExecutionException { + Observable w = Observable.from(1, 2); + Observable observable = Observable + .create(elementAtOrDefault(w, 2, 0)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(1); + verify(aObserver, never()).onNext(2); + verify(aObserver, times(1)).onNext(0); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testElementAtOrDefaultWithMinusIndex() { + Observable w = Observable.from(1, 2); + Observable observable = Observable + .create(elementAtOrDefault(w, -1, 0)); + + try { + Iterator iter = OperationToIterator + .toIterator(observable); + assertTrue(iter.hasNext()); + iter.next(); + fail("expect an IndexOutOfBoundsException when index is out of bounds"); + } catch (IndexOutOfBoundsException e) { + } + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java b/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java index 19e15ea81a..a6da7261e1 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java @@ -1,7 +1,35 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationFilter.filter; -@Ignore("WIP") public class OperationFilterTest { + + @Test + public void testFilter() { + Observable w = Observable.from("one", "two", "three"); + Observable observable = Observable.create(filter(w, new Func1() { + + @Override + public Boolean call(String t1) { + return t1.equals("two"); + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, Mockito.never()).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java b/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java index 0e46ce1cb2..b856c86fa6 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java @@ -1,7 +1,38 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Action0; + +import static org.mockito.Mockito.*; +import static rx.operators.OperationFinally.finallyDo; -@Ignore("WIP") public class OperationFinallyTest { + + private Action0 aAction0; + private Observer aObserver; + + @SuppressWarnings("unchecked") // mocking has to be unchecked, unfortunately + @Before + public void before() { + aAction0 = mock(Action0.class); + aObserver = mock(Observer.class); + } + + private void checkActionCalled(Observable input) { + Observable.create(finallyDo(input, aAction0)).subscribe(aObserver); + verify(aAction0, times(1)).call(); + } + + @Test + public void testFinallyCalledOnComplete() { + checkActionCalled(Observable.from(new String[]{"1", "2", "3"})); + } + + @Test + public void testFinallyCalledOnError() { + checkActionCalled(Observable.error(new RuntimeException("expected"))); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java b/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java index 39a60a03c5..344ab98578 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java @@ -1,7 +1,78 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.initMocks; +import static rx.Observable.*; +import static rx.operators.OperationFirstOrDefault.firstOrDefault; -@Ignore("WIP") public class OperationFirstOrDefaultTest { + + @Mock + Observer w; + + private static final Func1 IS_D = new Func1() { + @Override + public Boolean call(String value) { + return "d".equals(value); + } + }; + + @Before + public void before() { + initMocks(this); + } + + @Test + public void testFirstOrElseOfNone() { + Observable src = empty(); + create(firstOrDefault(src, "default")).subscribe(w); + + verify(w, times(1)).onNext(anyString()); + verify(w, times(1)).onNext("default"); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testFirstOrElseOfSome() { + Observable src = from("a", "b", "c"); + create(firstOrDefault(src, "default")).subscribe(w); + + verify(w, times(1)).onNext(anyString()); + verify(w, times(1)).onNext("a"); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testFirstOrElseWithPredicateOfNoneMatchingThePredicate() { + Observable src = from("a", "b", "c"); + create(firstOrDefault(src, IS_D, "default")).subscribe(w); + + verify(w, times(1)).onNext(anyString()); + verify(w, times(1)).onNext("default"); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testFirstOrElseWithPredicateOfSome() { + Observable src = from("a", "b", "c", "d", "e", "f"); + create(firstOrDefault(src, IS_D, "default")).subscribe(w); + + verify(w, times(1)).onNext(anyString()); + verify(w, times(1)).onNext("d"); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java b/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java index 222085a96f..e62be97d11 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java @@ -1,7 +1,331 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.observables.GroupedObservable; +import rx.subscriptions.BooleanSubscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action1; +import rx.util.functions.Func1; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.*; +import static rx.operators.OperationGroupBy.groupBy; -@Ignore("WIP") public class OperationGroupByTest { + + final Func1 length = new Func1() { + @Override + public Integer call(String s) { + return s.length(); + } + }; + + @Test + public void testGroupBy() { + Observable source = Observable.from("one", "two", "three", "four", "five", "six"); + Observable> grouped = Observable.create(groupBy(source, length)); + + Map> map = toMap(grouped); + + assertEquals(3, map.size()); + assertArrayEquals(Arrays.asList("one", "two", "six").toArray(), map.get(3).toArray()); + assertArrayEquals(Arrays.asList("four", "five").toArray(), map.get(4).toArray()); + assertArrayEquals(Arrays.asList("three").toArray(), map.get(5).toArray()); + } + + @Test + public void testEmpty() { + Observable source = Observable.empty(); + Observable> grouped = Observable.create(groupBy(source, length)); + + Map> map = toMap(grouped); + + assertTrue(map.isEmpty()); + } + + @Test + public void testError() { + Observable sourceStrings = Observable.from("one", "two", "three", "four", "five", "six"); + Observable errorSource = Observable.error(new RuntimeException("forced failure")); + Observable source = Observable.concat(sourceStrings, errorSource); + + Observable> grouped = Observable.create(groupBy(source, length)); + + final AtomicInteger groupCounter = new AtomicInteger(); + final AtomicInteger eventCounter = new AtomicInteger(); + final AtomicReference error = new AtomicReference(); + + grouped.mapMany(new Func1, Observable>() { + + @Override + public Observable call(final GroupedObservable o) { + groupCounter.incrementAndGet(); + return o.map(new Func1() { + + @Override + public String call(String v) { + return "Event => key: " + o.getKey() + " value: " + v; + } + }); + } + }).subscribe(new Observer() { + + @Override + public void onCompleted() { + + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + error.set(e); + } + + @Override + public void onNext(String v) { + eventCounter.incrementAndGet(); + System.out.println(v); + + } + }); + + assertEquals(3, groupCounter.get()); + assertEquals(6, eventCounter.get()); + assertNotNull(error.get()); + } + + private static Map> toMap(Observable> observable) { + + final ConcurrentHashMap> result = new ConcurrentHashMap>(); + + observable.toBlockingObservable().forEach(new Action1>() { + + @Override + public void call(final GroupedObservable o) { + result.put(o.getKey(), new ConcurrentLinkedQueue()); + o.subscribe(new Action1() { + + @Override + public void call(V v) { + result.get(o.getKey()).add(v); + } + + }); + } + }); + + return result; + } + + /** + * Assert that only a single subscription to a stream occurs and that all events are received. + * + * @throws Throwable + */ + @Test + public void testGroupedEventStream() throws Throwable { + + final AtomicInteger eventCounter = new AtomicInteger(); + final AtomicInteger subscribeCounter = new AtomicInteger(); + final AtomicInteger groupCounter = new AtomicInteger(); + final CountDownLatch latch = new CountDownLatch(1); + final int count = 100; + final int groupCount = 2; + + Observable es = Observable.create(new Observable.OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("*** Subscribing to EventStream ***"); + subscribeCounter.incrementAndGet(); + new Thread(new Runnable() { + + @Override + public void run() { + for (int i = 0; i < count; i++) { + Event e = new Event(); + e.source = i % groupCount; + e.message = "Event-" + i; + observer.onNext(e); + } + observer.onCompleted(); + } + + }).start(); + return Subscriptions.empty(); + } + + }); + + es.groupBy(new Func1() { + + @Override + public Integer call(Event e) { + return e.source; + } + }).mapMany(new Func1, Observable>() { + + @Override + public Observable call(GroupedObservable eventGroupedObservable) { + System.out.println("GroupedObservable Key: " + eventGroupedObservable.getKey()); + groupCounter.incrementAndGet(); + + return eventGroupedObservable.map(new Func1() { + + @Override + public String call(Event event) { + return "Source: " + event.source + " Message: " + event.message; + } + }); + + } + }).subscribe(new Observer() { + + @Override + public void onCompleted() { + latch.countDown(); + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + latch.countDown(); + } + + @Override + public void onNext(String outputMessage) { + System.out.println(outputMessage); + eventCounter.incrementAndGet(); + } + }); + + latch.await(5000, TimeUnit.MILLISECONDS); + assertEquals(1, subscribeCounter.get()); + assertEquals(groupCount, groupCounter.get()); + assertEquals(count, eventCounter.get()); + + } + + /* + * We will only take 1 group with 20 events from it and then unsubscribe. + */ + @Test + public void testUnsubscribe() throws InterruptedException { + + final AtomicInteger eventCounter = new AtomicInteger(); + final AtomicInteger subscribeCounter = new AtomicInteger(); + final AtomicInteger groupCounter = new AtomicInteger(); + final AtomicInteger sentEventCounter = new AtomicInteger(); + final CountDownLatch latch = new CountDownLatch(1); + final int count = 100; + final int groupCount = 2; + + Observable es = Observable.create(new Observable.OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(final Observer observer) { + final BooleanSubscription s = new BooleanSubscription(); + System.out.println("testUnsubscribe => *** Subscribing to EventStream ***"); + subscribeCounter.incrementAndGet(); + new Thread(new Runnable() { + + @Override + public void run() { + for (int i = 0; i < count; i++) { + if (s.isUnsubscribed()) { + break; + } + Event e = new Event(); + e.source = i % groupCount; + e.message = "Event-" + i; + observer.onNext(e); + sentEventCounter.incrementAndGet(); + } + observer.onCompleted(); + } + + }).start(); + return s; + } + + }); + + es.groupBy(new Func1() { + + @Override + public Integer call(Event e) { + return e.source; + } + }) + .take(1) // we want only the first group + .mapMany(new Func1, Observable>() { + + @Override + public Observable call(GroupedObservable eventGroupedObservable) { + System.out.println("testUnsubscribe => GroupedObservable Key: " + eventGroupedObservable.getKey()); + groupCounter.incrementAndGet(); + + return eventGroupedObservable + .take(20) // limit to only 20 events on this group + .map(new Func1() { + + @Override + public String call(Event event) { + return "testUnsubscribe => Source: " + event.source + " Message: " + event.message; + } + }); + + } + }).subscribe(new Observer() { + + @Override + public void onCompleted() { + latch.countDown(); + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + latch.countDown(); + } + + @Override + public void onNext(String outputMessage) { + System.out.println(outputMessage); + eventCounter.incrementAndGet(); + } + }); + + latch.await(5000, TimeUnit.MILLISECONDS); + assertEquals(1, subscribeCounter.get()); + assertEquals(1, groupCounter.get()); + assertEquals(20, eventCounter.get()); + // sentEvents will go until 'eventCounter' hits 20 and then unsubscribes + // which means it will also send (but ignore) the 19/20 events for the other group + // It will not however send all 100 events. + assertEquals(39, sentEventCounter.get(), 10); + // gave it a delta of 10 to account for the threading/unsubscription race condition which can vary depending on a machines performance, thread-scheduler, etc + } + + private static class Event { + int source; + String message; + + @Override + public String toString() { + return "Event => source: " + source + " message: " + message; + } + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java b/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java index 60b2ad7789..0ffd27c85b 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java @@ -1,7 +1,176 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.concurrency.TestScheduler; +import rx.observables.ConnectableObservable; + +import java.util.concurrent.TimeUnit; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.*; -@Ignore("WIP") public class OperationIntervalTest { + + private TestScheduler scheduler; + private Observer observer; + private Observer observer2; + + @Before + @SuppressWarnings("unchecked") // due to mocking + public void before() { + scheduler = new TestScheduler(); + observer = mock(Observer.class); + observer2 = mock(Observer.class); + } + + @Test + public void testInterval() { + Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler)); + Subscription sub = w.subscribe(observer); + + verify(observer, never()).onNext(0L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(2, TimeUnit.SECONDS); + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(0L); + inOrder.verify(observer, times(1)).onNext(1L); + inOrder.verify(observer, never()).onNext(2L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + sub.unsubscribe(); + scheduler.advanceTimeTo(4, TimeUnit.SECONDS); + verify(observer, never()).onNext(2L); + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + + @Test + public void testWithMultipleSubscribersStartingAtSameTime() { + Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler)); + Subscription sub1 = w.subscribe(observer); + Subscription sub2 = w.subscribe(observer2); + + verify(observer, never()).onNext(anyLong()); + verify(observer2, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(2, TimeUnit.SECONDS); + + InOrder inOrder1 = inOrder(observer); + InOrder inOrder2 = inOrder(observer2); + + inOrder1.verify(observer, times(1)).onNext(0L); + inOrder1.verify(observer, times(1)).onNext(1L); + inOrder1.verify(observer, never()).onNext(2L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + inOrder2.verify(observer2, times(1)).onNext(0L); + inOrder2.verify(observer2, times(1)).onNext(1L); + inOrder2.verify(observer2, never()).onNext(2L); + verify(observer2, never()).onCompleted(); + verify(observer2, never()).onError(any(Throwable.class)); + + sub1.unsubscribe(); + sub2.unsubscribe(); + scheduler.advanceTimeTo(4, TimeUnit.SECONDS); + + verify(observer, never()).onNext(2L); + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + verify(observer2, never()).onNext(2L); + verify(observer2, times(1)).onCompleted(); + verify(observer2, never()).onError(any(Throwable.class)); + } + + @Test + public void testWithMultipleStaggeredSubscribers() { + Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler)); + Subscription sub1 = w.subscribe(observer); + + verify(observer, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(2, TimeUnit.SECONDS); + Subscription sub2 = w.subscribe(observer2); + + InOrder inOrder1 = inOrder(observer); + inOrder1.verify(observer, times(1)).onNext(0L); + inOrder1.verify(observer, times(1)).onNext(1L); + inOrder1.verify(observer, never()).onNext(2L); + + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + verify(observer2, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(4, TimeUnit.SECONDS); + + inOrder1.verify(observer, times(1)).onNext(2L); + inOrder1.verify(observer, times(1)).onNext(3L); + + InOrder inOrder2 = inOrder(observer2); + inOrder2.verify(observer2, times(1)).onNext(0L); + inOrder2.verify(observer2, times(1)).onNext(1L); + + sub1.unsubscribe(); + sub2.unsubscribe(); + + inOrder1.verify(observer, never()).onNext(anyLong()); + inOrder1.verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + inOrder2.verify(observer2, never()).onNext(anyLong()); + inOrder2.verify(observer2, times(1)).onCompleted(); + verify(observer2, never()).onError(any(Throwable.class)); + } + + @Test + public void testWithMultipleStaggeredSubscribersAndPublish() { + ConnectableObservable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler)).publish(); + Subscription sub1 = w.subscribe(observer); + w.connect(); + + verify(observer, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(2, TimeUnit.SECONDS); + Subscription sub2 = w.subscribe(observer2); + + InOrder inOrder1 = inOrder(observer); + inOrder1.verify(observer, times(1)).onNext(0L); + inOrder1.verify(observer, times(1)).onNext(1L); + inOrder1.verify(observer, never()).onNext(2L); + + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + verify(observer2, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(4, TimeUnit.SECONDS); + + inOrder1.verify(observer, times(1)).onNext(2L); + inOrder1.verify(observer, times(1)).onNext(3L); + + InOrder inOrder2 = inOrder(observer2); + inOrder2.verify(observer2, times(1)).onNext(2L); + inOrder2.verify(observer2, times(1)).onNext(3L); + + sub1.unsubscribe(); + sub2.unsubscribe(); + + inOrder1.verify(observer, never()).onNext(anyLong()); + inOrder1.verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + inOrder2.verify(observer2, never()).onNext(anyLong()); + inOrder2.verify(observer2, never()).onCompleted(); + verify(observer2, never()).onError(any(Throwable.class)); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java index 3c3ee5813c..cfd081112e 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java @@ -1,7 +1,276 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import rx.Observable; +import rx.Observer; +import rx.concurrency.Schedulers; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationMap.*; -@Ignore("WIP") public class OperationMapTest { + + @Mock + Observer stringObserver; + @Mock + Observer stringObserver2; + + final static Func2 APPEND_INDEX = new Func2() { + @Override + public String call(String value, Integer index) { + return value + index; + } + }; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testMap() { + Map m1 = getMap("One"); + Map m2 = getMap("Two"); + Observable> observable = Observable.from(m1, m2); + + Observable m = Observable.create(map(observable, new Func1, String>() { + + @Override + public String call(Map map) { + return map.get("firstName"); + } + + })); + m.subscribe(stringObserver); + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(1)).onNext("OneFirst"); + verify(stringObserver, times(1)).onNext("TwoFirst"); + verify(stringObserver, times(1)).onCompleted(); + } + + @Test + public void testMapWithIndex() { + Observable w = Observable.from("a", "b", "c"); + Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX)); + m.subscribe(stringObserver); + InOrder inOrder = inOrder(stringObserver); + inOrder.verify(stringObserver, times(1)).onNext("a0"); + inOrder.verify(stringObserver, times(1)).onNext("b1"); + inOrder.verify(stringObserver, times(1)).onNext("c2"); + inOrder.verify(stringObserver, times(1)).onCompleted(); + verify(stringObserver, never()).onError(any(Throwable.class)); + } + + @Test + public void testMapWithIndexAndMultipleSubscribers() { + Observable w = Observable.from("a", "b", "c"); + Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX)); + m.subscribe(stringObserver); + m.subscribe(stringObserver2); + InOrder inOrder = inOrder(stringObserver); + inOrder.verify(stringObserver, times(1)).onNext("a0"); + inOrder.verify(stringObserver, times(1)).onNext("b1"); + inOrder.verify(stringObserver, times(1)).onNext("c2"); + inOrder.verify(stringObserver, times(1)).onCompleted(); + verify(stringObserver, never()).onError(any(Throwable.class)); + + InOrder inOrder2 = inOrder(stringObserver2); + inOrder2.verify(stringObserver2, times(1)).onNext("a0"); + inOrder2.verify(stringObserver2, times(1)).onNext("b1"); + inOrder2.verify(stringObserver2, times(1)).onNext("c2"); + inOrder2.verify(stringObserver2, times(1)).onCompleted(); + verify(stringObserver2, never()).onError(any(Throwable.class)); + } + + @Test + public void testMapMany() { + /* simulate a top-level async call which returns IDs */ + Observable ids = Observable.from(1, 2); + + /* now simulate the behavior to take those IDs and perform nested async calls based on them */ + Observable m = Observable.create(mapMany(ids, new Func1>() { + + @Override + public Observable call(Integer id) { + /* simulate making a nested async call which creates another Observable */ + Observable> subObservable = null; + if (id == 1) { + Map m1 = getMap("One"); + Map m2 = getMap("Two"); + subObservable = Observable.from(m1, m2); + } else { + Map m3 = getMap("Three"); + Map m4 = getMap("Four"); + subObservable = Observable.from(m3, m4); + } + + /* simulate kicking off the async call and performing a select on it to transform the data */ + return Observable.create(map(subObservable, new Func1, String>() { + @Override + public String call(Map map) { + return map.get("firstName"); + } + })); + } + + })); + m.subscribe(stringObserver); + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(1)).onNext("OneFirst"); + verify(stringObserver, times(1)).onNext("TwoFirst"); + verify(stringObserver, times(1)).onNext("ThreeFirst"); + verify(stringObserver, times(1)).onNext("FourFirst"); + verify(stringObserver, times(1)).onCompleted(); + } + + @Test + public void testMapMany2() { + Map m1 = getMap("One"); + Map m2 = getMap("Two"); + Observable> observable1 = Observable.from(m1, m2); + + Map m3 = getMap("Three"); + Map m4 = getMap("Four"); + Observable> observable2 = Observable.from(m3, m4); + + Observable>> observable = Observable.from(observable1, observable2); + + Observable m = Observable.create(mapMany(observable, new Func1>, Observable>() { + + @Override + public Observable call(Observable> o) { + return Observable.create(map(o, new Func1, String>() { + + @Override + public String call(Map map) { + return map.get("firstName"); + } + })); + } + + })); + m.subscribe(stringObserver); + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(1)).onNext("OneFirst"); + verify(stringObserver, times(1)).onNext("TwoFirst"); + verify(stringObserver, times(1)).onNext("ThreeFirst"); + verify(stringObserver, times(1)).onNext("FourFirst"); + verify(stringObserver, times(1)).onCompleted(); + + } + + @Test + public void testMapWithError() { + Observable w = Observable.from("one", "fail", "two", "three", "fail"); + Observable m = Observable.create(map(w, new Func1() { + @Override + public String call(String s) { + if ("fail".equals(s)) { + throw new RuntimeException("Forced Failure"); + } + return s; + } + })); + + m.subscribe(stringObserver); + verify(stringObserver, times(1)).onNext("one"); + verify(stringObserver, never()).onNext("two"); + verify(stringObserver, never()).onNext("three"); + verify(stringObserver, never()).onCompleted(); + verify(stringObserver, times(1)).onError(any(Throwable.class)); + } + + @Test + public void testMapWithSynchronousObservableContainingError() { + Observable w = Observable.from("one", "fail", "two", "three", "fail"); + final AtomicInteger c1 = new AtomicInteger(); + final AtomicInteger c2 = new AtomicInteger(); + Observable m = Observable.create(map(w, new Func1() { + @Override + public String call(String s) { + if ("fail".equals(s)) + throw new RuntimeException("Forced Failure"); + System.out.println("BadMapper:" + s); + c1.incrementAndGet(); + return s; + } + })).map(new Func1() { + @Override + public String call(String s) { + System.out.println("SecondMapper:" + s); + c2.incrementAndGet(); + return s; + } + }); + + m.subscribe(stringObserver); + + verify(stringObserver, times(1)).onNext("one"); + verify(stringObserver, never()).onNext("two"); + verify(stringObserver, never()).onNext("three"); + verify(stringObserver, never()).onCompleted(); + verify(stringObserver, times(1)).onError(any(Throwable.class)); + + // we should have only returned 1 value: "one" + assertEquals(1, c1.get()); + assertEquals(1, c2.get()); + } + + @Test(expected = IllegalArgumentException.class) + public void testMapWithIssue417() { + Observable.from(1).observeOn(Schedulers.threadPoolForComputation()) + .map(new Func1() { + public Integer call(Integer arg0) { + throw new IllegalArgumentException("any error"); + } + }).toBlockingObservable().single(); + } + + @Test + public void testMapWithErrorInFuncAndThreadPoolScheduler() throws InterruptedException { + // The error will throw in one of threads in the thread pool. + // If map does not handle it, the error will disappear. + // so map needs to handle the error by itself. + final CountDownLatch latch = new CountDownLatch(1); + Observable m = Observable.from("one") + .observeOn(Schedulers.threadPoolForComputation()) + .map(new Func1() { + public String call(String arg0) { + try { + throw new IllegalArgumentException("any error"); + } finally { + latch.countDown(); + } + } + }); + + m.subscribe(stringObserver); + latch.await(); + InOrder inorder = inOrder(stringObserver); + inorder.verify(stringObserver, times(1)).onError(any(IllegalArgumentException.class)); + inorder.verifyNoMoreInteractions(); + } + + private static Map getMap(String prefix) { + Map m = new HashMap(); + m.put("firstName", prefix + "First"); + m.put("lastName", prefix + "Last"); + return m; + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java b/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java index 24f67b432c..f7f4142672 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java @@ -1,7 +1,150 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Notification; +import rx.Observable; +import rx.Observer; +import rx.Subscription; + +import java.util.List; +import java.util.Vector; +import java.util.concurrent.ExecutionException; + +import static org.junit.Assert.*; +import static rx.operators.OperationMaterialize.materialize; -@Ignore("WIP") public class OperationMaterializeTest { + + @Test + public void testMaterialize1() { + // null will cause onError to be triggered before "three" can be returned + final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", null, "three"); + + TestObserver Observer = new TestObserver(); + Observable> m = Observable.create(materialize(Observable.create(o1))); + m.subscribe(Observer); + + try { + o1.t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + assertFalse(Observer.onError); + assertTrue(Observer.onCompleted); + assertEquals(3, Observer.notifications.size()); + assertEquals("one", Observer.notifications.get(0).getValue()); + assertTrue(Observer.notifications.get(0).isOnNext()); + assertEquals("two", Observer.notifications.get(1).getValue()); + assertTrue(Observer.notifications.get(1).isOnNext()); + assertEquals(NullPointerException.class, Observer.notifications.get(2).getThrowable().getClass()); + assertTrue(Observer.notifications.get(2).isOnError()); + } + + @Test + public void testMaterialize2() { + final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", "three"); + + TestObserver Observer = new TestObserver(); + Observable> m = Observable.create(materialize(Observable.create(o1))); + m.subscribe(Observer); + + try { + o1.t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + assertFalse(Observer.onError); + assertTrue(Observer.onCompleted); + assertEquals(4, Observer.notifications.size()); + assertEquals("one", Observer.notifications.get(0).getValue()); + assertTrue(Observer.notifications.get(0).isOnNext()); + assertEquals("two", Observer.notifications.get(1).getValue()); + assertTrue(Observer.notifications.get(1).isOnNext()); + assertEquals("three", Observer.notifications.get(2).getValue()); + assertTrue(Observer.notifications.get(2).isOnNext()); + assertTrue(Observer.notifications.get(3).isOnCompleted()); + } + + @Test + public void testMultipleSubscribes() throws InterruptedException, ExecutionException { + final TestAsyncErrorObservable o = new TestAsyncErrorObservable("one", "two", null, "three"); + + Observable> m = Observable.create(materialize(Observable.create(o))); + + assertEquals(3, m.toList().toBlockingObservable().toFuture().get().size()); + assertEquals(3, m.toList().toBlockingObservable().toFuture().get().size()); + } + + private static class TestObserver implements Observer> { + + boolean onCompleted = false; + boolean onError = false; + List> notifications = new Vector>(); + + @Override + public void onCompleted() { + this.onCompleted = true; + } + + @Override + public void onError(Throwable e) { + this.onError = true; + } + + @Override + public void onNext(Notification value) { + this.notifications.add(value); + } + + } + + private static class TestAsyncErrorObservable implements Observable.OnSubscribeFunc { + + String[] valuesToReturn; + + TestAsyncErrorObservable(String... values) { + valuesToReturn = values; + } + + volatile Thread t; + + @Override + public Subscription onSubscribe(final Observer observer) { + t = new Thread(new Runnable() { + + @Override + public void run() { + for (String s : valuesToReturn) { + if (s == null) { + System.out.println("throwing exception"); + try { + Thread.sleep(100); + } catch (Throwable e) { + + } + observer.onError(new NullPointerException()); + return; + } else { + observer.onNext(s); + } + } + System.out.println("subscription complete"); + observer.onCompleted(); + } + + }); + t.start(); + + return new Subscription() { + + @Override + public void unsubscribe() { + + } + + }; + } + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java b/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java index 1e3c763573..03dbcd7044 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java @@ -1,7 +1,500 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.util.CompositeException; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationMergeDelayError.mergeDelayError; -@Ignore("WIP") public class OperationMergeDelayErrorTest { + + + @Mock + Observer stringObserver; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testErrorDelayed1() { + final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o2 = Observable.create(new TestErrorObservable("one", "two", "three")); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(mergeDelayError(o1, o2)); + m.subscribe(stringObserver); + + verify(stringObserver, times(1)).onError(any(NullPointerException.class)); + verify(stringObserver, never()).onCompleted(); + verify(stringObserver, times(1)).onNext("one"); + verify(stringObserver, times(1)).onNext("two"); + verify(stringObserver, times(1)).onNext("three"); + verify(stringObserver, times(1)).onNext("four"); + verify(stringObserver, times(0)).onNext("five"); + verify(stringObserver, times(0)).onNext("six"); + } + + @Test + public void testErrorDelayed2() { + final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); + final Observable o2 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null)); + final Observable o4 = Observable.create(new TestErrorObservable("nine")); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4)); + m.subscribe(stringObserver); + + verify(stringObserver, times(1)).onError(any(NullPointerException.class)); + verify(stringObserver, never()).onCompleted(); + verify(stringObserver, times(1)).onNext("one"); + verify(stringObserver, times(1)).onNext("two"); + verify(stringObserver, times(1)).onNext("three"); + verify(stringObserver, times(1)).onNext("four"); + verify(stringObserver, times(0)).onNext("five"); + verify(stringObserver, times(0)).onNext("six"); + verify(stringObserver, times(1)).onNext("seven"); + verify(stringObserver, times(1)).onNext("eight"); + verify(stringObserver, times(1)).onNext("nine"); + } + + @Test + public void testErrorDelayed3() { + final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); + final Observable o2 = Observable.create(new TestErrorObservable("four", "five", "six")); + final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null)); + final Observable o4 = Observable.create(new TestErrorObservable("nine")); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4)); + m.subscribe(stringObserver); + + verify(stringObserver, times(1)).onError(any(NullPointerException.class)); + verify(stringObserver, never()).onCompleted(); + verify(stringObserver, times(1)).onNext("one"); + verify(stringObserver, times(1)).onNext("two"); + verify(stringObserver, times(1)).onNext("three"); + verify(stringObserver, times(1)).onNext("four"); + verify(stringObserver, times(1)).onNext("five"); + verify(stringObserver, times(1)).onNext("six"); + verify(stringObserver, times(1)).onNext("seven"); + verify(stringObserver, times(1)).onNext("eight"); + verify(stringObserver, times(1)).onNext("nine"); + } + + @Test + public void testErrorDelayed4() { + final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); + final Observable o2 = Observable.create(new TestErrorObservable("four", "five", "six")); + final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight")); + final Observable o4 = Observable.create(new TestErrorObservable("nine", null)); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4)); + m.subscribe(stringObserver); + + verify(stringObserver, times(1)).onError(any(NullPointerException.class)); + verify(stringObserver, never()).onCompleted(); + verify(stringObserver, times(1)).onNext("one"); + verify(stringObserver, times(1)).onNext("two"); + verify(stringObserver, times(1)).onNext("three"); + verify(stringObserver, times(1)).onNext("four"); + verify(stringObserver, times(1)).onNext("five"); + verify(stringObserver, times(1)).onNext("six"); + verify(stringObserver, times(1)).onNext("seven"); + verify(stringObserver, times(1)).onNext("eight"); + verify(stringObserver, times(1)).onNext("nine"); + } + + @Test + public void testErrorDelayed4WithThreading() { + final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", "three"); + final TestAsyncErrorObservable o2 = new TestAsyncErrorObservable("four", "five", "six"); + final TestAsyncErrorObservable o3 = new TestAsyncErrorObservable("seven", "eight"); + // throw the error at the very end so no onComplete will be called after it + final TestAsyncErrorObservable o4 = new TestAsyncErrorObservable("nine", null); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(mergeDelayError(Observable.create(o1), Observable.create(o2), Observable.create(o3), Observable.create(o4))); + m.subscribe(stringObserver); + + try { + o1.t.join(); + o2.t.join(); + o3.t.join(); + o4.t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + verify(stringObserver, times(1)).onError(any(NullPointerException.class)); + verify(stringObserver, never()).onCompleted(); + verify(stringObserver, times(1)).onNext("one"); + verify(stringObserver, times(1)).onNext("two"); + verify(stringObserver, times(1)).onNext("three"); + verify(stringObserver, times(1)).onNext("four"); + verify(stringObserver, times(1)).onNext("five"); + verify(stringObserver, times(1)).onNext("six"); + verify(stringObserver, times(1)).onNext("seven"); + verify(stringObserver, times(1)).onNext("eight"); + verify(stringObserver, times(1)).onNext("nine"); + } + + @Test + public void testCompositeErrorDelayed1() { + final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o2 = Observable.create(new TestErrorObservable("one", "two", null)); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(mergeDelayError(o1, o2)); + m.subscribe(stringObserver); + + verify(stringObserver, times(1)).onError(any(CompositeException.class)); + verify(stringObserver, never()).onCompleted(); + verify(stringObserver, times(1)).onNext("one"); + verify(stringObserver, times(1)).onNext("two"); + verify(stringObserver, times(0)).onNext("three"); + verify(stringObserver, times(1)).onNext("four"); + verify(stringObserver, times(0)).onNext("five"); + verify(stringObserver, times(0)).onNext("six"); + } + + @Test + public void testCompositeErrorDelayed2() { + final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o2 = Observable.create(new TestErrorObservable("one", "two", null)); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(mergeDelayError(o1, o2)); + CaptureObserver w = new CaptureObserver(); + m.subscribe(w); + + assertNotNull(w.e); + if (w.e instanceof CompositeException) { + assertEquals(2, ((CompositeException) w.e).getExceptions().size()); + w.e.printStackTrace(); + } else { + fail("Expecting CompositeException"); + } + + } + + /** + * The unit tests below are from OperationMerge and should ensure the normal merge functionality is correct. + */ + + @Test + public void testMergeObservableOfObservables() { + final Observable o1 = Observable.create(new TestSynchronousObservable()); + final Observable o2 = Observable.create(new TestSynchronousObservable()); + + Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() { + + @Override + public Subscription onSubscribe(Observer> observer) { + // simulate what would happen in an observable + observer.onNext(o1); + observer.onNext(o2); + observer.onCompleted(); + + return new Subscription() { + + @Override + public void unsubscribe() { + // unregister ... will never be called here since we are executing synchronously + } + + }; + } + + }); + Observable m = Observable.create(mergeDelayError(observableOfObservables)); + m.subscribe(stringObserver); + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(1)).onCompleted(); + verify(stringObserver, times(2)).onNext("hello"); + } + + @Test + public void testMergeArray() { + final Observable o1 = Observable.create(new TestSynchronousObservable()); + final Observable o2 = Observable.create(new TestSynchronousObservable()); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(mergeDelayError(o1, o2)); + m.subscribe(stringObserver); + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(2)).onNext("hello"); + verify(stringObserver, times(1)).onCompleted(); + } + + @Test + public void testMergeList() { + final Observable o1 = Observable.create(new TestSynchronousObservable()); + final Observable o2 = Observable.create(new TestSynchronousObservable()); + List> listOfObservables = new ArrayList>(); + listOfObservables.add(o1); + listOfObservables.add(o2); + + Observable m = Observable.create(mergeDelayError(listOfObservables)); + m.subscribe(stringObserver); + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(1)).onCompleted(); + verify(stringObserver, times(2)).onNext("hello"); + } + + @Test + public void testUnSubscribe() { + TestObservable tA = new TestObservable(); + TestObservable tB = new TestObservable(); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(mergeDelayError(Observable.create(tA), Observable.create(tB))); + Subscription s = m.subscribe(stringObserver); + + tA.sendOnNext("Aone"); + tB.sendOnNext("Bone"); + s.unsubscribe(); + tA.sendOnNext("Atwo"); + tB.sendOnNext("Btwo"); + tA.sendOnCompleted(); + tB.sendOnCompleted(); + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(1)).onNext("Aone"); + verify(stringObserver, times(1)).onNext("Bone"); + assertTrue(tA.unsubscribed); + assertTrue(tB.unsubscribed); + verify(stringObserver, never()).onNext("Atwo"); + verify(stringObserver, never()).onNext("Btwo"); + verify(stringObserver, never()).onCompleted(); + } + + @Test + public void testMergeArrayWithThreading() { + final TestASynchronousObservable o1 = new TestASynchronousObservable(); + final TestASynchronousObservable o2 = new TestASynchronousObservable(); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(mergeDelayError(Observable.create(o1), Observable.create(o2))); + m.subscribe(stringObserver); + + try { + o1.t.join(); + o2.t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(2)).onNext("hello"); + verify(stringObserver, times(1)).onCompleted(); + } + + private static class TestSynchronousObservable implements Observable.OnSubscribeFunc { + + @Override + public Subscription onSubscribe(Observer observer) { + + observer.onNext("hello"); + observer.onCompleted(); + + return new Subscription() { + + @Override + public void unsubscribe() { + // unregister ... will never be called here since we are executing synchronously + } + + }; + } + } + + private static class TestASynchronousObservable implements Observable.OnSubscribeFunc { + Thread t; + + @Override + public Subscription onSubscribe(final Observer observer) { + t = new Thread(new Runnable() { + + @Override + public void run() { + observer.onNext("hello"); + observer.onCompleted(); + } + + }); + t.start(); + + return new Subscription() { + + @Override + public void unsubscribe() { + + } + + }; + } + } + + /** + * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. + */ + private static class TestObservable implements Observable.OnSubscribeFunc { + + Observer observer = null; + volatile boolean unsubscribed = false; + Subscription s = new Subscription() { + + @Override + public void unsubscribe() { + unsubscribed = true; + + } + + }; + + /* used to simulate subscription */ + public void sendOnCompleted() { + observer.onCompleted(); + } + + /* used to simulate subscription */ + public void sendOnNext(String value) { + observer.onNext(value); + } + + /* used to simulate subscription */ + @SuppressWarnings("unused") + public void sendOnError(Throwable e) { + observer.onError(e); + } + + @Override + public Subscription onSubscribe(final Observer observer) { + this.observer = observer; + return s; + } + } + + private static class TestErrorObservable implements Observable.OnSubscribeFunc { + + String[] valuesToReturn; + + TestErrorObservable(String... values) { + valuesToReturn = values; + } + + @Override + public Subscription onSubscribe(Observer observer) { + boolean errorThrown = false; + for (String s : valuesToReturn) { + if (s == null) { + System.out.println("throwing exception"); + observer.onError(new NullPointerException()); + errorThrown = true; + // purposefully not returning here so it will continue calling onNext + // so that we also test that we handle bad sequences like this + } else { + observer.onNext(s); + } + } + if (!errorThrown) { + observer.onCompleted(); + } + + return new Subscription() { + + @Override + public void unsubscribe() { + // unregister ... will never be called here since we are executing synchronously + } + + }; + } + } + + private static class TestAsyncErrorObservable implements Observable.OnSubscribeFunc { + + String[] valuesToReturn; + + TestAsyncErrorObservable(String... values) { + valuesToReturn = values; + } + + Thread t; + + @Override + public Subscription onSubscribe(final Observer observer) { + t = new Thread(new Runnable() { + + @Override + public void run() { + for (String s : valuesToReturn) { + if (s == null) { + System.out.println("throwing exception"); + try { + Thread.sleep(100); + } catch (Throwable e) { + + } + observer.onError(new NullPointerException()); + return; + } else { + observer.onNext(s); + } + } + System.out.println("subscription complete"); + observer.onCompleted(); + } + + }); + t.start(); + + return new Subscription() { + + @Override + public void unsubscribe() { + + } + + }; + } + } + + private static class CaptureObserver implements Observer { + volatile Throwable e; + + @Override + public void onCompleted() { + + } + + @Override + public void onError(Throwable e) { + this.e = e; + } + + @Override + public void onNext(String args) { + + } + + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java b/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java index ef50b179e6..b3bdf1423c 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java @@ -1,7 +1,458 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; +import rx.util.functions.Action1; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationMerge.merge; -@Ignore("WIP") public class OperationMergeTest { + + @Mock + Observer stringObserver; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testMergeObservableOfObservables() { + final Observable o1 = Observable.create(new TestSynchronousObservable()); + final Observable o2 = Observable.create(new TestSynchronousObservable()); + + Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() { + + @Override + public Subscription onSubscribe(Observer> observer) { + // simulate what would happen in an observable + observer.onNext(o1); + observer.onNext(o2); + observer.onCompleted(); + + return new Subscription() { + + @Override + public void unsubscribe() { + // unregister ... will never be called here since we are executing synchronously + } + + }; + } + + }); + Observable m = Observable.create(merge(observableOfObservables)); + m.subscribe(stringObserver); + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(1)).onCompleted(); + verify(stringObserver, times(2)).onNext("hello"); + } + + @Test + public void testMergeArray() { + final Observable o1 = Observable.create(new TestSynchronousObservable()); + final Observable o2 = Observable.create(new TestSynchronousObservable()); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(merge(o1, o2)); + m.subscribe(stringObserver); + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(2)).onNext("hello"); + verify(stringObserver, times(1)).onCompleted(); + } + + @Test + public void testMergeList() { + final Observable o1 = Observable.create(new TestSynchronousObservable()); + final Observable o2 = Observable.create(new TestSynchronousObservable()); + List> listOfObservables = new ArrayList>(); + listOfObservables.add(o1); + listOfObservables.add(o2); + + Observable m = Observable.create(merge(listOfObservables)); + m.subscribe(stringObserver); + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(1)).onCompleted(); + verify(stringObserver, times(2)).onNext("hello"); + } + + @Test + public void testUnSubscribe() { + TestObservable tA = new TestObservable(); + TestObservable tB = new TestObservable(); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(merge(Observable.create(tA), Observable.create(tB))); + Subscription s = m.subscribe(stringObserver); + + tA.sendOnNext("Aone"); + tB.sendOnNext("Bone"); + s.unsubscribe(); + tA.sendOnNext("Atwo"); + tB.sendOnNext("Btwo"); + tA.sendOnCompleted(); + tB.sendOnCompleted(); + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(1)).onNext("Aone"); + verify(stringObserver, times(1)).onNext("Bone"); + assertTrue(tA.unsubscribed); + assertTrue(tB.unsubscribed); + verify(stringObserver, never()).onNext("Atwo"); + verify(stringObserver, never()).onNext("Btwo"); + verify(stringObserver, never()).onCompleted(); + } + + @Test + public void testUnSubscribeObservableOfObservables() throws InterruptedException { + + final AtomicBoolean unsubscribed = new AtomicBoolean(); + final CountDownLatch latch = new CountDownLatch(1); + + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + + @Override + public Subscription onSubscribe(final Observer> observer) { + // verbose on purpose so I can track the inside of it + final Subscription s = Subscriptions.create(new Action0() { + + @Override + public void call() { + System.out.println("*** unsubscribed"); + unsubscribed.set(true); + } + + }); + + new Thread(new Runnable() { + + @Override + public void run() { + + while (!unsubscribed.get()) { + observer.onNext(Observable.from(1L, 2L)); + } + System.out.println("Done looping after unsubscribe: " + unsubscribed.get()); + observer.onCompleted(); + + // mark that the thread is finished + latch.countDown(); + } + }).start(); + + return s; + } + + ; + + }); + + final AtomicInteger count = new AtomicInteger(); + Observable.create(merge(source)).take(6).toBlockingObservable().forEach(new Action1() { + + @Override + public void call(Long v) { + System.out.println("Value: " + v); + int c = count.incrementAndGet(); + if (c > 6) { + fail("Should be only 6"); + } + + } + }); + + latch.await(1000, TimeUnit.MILLISECONDS); + + System.out.println("unsubscribed: " + unsubscribed.get()); + + assertTrue(unsubscribed.get()); + + } + + @Test + public void testMergeArrayWithThreading() { + final TestASynchronousObservable o1 = new TestASynchronousObservable(); + final TestASynchronousObservable o2 = new TestASynchronousObservable(); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(merge(Observable.create(o1), Observable.create(o2))); + m.subscribe(stringObserver); + + try { + o1.t.join(); + o2.t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + verify(stringObserver, never()).onError(any(Throwable.class)); + verify(stringObserver, times(2)).onNext("hello"); + verify(stringObserver, times(1)).onCompleted(); + } + + @Test + public void testSynchronizationOfMultipleSequences() throws Throwable { + final TestASynchronousObservable o1 = new TestASynchronousObservable(); + final TestASynchronousObservable o2 = new TestASynchronousObservable(); + + // use this latch to cause onNext to wait until we're ready to let it go + final CountDownLatch endLatch = new CountDownLatch(1); + + final AtomicInteger concurrentCounter = new AtomicInteger(); + final AtomicInteger totalCounter = new AtomicInteger(); + + @SuppressWarnings("unchecked") + Observable m = Observable.create(merge(Observable.create(o1), Observable.create(o2))); + m.subscribe(new Observer() { + + @Override + public void onCompleted() { + + } + + @Override + public void onError(Throwable e) { + throw new RuntimeException("failed", e); + } + + @Override + public void onNext(String v) { + totalCounter.incrementAndGet(); + concurrentCounter.incrementAndGet(); + try { + // wait here until we're done asserting + endLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + throw new RuntimeException("failed", e); + } finally { + concurrentCounter.decrementAndGet(); + } + } + + }); + + // wait for both observables to send (one should be blocked) + o1.onNextBeingSent.await(); + o2.onNextBeingSent.await(); + + // I can't think of a way to know for sure that both threads have or are trying to send onNext + // since I can't use a CountDownLatch for "after" onNext since I want to catch during it + // but I can't know for sure onNext is invoked + // so I'm unfortunately reverting to using a Thread.sleep to allow the process scheduler time + // to make sure after o1.onNextBeingSent and o2.onNextBeingSent are hit that the following + // onNext is invoked. + + Thread.sleep(300); + + try { // in try/finally so threads are released via latch countDown even if assertion fails + assertEquals(1, concurrentCounter.get()); + } finally { + // release so it can finish + endLatch.countDown(); + } + + try { + o1.t.join(); + o2.t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + assertEquals(2, totalCounter.get()); + assertEquals(0, concurrentCounter.get()); + } + + /** + * unit test from OperationMergeDelayError backported here to show how these use cases work with normal merge + */ + @Test + public void testError1() { + // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior + final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" + final Observable o2 = Observable.create(new TestErrorObservable("one", "two", "three")); // we expect to lose all of these since o1 is done first and fails + + @SuppressWarnings("unchecked") + Observable m = Observable.create(merge(o1, o2)); + m.subscribe(stringObserver); + + verify(stringObserver, times(1)).onError(any(NullPointerException.class)); + verify(stringObserver, never()).onCompleted(); + verify(stringObserver, times(0)).onNext("one"); + verify(stringObserver, times(0)).onNext("two"); + verify(stringObserver, times(0)).onNext("three"); + verify(stringObserver, times(1)).onNext("four"); + verify(stringObserver, times(0)).onNext("five"); + verify(stringObserver, times(0)).onNext("six"); + } + + /** + * unit test from OperationMergeDelayError backported here to show how these use cases work with normal merge + */ + @Test + public void testError2() { + // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior + final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three")); + final Observable o2 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" + final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null));// we expect to lose all of these since o2 is done first and fails + final Observable o4 = Observable.create(new TestErrorObservable("nine"));// we expect to lose all of these since o2 is done first and fails + + @SuppressWarnings("unchecked") + Observable m = Observable.create(merge(o1, o2, o3, o4)); + m.subscribe(stringObserver); + + verify(stringObserver, times(1)).onError(any(NullPointerException.class)); + verify(stringObserver, never()).onCompleted(); + verify(stringObserver, times(1)).onNext("one"); + verify(stringObserver, times(1)).onNext("two"); + verify(stringObserver, times(1)).onNext("three"); + verify(stringObserver, times(1)).onNext("four"); + verify(stringObserver, times(0)).onNext("five"); + verify(stringObserver, times(0)).onNext("six"); + verify(stringObserver, times(0)).onNext("seven"); + verify(stringObserver, times(0)).onNext("eight"); + verify(stringObserver, times(0)).onNext("nine"); + } + + private static class TestSynchronousObservable implements Observable.OnSubscribeFunc { + + @Override + public Subscription onSubscribe(Observer observer) { + + observer.onNext("hello"); + observer.onCompleted(); + + return new Subscription() { + + @Override + public void unsubscribe() { + // unregister ... will never be called here since we are executing synchronously + } + + }; + } + } + + private static class TestASynchronousObservable implements Observable.OnSubscribeFunc { + Thread t; + final CountDownLatch onNextBeingSent = new CountDownLatch(1); + + @Override + public Subscription onSubscribe(final Observer observer) { + t = new Thread(new Runnable() { + + @Override + public void run() { + onNextBeingSent.countDown(); + observer.onNext("hello"); + // I can't use a countDownLatch to prove we are actually sending 'onNext' + // since it will block if synchronized and I'll deadlock + observer.onCompleted(); + } + + }); + t.start(); + + return new Subscription() { + + @Override + public void unsubscribe() { + + } + + }; + } + } + + /** + * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. + */ + private static class TestObservable implements Observable.OnSubscribeFunc { + + Observer observer = null; + volatile boolean unsubscribed = false; + Subscription s = new Subscription() { + + @Override + public void unsubscribe() { + unsubscribed = true; + + } + + }; + + /* used to simulate subscription */ + public void sendOnCompleted() { + observer.onCompleted(); + } + + /* used to simulate subscription */ + public void sendOnNext(String value) { + observer.onNext(value); + } + + /* used to simulate subscription */ + @SuppressWarnings("unused") + public void sendOnError(Throwable e) { + observer.onError(e); + } + + @Override + public Subscription onSubscribe(final Observer observer) { + this.observer = observer; + return s; + } + } + + private static class TestErrorObservable implements Observable.OnSubscribeFunc { + + String[] valuesToReturn; + + TestErrorObservable(String... values) { + valuesToReturn = values; + } + + @Override + public Subscription onSubscribe(Observer observer) { + + for (String s : valuesToReturn) { + if (s == null) { + System.out.println("throwing exception"); + observer.onError(new NullPointerException()); + } else { + observer.onNext(s); + } + } + observer.onCompleted(); + + return new Subscription() { + + @Override + public void unsubscribe() { + // unregister ... will never be called here since we are executing synchronously + } + + }; + } + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java b/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java index 1f5806b310..35c6c9fadf 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java @@ -1,7 +1,59 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.subjects.PublishSubject; +import rx.subjects.Subject; + +import java.util.Iterator; + +import static org.junit.Assert.*; +import static rx.operators.OperationMostRecent.mostRecent; -@Ignore("WIP") public class OperationMostRecentTest { + + + @Test + public void testMostRecent() { + Subject observable = PublishSubject.create(); + + Iterator it = mostRecent(observable, "default").iterator(); + + assertTrue(it.hasNext()); + assertEquals("default", it.next()); + assertEquals("default", it.next()); + + observable.onNext("one"); + assertTrue(it.hasNext()); + assertEquals("one", it.next()); + assertEquals("one", it.next()); + + observable.onNext("two"); + assertTrue(it.hasNext()); + assertEquals("two", it.next()); + assertEquals("two", it.next()); + + observable.onCompleted(); + assertFalse(it.hasNext()); + + } + + @Test(expected = TestException.class) + public void testMostRecentWithException() { + Subject observable = PublishSubject.create(); + + Iterator it = mostRecent(observable, "default").iterator(); + + assertTrue(it.hasNext()); + assertEquals("default", it.next()); + assertEquals("default", it.next()); + + observable.onError(new TestException()); + assertTrue(it.hasNext()); + + it.next(); + } + + private static class TestException extends RuntimeException { + private static final long serialVersionUID = 1L; + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java b/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java index dd2ba06360..e33ee00de3 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java @@ -1,7 +1,97 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observer; +import rx.Subscription; +import rx.observables.ConnectableObservable; +import rx.subjects.PublishSubject; +import rx.subjects.Subject; + +import static org.mockito.Mockito.*; -@Ignore("WIP") public class OperationMulticastTest { + + @Test + public void testMulticast() { + Subject source = PublishSubject.create(); + + ConnectableObservable multicasted = OperationMulticast.multicast(source, + PublishSubject.create()); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + multicasted.subscribe(observer); + + source.onNext("one"); + source.onNext("two"); + + multicasted.connect(); + + source.onNext("three"); + source.onNext("four"); + source.onCompleted(); + + verify(observer, never()).onNext("one"); + verify(observer, never()).onNext("two"); + verify(observer, times(1)).onNext("three"); + verify(observer, times(1)).onNext("four"); + verify(observer, times(1)).onCompleted(); + + } + + @Test + public void testMulticastConnectTwice() { + Subject source = PublishSubject.create(); + + ConnectableObservable multicasted = OperationMulticast.multicast(source, + PublishSubject.create()); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + multicasted.subscribe(observer); + + source.onNext("one"); + + multicasted.connect(); + multicasted.connect(); + + source.onNext("two"); + source.onCompleted(); + + verify(observer, never()).onNext("one"); + verify(observer, times(1)).onNext("two"); + verify(observer, times(1)).onCompleted(); + + } + + @Test + public void testMulticastDisconnect() { + Subject source = PublishSubject.create(); + + ConnectableObservable multicasted = OperationMulticast.multicast(source, + PublishSubject.create()); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + multicasted.subscribe(observer); + + source.onNext("one"); + + Subscription connection = multicasted.connect(); + source.onNext("two"); + + connection.unsubscribe(); + source.onNext("three"); + + multicasted.connect(); + source.onNext("four"); + source.onCompleted(); + + verify(observer, never()).onNext("one"); + verify(observer, times(1)).onNext("two"); + verify(observer, never()).onNext("three"); + verify(observer, times(1)).onNext("four"); + verify(observer, times(1)).onCompleted(); + + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationNextTest.java b/rxjava-core/src/test/java/rx/operators/OperationNextTest.java index bad26bbcb1..c696a64c27 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationNextTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationNextTest.java @@ -1,7 +1,281 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.concurrency.Schedulers; +import rx.subjects.PublishSubject; +import rx.subjects.Subject; +import rx.subscriptions.Subscriptions; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.*; +import static rx.operators.OperationNext.next; -@Ignore("WIP") public class OperationNextTest { + + private void fireOnNextInNewThread(final Subject o, final String value) { + new Thread() { + @Override + public void run() { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // ignore + } + o.onNext(value); + } + }.start(); + } + + private void fireOnErrorInNewThread(final Subject o) { + new Thread() { + @Override + public void run() { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // ignore + } + o.onError(new TestException()); + } + }.start(); + } + + + @Test + public void testNext() { + Subject obs = PublishSubject.create(); + Iterator it = next(obs).iterator(); + fireOnNextInNewThread(obs, "one"); + assertTrue(it.hasNext()); + assertEquals("one", it.next()); + + fireOnNextInNewThread(obs, "two"); + assertTrue(it.hasNext()); + assertEquals("two", it.next()); + + obs.onCompleted(); + assertFalse(it.hasNext()); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } catch (NoSuchElementException e) { + } + + // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException. + assertFalse(it.hasNext()); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } catch (NoSuchElementException e) { + } + } + + @Test + public void testNextWithError() { + Subject obs = PublishSubject.create(); + Iterator it = next(obs).iterator(); + fireOnNextInNewThread(obs, "one"); + assertTrue(it.hasNext()); + assertEquals("one", it.next()); + + fireOnErrorInNewThread(obs); + try { + it.hasNext(); + fail("Expected an TestException"); + } catch (TestException e) { + } + + assertErrorAfterObservableFail(it); + } + + @Test + public void testNextWithEmpty() { + Observable obs = Observable.empty().observeOn(Schedulers.newThread()); + Iterator it = next(obs).iterator(); + + assertFalse(it.hasNext()); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } catch (NoSuchElementException e) { + } + + // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException. + assertFalse(it.hasNext()); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } catch (NoSuchElementException e) { + } + } + + @Test + public void testOnError() throws Throwable { + Subject obs = PublishSubject.create(); + Iterator it = next(obs).iterator(); + + obs.onError(new TestException()); + try { + it.hasNext(); + fail("Expected an TestException"); + } catch (TestException e) { + // successful + } + + assertErrorAfterObservableFail(it); + } + + @Test + public void testOnErrorInNewThread() { + Subject obs = PublishSubject.create(); + Iterator it = next(obs).iterator(); + + fireOnErrorInNewThread(obs); + + try { + it.hasNext(); + fail("Expected an TestException"); + } catch (TestException e) { + // successful + } + + assertErrorAfterObservableFail(it); + } + + private void assertErrorAfterObservableFail(Iterator it) { + // After the observable fails, hasNext and next always throw the exception. + try { + it.hasNext(); + fail("hasNext should throw a TestException"); + } catch (TestException e) { + } + try { + it.next(); + fail("next should throw a TestException"); + } catch (TestException e) { + } + } + + @Test + public void testNextWithOnlyUsingNextMethod() { + Subject obs = PublishSubject.create(); + Iterator it = next(obs).iterator(); + fireOnNextInNewThread(obs, "one"); + assertEquals("one", it.next()); + + fireOnNextInNewThread(obs, "two"); + assertEquals("two", it.next()); + + obs.onCompleted(); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } catch (NoSuchElementException e) { + } + } + + @Test + public void testNextWithCallingHasNextMultipleTimes() { + Subject obs = PublishSubject.create(); + Iterator it = next(obs).iterator(); + fireOnNextInNewThread(obs, "one"); + assertTrue(it.hasNext()); + assertTrue(it.hasNext()); + assertTrue(it.hasNext()); + assertTrue(it.hasNext()); + assertEquals("one", it.next()); + + obs.onCompleted(); + try { + it.next(); + fail("At the end of an iterator should throw a NoSuchElementException"); + } catch (NoSuchElementException e) { + } + } + + @SuppressWarnings("serial") + private static class TestException extends RuntimeException { + + } + + /** + * Confirm that no buffering or blocking of the Observable onNext calls occurs and it just grabs the next emitted value. + *

    + * This results in output such as => a: 1 b: 2 c: 89 + * + * @throws Throwable + */ + @Test + public void testNoBufferingOrBlockingOfSequence() throws Throwable { + final CountDownLatch finished = new CountDownLatch(1); + final int COUNT = 30; + final CountDownLatch timeHasPassed = new CountDownLatch(COUNT); + final AtomicBoolean running = new AtomicBoolean(true); + final AtomicInteger count = new AtomicInteger(0); + final Observable obs = Observable.create(new Observable.OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(final Observer o) { + new Thread(new Runnable() { + + @Override + public void run() { + try { + while (running.get()) { + o.onNext(count.incrementAndGet()); + timeHasPassed.countDown(); + } + o.onCompleted(); + } catch (Throwable e) { + o.onError(e); + } finally { + finished.countDown(); + } + } + }).start(); + return Subscriptions.empty(); + } + + }); + + Iterator it = next(obs).iterator(); + + assertTrue(it.hasNext()); + int a = it.next(); + assertTrue(it.hasNext()); + int b = it.next(); + // we should have a different value + assertTrue("a and b should be different", a != b); + + // wait for some time (if times out we are blocked somewhere so fail ... set very high for very slow, constrained machines) + timeHasPassed.await(8000, TimeUnit.MILLISECONDS); + + assertTrue(it.hasNext()); + int c = it.next(); + + assertTrue("c should not just be the next in sequence", c != (b + 1)); + assertTrue("expected that c [" + c + "] is higher than or equal to " + COUNT, c >= COUNT); + + assertTrue(it.hasNext()); + int d = it.next(); + assertTrue(d > c); + + // shut down the thread + running.set(false); + + finished.await(); + + assertFalse(it.hasNext()); + + System.out.println("a: " + a + " b: " + b + " c: " + c); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java b/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java index 9f439949a8..17b5292130 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java @@ -1,7 +1,68 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import rx.Observable; +import rx.Observer; +import rx.concurrency.Schedulers; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.*; +import static rx.operators.OperationObserveOn.observeOn; -@Ignore("WIP") public class OperationObserveOnTest { + + /** + * This is testing a no-op path since it uses Schedulers.immediate() which will not do scheduling. + */ + @Test + @SuppressWarnings("unchecked") + public void testObserveOn() { + Observer observer = mock(Observer.class); + Observable.create(observeOn(Observable.from(1, 2, 3), Schedulers.immediate())).subscribe(observer); + + verify(observer, times(1)).onNext(1); + verify(observer, times(1)).onNext(2); + verify(observer, times(1)).onNext(3); + verify(observer, times(1)).onCompleted(); + } + + @Test + @SuppressWarnings("unchecked") + public void testOrdering() throws InterruptedException { + Observable obs = Observable.from("one", null, "two", "three", "four"); + + Observer observer = mock(Observer.class); + + InOrder inOrder = inOrder(observer); + + final CountDownLatch completedLatch = new CountDownLatch(1); + doAnswer(new Answer() { + + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + completedLatch.countDown(); + return null; + } + }).when(observer).onCompleted(); + + obs.observeOn(Schedulers.threadPoolForComputation()).subscribe(observer); + + if (!completedLatch.await(1000, TimeUnit.MILLISECONDS)) { + fail("timed out waiting"); + } + + inOrder.verify(observer, times(1)).onNext("one"); + inOrder.verify(observer, times(1)).onNext(null); + 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)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java index 8a98b35dd2..3de89d6a49 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java @@ -1,7 +1,168 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Func1; + +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction; -@Ignore("WIP") public class OperationOnErrorResumeNextViaFunctionTest { + + @Test + public void testResumeNextWithSynchronousExecution() { + final AtomicReference receivedException = new AtomicReference(); + Observable w = Observable.create(new Observable.OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onError(new Throwable("injected failure")); + return Subscriptions.empty(); + } + }); + + Func1> resume = new Func1>() { + + @Override + public Observable call(Throwable t1) { + receivedException.set(t1); + return Observable.from("twoResume", "threeResume"); + } + + }; + Observable observable = Observable.create(onErrorResumeNextViaFunction(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + assertNotNull(receivedException.get()); + } + + @Test + public void testResumeNextWithAsyncExecution() { + final AtomicReference receivedException = new AtomicReference(); + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one"); + Func1> resume = new Func1>() { + + @Override + public Observable call(Throwable t1) { + receivedException.set(t1); + return Observable.from("twoResume", "threeResume"); + } + + }; + Observable observable = Observable.create(onErrorResumeNextViaFunction(Observable.create(w), resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + w.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + assertNotNull(receivedException.get()); + } + + /** + * Test that when a function throws an exception this is propagated through onError + */ + @Test + public void testFunctionThrowsError() { + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one"); + Func1> resume = new Func1>() { + + @Override + public Observable call(Throwable t1) { + throw new RuntimeException("exception from function"); + } + + }; + Observable observable = Observable.create(onErrorResumeNextViaFunction(Observable.create(w), resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + w.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + // we should get the "one" value before the error + verify(aObserver, times(1)).onNext("one"); + + // we should have received an onError call on the Observer since the resume function threw an exception + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, times(0)).onCompleted(); + } + + private static class TestObservable implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservable(Subscription s, String... values) { + this.s = s; + this.values = values; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + throw new RuntimeException("Forced Failure"); + } catch (Throwable e) { + observer.onError(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } + + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java index b0ec9f3211..69b62ec495 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java @@ -1,7 +1,127 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Func1; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable; -@Ignore("WIP") public class OperationOnErrorResumeNextViaObservableTest { + + @Test + public void testResumeNext() { + Subscription s = mock(Subscription.class); + // Trigger failure on second element + TestObservable f = new TestObservable(s, "one", "fail", "two", "three"); + Observable w = Observable.create(f); + Observable resume = Observable.from("twoResume", "threeResume"); + Observable observable = Observable.create(onErrorResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + } + + @Test + public void testMapResumeAsyncNext() { + Subscription sr = mock(Subscription.class); + // Trigger multiple failures + Observable w = Observable.from("one", "fail", "two", "three", "fail"); + // Resume Observable is async + TestObservable f = new TestObservable(sr, "twoResume", "threeResume"); + Observable resume = Observable.create(f); + + // Introduce map function that fails intermittently (Map does not prevent this when the observer is a + // rx.operator incl onErrorResumeNextViaObservable) + w = w.map(new Func1() { + public String call(String s) { + if ("fail".equals(s)) + throw new RuntimeException("Forced Failure"); + System.out.println("BadMapper:" + s); + return s; + } + }); + + Observable observable = Observable.create(onErrorResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + } + + private static class TestObservable implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservable(Subscription s, String... values) { + this.s = s; + this.values = values; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + if ("fail".equals(s)) + throw new RuntimeException("Forced Failure"); + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + System.out.println("TestObservable onCompleted"); + observer.onCompleted(); + } catch (Throwable e) { + System.out.println("TestObservable onError: " + e); + observer.onError(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java index 0fe50a7f04..b19e60598f 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java @@ -1,7 +1,130 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Func1; + +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationOnErrorReturn.onErrorReturn; -@Ignore("WIP") public class OperationOnErrorReturnTest { + + @Test + public void testResumeNext() { + Subscription s = mock(Subscription.class); + TestObservable f = new TestObservable(s, "one"); + Observable w = Observable.create(f); + final AtomicReference capturedException = new AtomicReference(); + + Observable observable = Observable.create(onErrorReturn(w, new Func1() { + + @Override + public String call(Throwable e) { + capturedException.set(e); + return "failure"; + } + + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("failure"); + assertNotNull(capturedException.get()); + } + + /** + * Test that when a function throws an exception this is propagated through onError + */ + @Test + public void testFunctionThrowsError() { + Subscription s = mock(Subscription.class); + TestObservable f = new TestObservable(s, "one"); + Observable w = Observable.create(f); + final AtomicReference capturedException = new AtomicReference(); + + Observable observable = Observable.create(onErrorReturn(w, new Func1() { + + @Override + public String call(Throwable e) { + capturedException.set(e); + throw new RuntimeException("exception from function"); + } + + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + // we should get the "one" value before the error + verify(aObserver, times(1)).onNext("one"); + + // we should have received an onError call on the Observer since the resume function threw an exception + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, times(0)).onCompleted(); + assertNotNull(capturedException.get()); + } + + private static class TestObservable implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservable(Subscription s, String... values) { + this.s = s; + this.values = values; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + throw new RuntimeException("Forced Failure"); + } catch (Throwable e) { + observer.onError(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java index 4e8a7c1586..ee85ed7517 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java @@ -1,7 +1,224 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Func1; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationOnExceptionResumeNextViaObservable.onExceptionResumeNextViaObservable; -@Ignore("WIP") public class OperationOnExceptionResumeNextViaObservableTest { + + @Test + public void testResumeNextWithException() { + Subscription s = mock(Subscription.class); + // Trigger failure on second element + TestObservable f = new TestObservable(s, "one", "EXCEPTION", "two", "three"); + Observable w = Observable.create(f); + Observable resume = Observable.from("twoResume", "threeResume"); + Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verifyNoMoreInteractions(aObserver); + } + + @Test + public void testResumeNextWithRuntimeException() { + Subscription s = mock(Subscription.class); + // Trigger failure on second element + TestObservable f = new TestObservable(s, "one", "RUNTIMEEXCEPTION", "two", "three"); + Observable w = Observable.create(f); + Observable resume = Observable.from("twoResume", "threeResume"); + Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verifyNoMoreInteractions(aObserver); + } + + @Test + public void testThrowablePassesThru() { + Subscription s = mock(Subscription.class); + // Trigger failure on second element + TestObservable f = new TestObservable(s, "one", "THROWABLE", "two", "three"); + Observable w = Observable.create(f); + Observable resume = Observable.from("twoResume", "threeResume"); + Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onNext("twoResume"); + verify(aObserver, never()).onNext("threeResume"); + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verifyNoMoreInteractions(aObserver); + } + + @Test + public void testErrorPassesThru() { + Subscription s = mock(Subscription.class); + // Trigger failure on second element + TestObservable f = new TestObservable(s, "one", "ERROR", "two", "three"); + Observable w = Observable.create(f); + Observable resume = Observable.from("twoResume", "threeResume"); + Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onNext("twoResume"); + verify(aObserver, never()).onNext("threeResume"); + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verifyNoMoreInteractions(aObserver); + } + + @Test + public void testMapResumeAsyncNext() { + Subscription sr = mock(Subscription.class); + // Trigger multiple failures + Observable w = Observable.from("one", "fail", "two", "three", "fail"); + // Resume Observable is async + TestObservable f = new TestObservable(sr, "twoResume", "threeResume"); + Observable resume = Observable.create(f); + + // Introduce map function that fails intermittently (Map does not prevent this when the observer is a + // rx.operator incl onErrorResumeNextViaObservable) + w = w.map(new Func1() { + public String call(String s) { + if ("fail".equals(s)) + throw new RuntimeException("Forced Failure"); + System.out.println("BadMapper:" + s); + return s; + } + }); + + Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + // if the thread gets started (which it shouldn't if it's working correctly) + if (f.t != null) { + f.t.join(); + } + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + private static class TestObservable implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservable(Subscription s, String... values) { + this.s = s; + this.values = values; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + if ("EXCEPTION".equals(s)) + throw new Exception("Forced Exception"); + else if ("RUNTIMEEXCEPTION".equals(s)) + throw new RuntimeException("Forced RuntimeException"); + else if ("ERROR".equals(s)) + throw new Error("Forced Error"); + else if ("THROWABLE".equals(s)) + throw new Throwable("Forced Throwable"); + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + System.out.println("TestObservable onCompleted"); + observer.onCompleted(); + } catch (Throwable e) { + System.out.println("TestObservable onError: " + e); + observer.onError(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java b/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java index fee3816e98..fd79f9e9fb 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java @@ -1,7 +1,45 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.util.functions.Action1; +import rx.util.functions.Func1; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; -@Ignore("WIP") public class OperationParallelTest { + + @Test + public void testParallel() { + int NUM = 1000; + final AtomicInteger count = new AtomicInteger(); + Observable.range(1, NUM).parallel( + new Func1, Observable>() { + + @Override + public Observable call(Observable o) { + return o.map(new Func1() { + + @Override + public Integer[] call(Integer t) { + return new Integer[]{t, t * 99}; + } + + }); + } + }).toBlockingObservable().forEach(new Action1() { + + @Override + public void call(Integer[] v) { + count.incrementAndGet(); + System.out.println("V: " + v[0] + " R: " + v[1] + " Thread: " + Thread.currentThread()); + } + + }); + + // just making sure we finish and get the number we expect + assertEquals(NUM, count.get()); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java b/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java index 768d1f5f9c..5132b84219 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java @@ -1,7 +1,114 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.Subscriptions; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationRetry.retry; -@Ignore("WIP") public class OperationRetryTest { + + @Test + public void testOriginFails() { + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Observable origin = Observable.create(new FuncWithErrors(2)); + origin.subscribe(observer); + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext("beginningEveryTime"); + inOrder.verify(observer, times(1)).onError(any(RuntimeException.class)); + inOrder.verify(observer, never()).onNext("onSuccessOnly"); + inOrder.verify(observer, never()).onCompleted(); + } + + @Test + public void testRetryFail() { + int NUM_RETRIES = 1; + int NUM_FAILURES = 2; + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); + Observable.create(retry(origin, NUM_RETRIES)).subscribe(observer); + + InOrder inOrder = inOrder(observer); + // should show 2 attempts (first time fail, second time (1st retry) fail) + inOrder.verify(observer, times(1 + NUM_RETRIES)).onNext("beginningEveryTime"); + // should only retry once, fail again and emit onError + inOrder.verify(observer, times(1)).onError(any(RuntimeException.class)); + // no success + inOrder.verify(observer, never()).onNext("onSuccessOnly"); + inOrder.verify(observer, never()).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testRetrySuccess() { + int NUM_RETRIES = 3; + int NUM_FAILURES = 2; + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); + Observable.create(retry(origin, NUM_RETRIES)).subscribe(observer); + + InOrder inOrder = inOrder(observer); + // should show 3 attempts + inOrder.verify(observer, times(1 + NUM_FAILURES)).onNext("beginningEveryTime"); + // should have no errors + inOrder.verify(observer, never()).onError(any(Throwable.class)); + // should have a single success + inOrder.verify(observer, times(1)).onNext("onSuccessOnly"); + // should have a single successful onCompleted + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testInfiniteRetry() { + int NUM_FAILURES = 20; + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); + Observable.create(retry(origin)).subscribe(observer); + + InOrder inOrder = inOrder(observer); + // should show 3 attempts + inOrder.verify(observer, times(1 + NUM_FAILURES)).onNext("beginningEveryTime"); + // should have no errors + inOrder.verify(observer, never()).onError(any(Throwable.class)); + // should have a single success + inOrder.verify(observer, times(1)).onNext("onSuccessOnly"); + // should have a single successful onCompleted + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + public static class FuncWithErrors implements Observable.OnSubscribeFunc { + + private final int numFailures; + private final AtomicInteger count = new AtomicInteger(0); + + FuncWithErrors(int count) { + this.numFailures = count; + } + + @Override + public Subscription onSubscribe(Observer o) { + o.onNext("beginningEveryTime"); + if (count.incrementAndGet() <= numFailures) { + o.onError(new RuntimeException("forced failure: " + count.get())); + } else { + o.onNext("onSuccessOnly"); + o.onCompleted(); + } + return Subscriptions.empty(); + } + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java b/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java index a718adbc0a..789fa9cf2f 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java @@ -1,7 +1,91 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.concurrency.TestScheduler; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; + +import java.util.concurrent.TimeUnit; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; -@Ignore("WIP") public class OperationSampleTest { + private TestScheduler scheduler; + private Observer observer; + + @Before + @SuppressWarnings("unchecked") // due to mocking + public void before() { + scheduler = new TestScheduler(); + observer = mock(Observer.class); + } + + @Test + public void testSample() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(final Observer observer1) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer1.onNext(1L); + } + }, 1, TimeUnit.SECONDS); + scheduler.schedule(new Action0() { + @Override + public void call() { + observer1.onNext(2L); + } + }, 2, TimeUnit.SECONDS); + scheduler.schedule(new Action0() { + @Override + public void call() { + observer1.onCompleted(); + } + }, 3, TimeUnit.SECONDS); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSample.sample(source, 400L, TimeUnit.MILLISECONDS, scheduler)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(800L, TimeUnit.MILLISECONDS); + verify(observer, never()).onNext(any(Long.class)); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(1200L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(1L); + verify(observer, never()).onNext(2L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(1600L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(1L); + verify(observer, never()).onNext(2L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(2000L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(1L); + inOrder.verify(observer, times(1)).onNext(2L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(3000L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(1L); + inOrder.verify(observer, times(2)).onNext(2L); + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationScanTest.java b/rxjava-core/src/test/java/rx/operators/OperationScanTest.java index aa3d7d6f9e..691df4b0f1 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationScanTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationScanTest.java @@ -1,7 +1,102 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func2; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; +import static rx.operators.OperationScan.scan; -@Ignore("WIP") public class OperationScanTest { + + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testScanIntegersWithInitialValue() { + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + + Observable observable = Observable.from(1, 2, 3); + + Observable m = Observable.create(scan(observable, "", new Func2() { + + @Override + public String call(String s, Integer n) { + return s + n.toString(); + } + + })); + m.subscribe(observer); + + verify(observer, never()).onError(any(Throwable.class)); + verify(observer, times(1)).onNext(""); + verify(observer, times(1)).onNext("1"); + verify(observer, times(1)).onNext("12"); + verify(observer, times(1)).onNext("123"); + verify(observer, times(4)).onNext(anyString()); + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + + @Test + public void testScanIntegersWithoutInitialValue() { + @SuppressWarnings("unchecked") + Observer Observer = mock(Observer.class); + + Observable observable = Observable.from(1, 2, 3); + + Observable m = Observable.create(scan(observable, new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + + })); + m.subscribe(Observer); + + verify(Observer, never()).onError(any(Throwable.class)); + verify(Observer, never()).onNext(0); + verify(Observer, times(1)).onNext(1); + verify(Observer, times(1)).onNext(3); + verify(Observer, times(1)).onNext(6); + verify(Observer, times(3)).onNext(anyInt()); + verify(Observer, times(1)).onCompleted(); + verify(Observer, never()).onError(any(Throwable.class)); + } + + @Test + public void testScanIntegersWithoutInitialValueAndOnlyOneValue() { + @SuppressWarnings("unchecked") + Observer Observer = mock(Observer.class); + + Observable observable = Observable.from(1); + + Observable m = Observable.create(scan(observable, new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + + })); + m.subscribe(Observer); + + verify(Observer, never()).onError(any(Throwable.class)); + verify(Observer, never()).onNext(0); + verify(Observer, times(1)).onNext(1); + verify(Observer, times(1)).onNext(anyInt()); + verify(Observer, times(1)).onCompleted(); + verify(Observer, never()).onError(any(Throwable.class)); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java index 9604c718c7..8b1dd163fb 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java @@ -1,7 +1,98 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationSkipLast.skipLast; -@Ignore("WIP") public class OperationSkipLastTest { + + @Test + public void testSkipLastEmpty() { + Observable w = Observable.empty(); + Observable observable = Observable.create(skipLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLast1() { + Observable w = Observable.from("one", "two", "three"); + Observable observable = Observable.create(skipLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + InOrder inOrder = inOrder(aObserver); + observable.subscribe(aObserver); + inOrder.verify(aObserver, never()).onNext("two"); + inOrder.verify(aObserver, never()).onNext("three"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLast2() { + Observable w = Observable.from("one", "two"); + Observable observable = Observable.create(skipLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLastWithZeroCount() { + Observable w = Observable.from("one", "two"); + Observable observable = Observable.create(skipLast(w, 0)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLastWithNull() { + Observable w = Observable.from("one", null, "two"); + Observable observable = Observable.create(skipLast(w, 1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext(null); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLastWithNegativeCount() { + Observable w = Observable.from("one"); + Observable observable = Observable.create(skipLast(w, -1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, times(1)).onError( + any(IndexOutOfBoundsException.class)); + verify(aObserver, never()).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java index 1d74ed6302..acc351de34 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java @@ -1,7 +1,42 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationSkip.skip; -@Ignore("WIP") public class OperationSkipTest { + + @Test + public void testSkip1() { + Observable w = Observable.from("one", "two", "three"); + Observable skip = Observable.create(skip(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + skip.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkip2() { + Observable w = Observable.from("one", "two", "three"); + Observable skip = Observable.create(skip(w, 1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + skip.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java index d9347d907e..f0d8391928 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java @@ -1,7 +1,104 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.*; +import static rx.Observable.create; +import static rx.operators.OperationSkipWhile.skipWhile; +import static rx.operators.OperationSkipWhile.skipWhileWithIndex; -@Ignore("WIP") public class OperationSkipWhileTest { + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + + private static final Func1 LESS_THAN_FIVE = new Func1() { + @Override + public Boolean call(Integer v) { + if (v == 42) throw new RuntimeException("that's not the answer to everything!"); + return v < 5; + } + }; + + private static final Func2 INDEX_LESS_THAN_THREE = new Func2() { + @Override + public Boolean call(Integer value, Integer index) { + return index < 3; + } + }; + + @Test + public void testSkipWithIndex() { + Observable src = Observable.from(1, 2, 3, 4, 5); + create(skipWhileWithIndex(src, INDEX_LESS_THAN_THREE)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext(4); + inOrder.verify(w, times(1)).onNext(5); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testSkipEmpty() { + Observable src = Observable.empty(); + create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + verify(w, never()).onNext(anyInt()); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testSkipEverything() { + Observable src = Observable.from(1, 2, 3, 4, 3, 2, 1); + create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + verify(w, never()).onNext(anyInt()); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testSkipNothing() { + Observable src = Observable.from(5, 3, 1); + create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext(5); + inOrder.verify(w, times(1)).onNext(3); + inOrder.verify(w, times(1)).onNext(1); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testSkipSome() { + Observable src = Observable.from(1, 2, 3, 4, 5, 3, 1, 5); + create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext(5); + inOrder.verify(w, times(1)).onNext(3); + inOrder.verify(w, times(1)).onNext(1); + inOrder.verify(w, times(1)).onNext(5); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testSkipError() { + Observable src = Observable.from(1, 2, 42, 5, 3, 1); + create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, never()).onNext(anyInt()); + inOrder.verify(w, never()).onCompleted(); + inOrder.verify(w, times(1)).onError(any(RuntimeException.class)); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java b/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java index 495cbc6713..0c260cf8e8 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java @@ -1,7 +1,40 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; +import rx.Scheduler; +import rx.Subscription; +import rx.concurrency.Schedulers; +import rx.test.OperatorTester; +import rx.util.functions.Action0; +import rx.util.functions.Func2; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.*; +import static rx.operators.OperationSubscribeOn.subscribeOn; -@Ignore("WIP") public class OperationSubscribeOnTest { + + @Test + @SuppressWarnings("unchecked") + public void testSubscribeOn() { + Observable w = Observable.from(1, 2, 3); + + Scheduler scheduler = spy(OperatorTester.forwardingScheduler(Schedulers.immediate())); + + Observer observer = mock(Observer.class); + Subscription subscription = Observable.create(subscribeOn(w, scheduler)).subscribe(observer); + + verify(scheduler, times(1)).schedule(isNull(), any(Func2.class)); + subscription.unsubscribe(); + verify(scheduler, times(1)).schedule(any(Action0.class)); + verifyNoMoreInteractions(scheduler); + + verify(observer, times(1)).onNext(1); + verify(observer, times(1)).onNext(2); + verify(observer, times(1)).onNext(3); + verify(observer, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSumTest.java b/rxjava-core/src/test/java/rx/operators/OperationSumTest.java index c89121e207..9e826d8343 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSumTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSumTest.java @@ -1,7 +1,113 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.anyFloat; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.*; +import static rx.operators.OperationSum.*; -@Ignore("WIP") public class OperationSumTest { + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer wl = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer wf = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer wd = mock(Observer.class); + + @Test + public void testSumOfAFewInts() throws Throwable { + Observable src = Observable.from(1, 2, 3, 4, 5); + sum(src).subscribe(w); + + verify(w, times(1)).onNext(anyInt()); + verify(w).onNext(15); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testEmptySum() throws Throwable { + Observable src = Observable.empty(); + sum(src).subscribe(w); + + 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 testSumOfAFewLongs() throws Throwable { + Observable src = Observable.from(1L, 2L, 3L, 4L, 5L); + sumLongs(src).subscribe(wl); + + verify(wl, times(1)).onNext(anyLong()); + verify(wl).onNext(15L); + verify(wl, never()).onError(any(Throwable.class)); + verify(wl, times(1)).onCompleted(); + } + + @Test + public void testEmptySumLongs() throws Throwable { + Observable src = Observable.empty(); + sumLongs(src).subscribe(wl); + + verify(wl, times(1)).onNext(anyLong()); + verify(wl).onNext(0L); + verify(wl, never()).onError(any(Throwable.class)); + verify(wl, times(1)).onCompleted(); + } + + @Test + public void testSumOfAFewFloats() throws Throwable { + Observable src = Observable.from(1.0f); + sumFloats(src).subscribe(wf); + + verify(wf, times(1)).onNext(anyFloat()); + verify(wf).onNext(1.0f); + verify(wf, never()).onError(any(Throwable.class)); + verify(wf, times(1)).onCompleted(); + } + + @Test + public void testEmptySumFloats() throws Throwable { + Observable src = Observable.empty(); + sumFloats(src).subscribe(wf); + + verify(wf, times(1)).onNext(anyFloat()); + verify(wf).onNext(0.0f); + verify(wf, never()).onError(any(Throwable.class)); + verify(wf, times(1)).onCompleted(); + } + + @Test + public void testSumOfAFewDoubles() throws Throwable { + Observable src = Observable.from(0.0d, 1.0d, 0.5d); + sumDoubles(src).subscribe(wd); + + verify(wd, times(1)).onNext(anyDouble()); + verify(wd).onNext(1.5d); + verify(wd, never()).onError(any(Throwable.class)); + verify(wd, times(1)).onCompleted(); + } + + @Test + public void testEmptySumDoubles() throws Throwable { + Observable src = Observable.empty(); + sumDoubles(src).subscribe(wd); + + verify(wd, times(1)).onNext(anyDouble()); + verify(wd).onNext(0.0d); + verify(wd, never()).onError(any(Throwable.class)); + verify(wd, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java b/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java index d91bb804bf..7c8c9ea21a 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java @@ -1,7 +1,368 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.concurrency.TestScheduler; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; + +import java.util.concurrent.TimeUnit; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; -@Ignore("WIP") public class OperationSwitchTest { + + private TestScheduler scheduler; + private Observer observer; + + @Before + @SuppressWarnings("unchecked") + public void before() { + scheduler = new TestScheduler(); + observer = mock(Observer.class); + } + + @Test + public void testSwitchWhenOuterCompleteBeforeInner() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 70, "one"); + publishNext(observer, 100, "two"); + publishCompleted(observer, 200); + return Subscriptions.empty(); + } + })); + publishCompleted(observer, 60); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(2)).onNext(anyString()); + inOrder.verify(observer, times(1)).onCompleted(); + } + + @Test + public void testSwitchWhenInnerCompleteBeforeOuter() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 10, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 0, "one"); + publishNext(observer, 10, "two"); + publishCompleted(observer, 20); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 100, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 0, "three"); + publishNext(observer, 10, "four"); + publishCompleted(observer, 20); + return Subscriptions.empty(); + } + })); + publishCompleted(observer, 200); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(150, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onCompleted(); + 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"); + + scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + inOrder.verify(observer, times(1)).onCompleted(); + } + + @Test + public void testSwitchWithComplete() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 60, "one"); + publishNext(observer, 100, "two"); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 200, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 0, "three"); + publishNext(observer, 100, "four"); + return Subscriptions.empty(); + } + })); + + publishCompleted(observer, 250); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(175, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("two"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(225, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("three"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("four"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + + @Test + public void testSwitchWithError() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 50, "one"); + publishNext(observer, 100, "two"); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 200, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 0, "three"); + publishNext(observer, 100, "four"); + return Subscriptions.empty(); + } + })); + + publishError(observer, 250, new TestException()); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(175, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("two"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(225, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("three"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + verify(observer, never()).onCompleted(); + verify(observer, times(1)).onError(any(TestException.class)); + } + + @Test + public void testSwitchWithSubsequenceComplete() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 50, "one"); + publishNext(observer, 100, "two"); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 130, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishCompleted(observer, 0); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 150, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 50, "three"); + return Subscriptions.empty(); + } + })); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("three"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + + @Test + public void testSwitchWithSubsequenceError() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 50, "one"); + publishNext(observer, 100, "two"); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 130, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishError(observer, 0, new TestException()); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 150, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 50, "three"); + return Subscriptions.empty(); + } + })); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext("three"); + verify(observer, never()).onCompleted(); + verify(observer, times(1)).onError(any(TestException.class)); + } + + private void publishCompleted(final Observer observer, long delay) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onCompleted(); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void publishError(final Observer observer, long delay, final Throwable error) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onError(error); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void publishNext(final Observer observer, long delay, final T value) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onNext(value); + } + }, delay, TimeUnit.MILLISECONDS); + } + + @SuppressWarnings("serial") + private class TestException extends Throwable { + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java b/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java index 2dce4a7d32..28d7676c66 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java @@ -1,7 +1,203 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import rx.Observable; +import rx.Observer; +import rx.Subscription; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationSynchronize.synchronize; -@Ignore("WIP") public class OperationSynchronizeTest { + + /** + * Ensure onCompleted can not be called after an Unsubscribe + */ + @Test + public void testOnCompletedAfterUnSubscribe() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + ws.unsubscribe(); + t.sendOnCompleted(); + + verify(w, times(1)).onNext("one"); + verify(w, Mockito.never()).onCompleted(); + } + + /** + * Ensure onNext can not be called after an Unsubscribe + */ + @Test + public void testOnNextAfterUnSubscribe() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + ws.unsubscribe(); + t.sendOnNext("two"); + + verify(w, times(1)).onNext("one"); + verify(w, Mockito.never()).onNext("two"); + } + + /** + * Ensure onError can not be called after an Unsubscribe + */ + @Test + public void testOnErrorAfterUnSubscribe() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + ws.unsubscribe(); + t.sendOnError(new RuntimeException("bad")); + + verify(w, times(1)).onNext("one"); + verify(w, Mockito.never()).onError(any(Throwable.class)); + } + + /** + * Ensure onNext can not be called after onError + */ + @Test + public void testOnNextAfterOnError() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + @SuppressWarnings("unused") + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + t.sendOnError(new RuntimeException("bad")); + t.sendOnNext("two"); + + verify(w, times(1)).onNext("one"); + verify(w, times(1)).onError(any(Throwable.class)); + verify(w, Mockito.never()).onNext("two"); + } + + /** + * Ensure onCompleted can not be called after onError + */ + @Test + public void testOnCompletedAfterOnError() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + @SuppressWarnings("unused") + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + t.sendOnError(new RuntimeException("bad")); + t.sendOnCompleted(); + + verify(w, times(1)).onNext("one"); + verify(w, times(1)).onError(any(Throwable.class)); + verify(w, Mockito.never()).onCompleted(); + } + + /** + * Ensure onNext can not be called after onCompleted + */ + @Test + public void testOnNextAfterOnCompleted() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + @SuppressWarnings("unused") + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + t.sendOnCompleted(); + t.sendOnNext("two"); + + verify(w, times(1)).onNext("one"); + verify(w, Mockito.never()).onNext("two"); + verify(w, times(1)).onCompleted(); + verify(w, Mockito.never()).onError(any(Throwable.class)); + } + + /** + * Ensure onError can not be called after onCompleted + */ + @Test + public void testOnErrorAfterOnCompleted() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + @SuppressWarnings("unused") + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + t.sendOnCompleted(); + t.sendOnError(new RuntimeException("bad")); + + verify(w, times(1)).onNext("one"); + verify(w, times(1)).onCompleted(); + verify(w, Mockito.never()).onError(any(Throwable.class)); + } + + /** + * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. + */ + private static class TestObservable implements Observable.OnSubscribeFunc { + + Observer observer = null; + + public TestObservable(Subscription s) { + } + + /* used to simulate subscription */ + public void sendOnCompleted() { + observer.onCompleted(); + } + + /* used to simulate subscription */ + public void sendOnNext(String value) { + observer.onNext(value); + } + + /* used to simulate subscription */ + public void sendOnError(Throwable e) { + observer.onError(e); + } + + @Override + public Subscription onSubscribe(final Observer observer) { + this.observer = observer; + return new Subscription() { + + @Override + public void unsubscribe() { + // going to do nothing to pretend I'm a bad Observable that keeps allowing events to be sent + } + + }; + } + + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java index 234e70d543..89fc932b51 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java @@ -1,7 +1,97 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationTakeLast.takeLast; -@Ignore("WIP") public class OperationTakeLastTest { + + @Test + public void testTakeLastEmpty() { + Observable w = Observable.empty(); + Observable take = Observable.create(takeLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeLast1() { + Observable w = Observable.from("one", "two", "three"); + Observable take = Observable.create(takeLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + InOrder inOrder = inOrder(aObserver); + take.subscribe(aObserver); + inOrder.verify(aObserver, times(1)).onNext("two"); + inOrder.verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onNext("one"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeLast2() { + Observable w = Observable.from("one"); + Observable take = Observable.create(takeLast(w, 10)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeLastWithZeroCount() { + Observable w = Observable.from("one"); + Observable take = Observable.create(takeLast(w, 0)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeLastWithNull() { + Observable w = Observable.from("one", null, "three"); + Observable take = Observable.create(takeLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, times(1)).onNext(null); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeLastWithNegativeCount() { + Observable w = Observable.from("one"); + Observable take = Observable.create(takeLast(w, -1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, times(1)).onError( + any(IndexOutOfBoundsException.class)); + verify(aObserver, never()).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java index eaa1bdf1d4..b02c4925cd 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java @@ -1,7 +1,213 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Func1; + +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; +import static rx.operators.OperationTake.take; -@Ignore("WIP") public class OperationTakeTest { + + @Test + public void testTake1() { + Observable w = Observable.from("one", "two", "three"); + Observable take = Observable.create(take(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTake2() { + Observable w = Observable.from("one", "two", "three"); + Observable take = Observable.create(take(w, 1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test(expected = IllegalArgumentException.class) + public void testTakeWithError() { + Observable.from(1, 2, 3).take(1).map(new Func1() { + public Integer call(Integer t1) { + throw new IllegalArgumentException("some error"); + } + }).toBlockingObservable().single(); + } + + @Test + public void testTakeWithErrorHappeningInOnNext() { + Observable w = Observable.from(1, 2, 3).take(2).map(new Func1() { + public Integer call(Integer t1) { + throw new IllegalArgumentException("some error"); + } + }); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + w.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError(any(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testTakeWithErrorHappeningInTheLastOnNext() { + Observable w = Observable.from(1, 2, 3).take(1).map(new Func1() { + public Integer call(Integer t1) { + throw new IllegalArgumentException("some error"); + } + }); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + w.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError(any(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testTakeDoesntLeakErrors() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onError(new Throwable("test failed")); + return Subscriptions.empty(); + } + }); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + + Observable.create(take(source, 1)).subscribe(aObserver); + + verify(aObserver, times(1)).onNext("one"); + // even though onError is called we take(1) so shouldn't see it + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verifyNoMoreInteractions(aObserver); + } + + @Test + public void testTakeZeroDoesntLeakError() { + final AtomicBoolean subscribed = new AtomicBoolean(false); + final AtomicBoolean unSubscribed = new AtomicBoolean(false); + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + subscribed.set(true); + observer.onError(new Throwable("test failed")); + return new Subscription() { + @Override + public void unsubscribe() { + unSubscribed.set(true); + } + }; + } + }); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + + Observable.create(take(source, 0)).subscribe(aObserver); + assertTrue("source subscribed", subscribed.get()); + assertTrue("source unsubscribed", unSubscribed.get()); + + verify(aObserver, never()).onNext(anyString()); + // even though onError is called we take(0) so shouldn't see it + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verifyNoMoreInteractions(aObserver); + } + + @Test + public void testUnsubscribeAfterTake() { + final Subscription s = mock(Subscription.class); + TestObservableFunc f = new TestObservableFunc(s, "one", "two", "three"); + Observable w = Observable.create(f); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Observable take = Observable.create(take(w, 1)); + take.subscribe(aObserver); + + // wait for the Observable to complete + try { + f.t.join(); + } catch (Throwable e) { + e.printStackTrace(); + fail(e.getMessage()); + } + + System.out.println("TestObservable thread finished"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, times(1)).onCompleted(); + verify(s, times(1)).unsubscribe(); + verifyNoMoreInteractions(aObserver); + } + + private static class TestObservableFunc implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservableFunc(Subscription s, String... values) { + this.s = s; + this.values = values; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + observer.onCompleted(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java index 22cd5eb078..0e46af91e8 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java @@ -1,7 +1,164 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observable; +import rx.Observer; +import rx.Subscription; + +import static org.mockito.Mockito.*; +import static rx.operators.OperationTakeUntil.takeUntil; -@Ignore("WIP") public class OperationTakeUntilTest { + + @Test + @SuppressWarnings("unchecked") + public void testTakeUntil() { + Subscription sSource = mock(Subscription.class); + Subscription sOther = mock(Subscription.class); + TestObservable source = new TestObservable(sSource); + TestObservable other = new TestObservable(sOther); + + Observer result = mock(Observer.class); + Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); + stringObservable.subscribe(result); + source.sendOnNext("one"); + source.sendOnNext("two"); + other.sendOnNext("three"); + source.sendOnNext("four"); + source.sendOnCompleted(); + other.sendOnCompleted(); + + verify(result, times(1)).onNext("one"); + verify(result, times(1)).onNext("two"); + verify(result, times(0)).onNext("three"); + verify(result, times(0)).onNext("four"); + verify(sSource, times(1)).unsubscribe(); + verify(sOther, times(1)).unsubscribe(); + + } + + @Test + @SuppressWarnings("unchecked") + public void testTakeUntilSourceCompleted() { + Subscription sSource = mock(Subscription.class); + Subscription sOther = mock(Subscription.class); + TestObservable source = new TestObservable(sSource); + TestObservable other = new TestObservable(sOther); + + Observer result = mock(Observer.class); + Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); + stringObservable.subscribe(result); + source.sendOnNext("one"); + source.sendOnNext("two"); + source.sendOnCompleted(); + + verify(result, times(1)).onNext("one"); + verify(result, times(1)).onNext("two"); + verify(sSource, times(1)).unsubscribe(); + verify(sOther, times(1)).unsubscribe(); + + } + + @Test + @SuppressWarnings("unchecked") + public void testTakeUntilSourceError() { + Subscription sSource = mock(Subscription.class); + Subscription sOther = mock(Subscription.class); + TestObservable source = new TestObservable(sSource); + TestObservable other = new TestObservable(sOther); + Throwable error = new Throwable(); + + Observer result = mock(Observer.class); + Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); + stringObservable.subscribe(result); + source.sendOnNext("one"); + source.sendOnNext("two"); + source.sendOnError(error); + + verify(result, times(1)).onNext("one"); + verify(result, times(1)).onNext("two"); + verify(result, times(1)).onError(error); + verify(sSource, times(1)).unsubscribe(); + verify(sOther, times(1)).unsubscribe(); + + } + + @Test + @SuppressWarnings("unchecked") + public void testTakeUntilOtherError() { + Subscription sSource = mock(Subscription.class); + Subscription sOther = mock(Subscription.class); + TestObservable source = new TestObservable(sSource); + TestObservable other = new TestObservable(sOther); + Throwable error = new Throwable(); + + Observer result = mock(Observer.class); + Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); + stringObservable.subscribe(result); + source.sendOnNext("one"); + source.sendOnNext("two"); + other.sendOnError(error); + + verify(result, times(1)).onNext("one"); + verify(result, times(1)).onNext("two"); + verify(result, times(1)).onError(error); + verify(result, times(0)).onCompleted(); + verify(sSource, times(1)).unsubscribe(); + verify(sOther, times(1)).unsubscribe(); + + } + + @Test + @SuppressWarnings("unchecked") + public void testTakeUntilOtherCompleted() { + Subscription sSource = mock(Subscription.class); + Subscription sOther = mock(Subscription.class); + TestObservable source = new TestObservable(sSource); + TestObservable other = new TestObservable(sOther); + + Observer result = mock(Observer.class); + Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); + stringObservable.subscribe(result); + source.sendOnNext("one"); + source.sendOnNext("two"); + other.sendOnCompleted(); + + verify(result, times(1)).onNext("one"); + verify(result, times(1)).onNext("two"); + verify(result, times(0)).onCompleted(); + verify(sSource, times(0)).unsubscribe(); + verify(sOther, times(0)).unsubscribe(); + + } + + private static class TestObservable implements Observable.OnSubscribeFunc { + + Observer observer = null; + Subscription s; + + public TestObservable(Subscription s) { + this.s = s; + } + + /* used to simulate subscription */ + public void sendOnCompleted() { + observer.onCompleted(); + } + + /* used to simulate subscription */ + public void sendOnNext(String value) { + observer.onNext(value); + } + + /* used to simulate subscription */ + public void sendOnError(Throwable e) { + observer.onError(e); + } + + @Override + public Subscription onSubscribe(final Observer observer) { + this.observer = observer; + return s; + } + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java new file mode 100644 index 0000000000..8109c818e0 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java @@ -0,0 +1,204 @@ +package rx.operators; + +import org.junit.Test; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.subjects.PublishSubject; +import rx.subjects.Subject; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationTakeWhile.takeWhile; +import static rx.operators.OperationTakeWhile.takeWhileWithIndex; + +public class OperationTakeWhileTest { + + @Test + public void testTakeWhile1() { + Observable w = Observable.from(1, 2, 3); + Observable take = Observable.create(takeWhile(w, new Func1() { + @Override + public Boolean call(Integer input) { + return input < 3; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext(1); + verify(aObserver, times(1)).onNext(2); + verify(aObserver, never()).onNext(3); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeWhileOnSubject1() { + Subject s = PublishSubject.create(); + Observable take = Observable.create(takeWhile(s, new Func1() { + @Override + public Boolean call(Integer input) { + return input < 3; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + + s.onNext(1); + s.onNext(2); + s.onNext(3); + s.onNext(4); + s.onNext(5); + s.onCompleted(); + + verify(aObserver, times(1)).onNext(1); + verify(aObserver, times(1)).onNext(2); + verify(aObserver, never()).onNext(3); + verify(aObserver, never()).onNext(4); + verify(aObserver, never()).onNext(5); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeWhile2() { + Observable w = Observable.from("one", "two", "three"); + Observable take = Observable.create(takeWhileWithIndex(w, new Func2() { + @Override + public Boolean call(String input, Integer index) { + return index < 2; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeWhileDoesntLeakErrors() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onError(new Throwable("test failed")); + return Subscriptions.empty(); + } + }); + + Observable.create(takeWhile(source, new Func1() { + @Override + public Boolean call(String s) { + return false; + } + })).toBlockingObservable().last(); + } + + @Test + public void testTakeWhileProtectsPredicateCall() { + TestObservable source = new TestObservable(mock(Subscription.class), "one"); + final RuntimeException testException = new RuntimeException("test exception"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Observable take = Observable.create(takeWhile(Observable.create(source), new Func1() { + @Override + public Boolean call(String s) { + throw testException; + } + })); + take.subscribe(aObserver); + + // wait for the Observable to complete + try { + source.t.join(); + } catch (Throwable e) { + e.printStackTrace(); + fail(e.getMessage()); + } + + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, times(1)).onError(testException); + } + + @Test + public void testUnsubscribeAfterTake() { + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one", "two", "three"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Observable take = Observable.create(takeWhileWithIndex(Observable.create(w), new Func2() { + @Override + public Boolean call(String s, Integer index) { + return index < 1; + } + })); + take.subscribe(aObserver); + + // wait for the Observable to complete + try { + w.t.join(); + } catch (Throwable e) { + e.printStackTrace(); + fail(e.getMessage()); + } + + System.out.println("TestObservable thread finished"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(s, times(1)).unsubscribe(); + } + + private static class TestObservable implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservable(Subscription s, String... values) { + this.s = s; + this.values = values; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + observer.onCompleted(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } + } +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java b/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java index 66301abfca..76cac5dfe9 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java @@ -1,7 +1,114 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.concurrency.TestScheduler; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; + +import java.util.concurrent.TimeUnit; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; -@Ignore("WIP") public class OperationThrottleFirstTest { + + private TestScheduler scheduler; + private Observer observer; + + @Before + @SuppressWarnings("unchecked") + public void before() { + scheduler = new TestScheduler(); + observer = mock(Observer.class); + } + + @Test + public void testThrottlingWithCompleted() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 100, "one"); // publish as it's first + publishNext(observer, 300, "two"); // skip as it's last within the first 400 + publishNext(observer, 900, "three"); // publish + publishNext(observer, 905, "four"); // skip + publishCompleted(observer, 1000); // Should be published as soon as the timeout expires. + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationThrottleFirst.throttleFirst(source, 400, TimeUnit.MILLISECONDS, scheduler)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + inOrder.verify(observer, times(0)).onNext("two"); + inOrder.verify(observer, times(1)).onNext("three"); + inOrder.verify(observer, times(0)).onNext("four"); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThrottlingWithError() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + Exception error = new TestException(); + publishNext(observer, 100, "one"); // Should be published since it is first + publishNext(observer, 200, "two"); // Should be skipped since onError will arrive before the timeout expires + publishError(observer, 300, error); // Should be published as soon as the timeout expires. + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationThrottleFirst.throttleFirst(source, 400, TimeUnit.MILLISECONDS, scheduler)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(400, TimeUnit.MILLISECONDS); + inOrder.verify(observer).onNext("one"); + inOrder.verify(observer).onError(any(TestException.class)); + inOrder.verifyNoMoreInteractions(); + } + + private void publishCompleted(final Observer observer, long delay) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onCompleted(); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void publishError(final Observer observer, long delay, final Exception error) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onError(error); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void publishNext(final Observer observer, long delay, final T value) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onNext(value); + } + }, delay, TimeUnit.MILLISECONDS); + } + + @SuppressWarnings("serial") + private class TestException extends Exception { + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java b/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java new file mode 100644 index 0000000000..6ce6504b05 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java @@ -0,0 +1,60 @@ +package rx.operators; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import rx.Observable; +import rx.Observer; +import rx.concurrency.TestScheduler; +import rx.subjects.PublishSubject; +import rx.util.TimeInterval; + +import java.util.concurrent.TimeUnit; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.times; + +public class OperationTimeIntervalTest { + + private static final TimeUnit TIME_UNIT = TimeUnit.MILLISECONDS; + + @Mock + private Observer> observer; + + private TestScheduler testScheduler; + private PublishSubject subject; + private Observable> observable; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + testScheduler = new TestScheduler(); + subject = PublishSubject.create(); + observable = subject.timeInterval(testScheduler); + } + + @Test + public void testTimeInterval() { + InOrder inOrder = inOrder(observer); + observable.subscribe(observer); + + testScheduler.advanceTimeBy(1000, TIME_UNIT); + subject.onNext(1); + testScheduler.advanceTimeBy(2000, TIME_UNIT); + subject.onNext(2); + testScheduler.advanceTimeBy(3000, TIME_UNIT); + subject.onNext(3); + subject.onCompleted(); + + inOrder.verify(observer, times(1)).onNext( + new TimeInterval(1000, 1)); + inOrder.verify(observer, times(1)).onNext( + new TimeInterval(2000, 2)); + inOrder.verify(observer, times(1)).onNext( + new TimeInterval(3000, 3)); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java index 3f58b63911..f3891128e1 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java @@ -1,7 +1,47 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import rx.Observer; +import rx.Subscription; + +import java.util.concurrent.Future; + +import static org.mockito.Mockito.*; +import static rx.operators.OperationToObservableFuture.ToObservableFuture; -@Ignore("WIP") public class OperationToObservableFutureTest { + + @Test + public void testSuccess() throws Exception { + Future future = mock(Future.class); + Object value = new Object(); + when(future.get()).thenReturn(value); + ToObservableFuture ob = new ToObservableFuture(future); + Observer o = mock(Observer.class); + + Subscription sub = ob.onSubscribe(o); + sub.unsubscribe(); + + verify(o, times(1)).onNext(value); + verify(o, times(1)).onCompleted(); + verify(o, never()).onError(null); + verify(future, never()).cancel(true); + } + + @Test + public void testFailure() throws Exception { + Future future = mock(Future.class); + RuntimeException e = new RuntimeException(); + when(future.get()).thenThrow(e); + ToObservableFuture ob = new ToObservableFuture(future); + Observer o = mock(Observer.class); + + Subscription sub = ob.onSubscribe(o); + sub.unsubscribe(); + + verify(o, never()).onNext(null); + verify(o, never()).onCompleted(); + verify(o, times(1)).onError(e); + verify(future, never()).cancel(true); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java index 1805e1cbcd..a24cb92694 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java @@ -1,7 +1,32 @@ package rx.operators; import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import rx.Observable; +import rx.Observer; + +import java.util.Arrays; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static rx.operators.OperationToObservableIterable.toObservableIterable; -@Ignore("WIP") public class OperationToObservableIterableTest { + + @Test + public void testIterable() { + Observable observable = Observable.create(toObservableIterable(Arrays. asList("one", "two", "three"))); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java index 49c7409e86..12e47aa080 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java @@ -1,7 +1,53 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import rx.Observable; +import rx.Observer; + +import java.util.Arrays; +import java.util.List; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationToObservableList.toObservableList; -@Ignore("WIP") public class OperationToObservableListTest { + + @Test + public void testList() { + Observable w = Observable.from("one", "two", "three"); + Observable> observable = Observable.create(toObservableList(w)); + + @SuppressWarnings("unchecked") + Observer> aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(Arrays.asList("one", "two", "three")); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testListMultipleObservers() { + Observable w = Observable.from("one", "two", "three"); + Observable> observable = Observable.create(toObservableList(w)); + + @SuppressWarnings("unchecked") + Observer> o1 = mock(Observer.class); + observable.subscribe(o1); + + @SuppressWarnings("unchecked") + Observer> o2 = mock(Observer.class); + observable.subscribe(o2); + + List expected = Arrays.asList("one", "two", "three"); + + verify(o1, times(1)).onNext(expected); + verify(o1, Mockito.never()).onError(any(Throwable.class)); + verify(o1, times(1)).onCompleted(); + + verify(o2, times(1)).onNext(expected); + verify(o2, Mockito.never()).onError(any(Throwable.class)); + verify(o2, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java index 62efef680d..11b6c9ff6a 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java @@ -1,7 +1,50 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func2; + +import java.util.Arrays; +import java.util.List; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; +import static rx.operators.OperationToObservableSortedList.toSortedList; -@Ignore("WIP") public class OperationToObservableSortedListTest { + + @Test + public void testSortedList() { + Observable w = Observable.from(1, 3, 2, 5, 4); + Observable> observable = Observable.create(toSortedList(w)); + + @SuppressWarnings("unchecked") + Observer> aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(Arrays.asList(1, 2, 3, 4, 5)); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSortedListWithCustomFunction() { + Observable w = Observable.from(1, 3, 2, 5, 4); + Observable> observable = Observable.create(toSortedList(w, new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t2 - t1; + } + + })); + + @SuppressWarnings("unchecked") + Observer> aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(Arrays.asList(5, 4, 3, 2, 1)); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java b/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java index 0e602254f5..8a552f534f 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java @@ -1,7 +1,315 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.concurrency.TestScheduler; +import rx.subscriptions.Subscriptions; +import rx.util.Closing; +import rx.util.Closings; +import rx.util.Opening; +import rx.util.Openings; +import rx.util.functions.Action0; +import rx.util.functions.Action1; +import rx.util.functions.Func0; +import rx.util.functions.Func1; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static rx.operators.OperationWindow.window; -@Ignore("WIP") public class OperationWindowTest { + + private TestScheduler scheduler; + + @Before + public void before() { + scheduler = new TestScheduler(); + } + + private static List> toLists(Observable> observable) { + final List list = new ArrayList(); + final List> lists = new ArrayList>(); + + observable.subscribe(new Action1>() { + @Override + public void call(Observable tObservable) { + tObservable.subscribe(new Action1() { + @Override + public void call(T t) { + list.add(t); + } + }); + lists.add(new ArrayList(list)); + list.clear(); + } + }); + return lists; + } + + @Test + public void testNonOverlappingWindows() { + Observable subject = Observable.from("one", "two", "three", "four", "five"); + Observable> windowed = Observable.create(window(subject, 3)); + + List> windows = toLists(windowed); + + assertEquals(2, windows.size()); + assertEquals(list("one", "two", "three"), windows.get(0)); + assertEquals(list("four", "five"), windows.get(1)); + } + + @Test + public void testSkipAndCountGaplessEindows() { + Observable subject = Observable.from("one", "two", "three", "four", "five"); + Observable> windowed = Observable.create(window(subject, 3, 3)); + + List> windows = toLists(windowed); + + assertEquals(2, windows.size()); + assertEquals(list("one", "two", "three"), windows.get(0)); + assertEquals(list("four", "five"), windows.get(1)); + } + + @Test + public void testOverlappingWindows() { + Observable subject = Observable.from("zero", "one", "two", "three", "four", "five"); + Observable> windowed = Observable.create(window(subject, 3, 1)); + + List> windows = toLists(windowed); + + assertEquals(6, windows.size()); + assertEquals(list("zero", "one", "two"), windows.get(0)); + assertEquals(list("one", "two", "three"), windows.get(1)); + assertEquals(list("two", "three", "four"), windows.get(2)); + assertEquals(list("three", "four", "five"), windows.get(3)); + assertEquals(list("four", "five"), windows.get(4)); + assertEquals(list("five"), windows.get(5)); + } + + @Test + public void testSkipAndCountWindowsWithGaps() { + Observable subject = Observable.from("one", "two", "three", "four", "five"); + Observable> windowed = Observable.create(window(subject, 2, 3)); + + List> windows = toLists(windowed); + + assertEquals(2, windows.size()); + assertEquals(list("one", "two"), windows.get(0)); + assertEquals(list("four", "five"), windows.get(1)); + } + + @Test + public void testTimedAndCount() { + final List list = new ArrayList(); + final List> lists = new ArrayList>(); + + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 10); + push(observer, "two", 90); + push(observer, "three", 110); + push(observer, "four", 190); + push(observer, "five", 210); + complete(observer, 250); + return Subscriptions.empty(); + } + }); + + Observable> windowed = Observable.create(window(source, 100, TimeUnit.MILLISECONDS, 2, scheduler)); + windowed.subscribe(observeWindow(list, lists)); + + scheduler.advanceTimeTo(100, TimeUnit.MILLISECONDS); + assertEquals(1, lists.size()); + assertEquals(lists.get(0), list("one", "two")); + + scheduler.advanceTimeTo(200, TimeUnit.MILLISECONDS); + assertEquals(2, lists.size()); + assertEquals(lists.get(1), list("three", "four")); + + scheduler.advanceTimeTo(300, TimeUnit.MILLISECONDS); + assertEquals(3, lists.size()); + assertEquals(lists.get(2), list("five")); + } + + @Test + public void testTimed() { + final List list = new ArrayList(); + final List> lists = new ArrayList>(); + + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 98); + push(observer, "two", 99); + push(observer, "three", 100); + push(observer, "four", 101); + push(observer, "five", 102); + complete(observer, 150); + return Subscriptions.empty(); + } + }); + + Observable> windowed = Observable.create(window(source, 100, TimeUnit.MILLISECONDS, scheduler)); + windowed.subscribe(observeWindow(list, lists)); + + scheduler.advanceTimeTo(101, TimeUnit.MILLISECONDS); + assertEquals(1, lists.size()); + assertEquals(lists.get(0), list("one", "two", "three")); + + scheduler.advanceTimeTo(201, TimeUnit.MILLISECONDS); + assertEquals(2, lists.size()); + assertEquals(lists.get(1), list("four", "five")); + } + + @Test + public void testObservableBasedOpenerAndCloser() { + final List list = new ArrayList(); + final List> lists = new ArrayList>(); + + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 10); + push(observer, "two", 60); + push(observer, "three", 110); + push(observer, "four", 160); + push(observer, "five", 210); + complete(observer, 500); + return Subscriptions.empty(); + } + }); + + Observable openings = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, Openings.create(), 50); + push(observer, Openings.create(), 200); + complete(observer, 250); + return Subscriptions.empty(); + } + }); + + Func1> closer = new Func1>() { + @Override + public Observable call(Opening opening) { + return Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, Closings.create(), 100); + complete(observer, 101); + return Subscriptions.empty(); + } + }); + } + }; + + Observable> windowed = Observable.create(window(source, openings, closer)); + windowed.subscribe(observeWindow(list, lists)); + + scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); + assertEquals(2, lists.size()); + assertEquals(lists.get(0), list("two", "three")); + assertEquals(lists.get(1), list("five")); + } + + @Test + public void testObservableBasedCloser() { + final List list = new ArrayList(); + final List> lists = new ArrayList>(); + + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 10); + push(observer, "two", 60); + push(observer, "three", 110); + push(observer, "four", 160); + push(observer, "five", 210); + complete(observer, 250); + return Subscriptions.empty(); + } + }); + + Func0> closer = new Func0>() { + @Override + public Observable call() { + return Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, Closings.create(), 100); + complete(observer, 101); + return Subscriptions.empty(); + } + }); + } + }; + + Observable> windowed = Observable.create(window(source, closer)); + windowed.subscribe(observeWindow(list, lists)); + + scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); + assertEquals(3, lists.size()); + assertEquals(lists.get(0), list("one", "two")); + assertEquals(lists.get(1), list("three", "four")); + assertEquals(lists.get(2), list("five")); + } + + private List list(String... args) { + List list = new ArrayList(); + for (String arg : args) { + list.add(arg); + } + return list; + } + + private void push(final Observer observer, final T value, int delay) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onNext(value); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void complete(final Observer observer, int delay) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onCompleted(); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private Action1> observeWindow(final List list, final List> lists) { + return new Action1>() { + @Override + public void call(Observable stringObservable) { + stringObservable.subscribe(new Observer() { + @Override + public void onCompleted() { + lists.add(new ArrayList(list)); + list.clear(); + } + + @Override + public void onError(Throwable e) { + fail(e.getMessage()); + } + + @Override + public void onNext(String args) { + list.add(args); + } + }); + } + }; + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationZipTest.java b/rxjava-core/src/test/java/rx/operators/OperationZipTest.java index 3e027193ab..2a73ac3eab 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationZipTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationZipTest.java @@ -1,7 +1,584 @@ package rx.operators; import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Func2; +import rx.util.functions.Func3; +import rx.util.functions.FuncN; +import rx.util.functions.Functions; + +import java.util.Arrays; +import java.util.Collection; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; +import static rx.operators.OperationZip.Aggregator; +import static rx.operators.OperationZip.ZipObserver; +import static rx.operators.OperationZip.zip; -@Ignore("WIP") public class OperationZipTest { + + @SuppressWarnings("unchecked") + @Test + public void testCollectionSizeDifferentThanFunction() { + FuncN zipr = Functions.fromFunc(getConcatStringIntegerIntArrayZipr()); + //Func3 + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + + @SuppressWarnings("rawtypes") + Collection ws = java.util.Collections.singleton(Observable.from("one", "two")); + Observable w = Observable.create(zip(ws, zipr)); + w.subscribe(aObserver); + + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, never()).onNext(any(String.class)); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testZippingDifferentLengthObservableSequences1() { + Observer w = mock(Observer.class); + + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + TestObservable w3 = new TestObservable(); + + Observable zipW = Observable.create(zip(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsZipr())); + zipW.subscribe(w); + + /* simulate sending data */ + // once for w1 + w1.observer.onNext("1a"); + w1.observer.onCompleted(); + // twice for w2 + w2.observer.onNext("2a"); + w2.observer.onNext("2b"); + w2.observer.onCompleted(); + // 4 times for w3 + w3.observer.onNext("3a"); + w3.observer.onNext("3b"); + w3.observer.onNext("3c"); + w3.observer.onNext("3d"); + w3.observer.onCompleted(); + + /* we should have been called 1 time on the Observer */ + InOrder inOrder = inOrder(w); + inOrder.verify(w).onNext("1a2a3a"); + + inOrder.verify(w, times(1)).onCompleted(); + } + + @Test + public void testZippingDifferentLengthObservableSequences2() { + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + TestObservable w3 = new TestObservable(); + + Observable zipW = Observable.create(zip(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsZipr())); + zipW.subscribe(w); + + /* simulate sending data */ + // 4 times for w1 + w1.observer.onNext("1a"); + w1.observer.onNext("1b"); + w1.observer.onNext("1c"); + w1.observer.onNext("1d"); + w1.observer.onCompleted(); + // twice for w2 + w2.observer.onNext("2a"); + w2.observer.onNext("2b"); + w2.observer.onCompleted(); + // 1 times for w3 + w3.observer.onNext("3a"); + w3.observer.onCompleted(); + + /* we should have been called 1 time on the Observer */ + InOrder inOrder = inOrder(w); + inOrder.verify(w).onNext("1a2a3a"); + + inOrder.verify(w, times(1)).onCompleted(); + + } + + /** + * Testing internal private logic due to the complexity so I want to use TDD to test as a I build it rather than relying purely on the overall functionality expected by the public methods. + */ + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorSimple() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + + InOrder inOrder = inOrder(aObserver); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("helloworld"); + + a.next(r1, "hello "); + a.next(r2, "again"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("hello again"); + + a.complete(r1); + a.complete(r2); + + inOrder.verify(aObserver, never()).onNext(anyString()); + verify(aObserver, times(1)).onCompleted(); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorDifferentSizedResultsWithOnComplete() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + a.complete(r2); + + InOrder inOrder = inOrder(aObserver); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("helloworld"); + + a.next(r1, "hi"); + a.complete(r1); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verify(aObserver, never()).onNext(anyString()); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregateMultipleTypes() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + a.complete(r2); + + InOrder inOrder = inOrder(aObserver); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("helloworld"); + + a.next(r1, "hi"); + a.complete(r1); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verify(aObserver, never()).onNext(anyString()); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregate3Types() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + ZipObserver r3 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + a.addObserver(r3); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, 2); + a.next(r3, new int[] { 5, 6, 7 }); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("hello2[5, 6, 7]"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorsWithDifferentSizesAndTiming() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "one"); + a.next(r1, "two"); + a.next(r1, "three"); + a.next(r2, "A"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("oneA"); + + a.next(r1, "four"); + a.complete(r1); + a.next(r2, "B"); + verify(aObserver, times(1)).onNext("twoB"); + a.next(r2, "C"); + verify(aObserver, times(1)).onNext("threeC"); + a.next(r2, "D"); + verify(aObserver, times(1)).onNext("fourD"); + a.next(r2, "E"); + verify(aObserver, never()).onNext("E"); + a.complete(r2); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorError() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); + + a.error(r1, new RuntimeException("")); + a.next(r1, "hello"); + a.next(r2, "again"); + + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + // we don't want to be called again after an error + verify(aObserver, times(0)).onNext("helloagain"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorUnsubscribe() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Subscription subscription = a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); + + subscription.unsubscribe(); + a.next(r1, "hello"); + a.next(r2, "again"); + + verify(aObserver, times(0)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + // we don't want to be called again after an error + verify(aObserver, times(0)).onNext("helloagain"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorEarlyCompletion() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "one"); + a.next(r1, "two"); + a.complete(r1); + a.next(r2, "A"); + + InOrder inOrder = inOrder(aObserver); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("oneA"); + + a.complete(r2); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verify(aObserver, never()).onNext(anyString()); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testZip2Types() { + Func2 zipr = getConcatStringIntegerZipr(); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + + Observable w = Observable.create(zip(Observable.from("one", "two"), Observable.from(2, 3, 4), zipr)); + w.subscribe(aObserver); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one2"); + verify(aObserver, times(1)).onNext("two3"); + verify(aObserver, never()).onNext("4"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testZip3Types() { + Func3 zipr = getConcatStringIntegerIntArrayZipr(); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + + Observable w = Observable.create(zip(Observable.from("one", "two"), Observable.from(2), Observable.from(new int[] { 4, 5, 6 }), zipr)); + w.subscribe(aObserver); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one2[4, 5, 6]"); + verify(aObserver, never()).onNext("two"); + } + + @Test + public void testOnNextExceptionInvokesOnError() { + Func2 zipr = getDivideZipr(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + + Observable w = Observable.create(zip(Observable.from(10, 20, 30), Observable.from(0, 1, 2), zipr)); + w.subscribe(aObserver); + + verify(aObserver, times(1)).onError(any(Throwable.class)); + } + + private Func2 getDivideZipr() { + Func2 zipr = new Func2() { + + @Override + public Integer call(Integer i1, Integer i2) { + return i1 / i2; + } + + }; + return zipr; + } + + private Func3 getConcat3StringsZipr() { + Func3 zipr = new Func3() { + + @Override + public String call(String a1, String a2, String a3) { + if (a1 == null) { + a1 = ""; + } + if (a2 == null) { + a2 = ""; + } + if (a3 == null) { + a3 = ""; + } + return a1 + a2 + a3; + } + + }; + return zipr; + } + + private FuncN getConcatZipr() { + FuncN zipr = new FuncN() { + + @Override + public String call(Object... args) { + String returnValue = ""; + for (Object o : args) { + if (o != null) { + returnValue += getStringValue(o); + } + } + System.out.println("returning: " + returnValue); + return returnValue; + } + + }; + return zipr; + } + + private Func2 getConcatStringIntegerZipr() { + Func2 zipr = new Func2() { + + @Override + public String call(String s, Integer i) { + return getStringValue(s) + getStringValue(i); + } + + }; + return zipr; + } + + private Func3 getConcatStringIntegerIntArrayZipr() { + Func3 zipr = new Func3() { + + @Override + public String call(String s, Integer i, int[] iArray) { + return getStringValue(s) + getStringValue(i) + getStringValue(iArray); + } + + }; + return zipr; + } + + private static String getStringValue(Object o) { + if (o == null) { + return ""; + } else { + if (o instanceof int[]) { + return Arrays.toString((int[]) o); + } else { + return String.valueOf(o); + } + } + } + + private static class TestObservable implements Observable.OnSubscribeFunc { + + Observer observer; + + @Override + public Subscription onSubscribe(Observer Observer) { + // just store the variable where it can be accessed so we can manually trigger it + this.observer = Observer; + return Subscriptions.empty(); + } + + } } diff --git a/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java b/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java index c5b7e6d590..a4bf9e8e8c 100644 --- a/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java +++ b/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java @@ -1,7 +1,21 @@ package rx.operators; import org.junit.Ignore; +import org.junit.Test; +import rx.Subscription; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; -@Ignore("WIP") public class SafeObservableSubscriptionTest { + + @Test + public void testWrapAfterUnsubscribe() { + SafeObservableSubscription atomicObservableSubscription = new SafeObservableSubscription(); + atomicObservableSubscription.unsubscribe(); + Subscription innerSubscription = mock(Subscription.class); + atomicObservableSubscription.wrap(innerSubscription); + verify(innerSubscription, times(1)).unsubscribe(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java b/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java index 5b4b301590..b9e4b2e278 100644 --- a/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java +++ b/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java @@ -1,7 +1,751 @@ package rx.operators; -import org.junit.Ignore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import rx.Observable; +import rx.Observer; +import rx.Subscription; + +import java.util.Random; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; -@Ignore("WIP") public class SynchronizedObserverTest { + + @Mock + Observer aObserver; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testSingleThreadedBasic() { + Subscription s = mock(Subscription.class); + TestSingleThreadedObservable onSubscribe = new TestSingleThreadedObservable(s, "one", "two", "three"); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + SynchronizedObserver aw = new SynchronizedObserver(aObserver, as); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); + } + + @Test + public void testMultiThreadedBasic() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three"); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + assertEquals(3, busyObserver.onNextCount.get()); + assertFalse(busyObserver.onError); + assertTrue(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); + } + + @Test + public void testMultiThreadedBasicWithLock() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three"); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + + Object lock = new Object(); + ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); + + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); + + externalBusyThread.start(); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + try { + externalBusyThread.join(10000); + assertFalse(externalBusyThread.isAlive()); + assertFalse(externalBusyThread.fail); + } catch (InterruptedException e) { + // ignore + } + + assertEquals(3, busyObserver.onNextCount.get()); + assertFalse(busyObserver.onError); + assertTrue(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); + } + + @Test + public void testMultiThreadedWithNPE() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); + + // we can't know how many onNext calls will occur since they each run on a separate thread + // that depends on thread scheduling so 0, 1, 2 and 3 are all valid options + // assertEquals(3, busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 4); + assertTrue(busyObserver.onError); + // no onCompleted because onError was invoked + assertFalse(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + //verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); + } + + @Test + public void testMultiThreadedWithNPEAndLock() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + + Object lock = new Object(); + ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); + + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); + + externalBusyThread.start(); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + try { + externalBusyThread.join(10000); + assertFalse(externalBusyThread.isAlive()); + assertFalse(externalBusyThread.fail); + } catch (InterruptedException e) { + // ignore + } + + System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); + + // we can't know how many onNext calls will occur since they each run on a separate thread + // that depends on thread scheduling so 0, 1, 2 and 3 are all valid options + // assertEquals(3, busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 4); + assertTrue(busyObserver.onError); + // no onCompleted because onError was invoked + assertFalse(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + //verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); + } + + @Test + public void testMultiThreadedWithNPEinMiddle() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); + // this should not be the full number of items since the error should stop it before it completes all 9 + System.out.println("onNext count: " + busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 9); + assertTrue(busyObserver.onError); + // no onCompleted because onError was invoked + assertFalse(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); + } + + @Test + public void testMultiThreadedWithNPEinMiddleAndLock() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + + Object lock = new Object(); + ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); + + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); + + externalBusyThread.start(); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + try { + externalBusyThread.join(10000); + assertFalse(externalBusyThread.isAlive()); + assertFalse(externalBusyThread.fail); + } catch (InterruptedException e) { + // ignore + } + + System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); + // this should not be the full number of items since the error should stop it before it completes all 9 + System.out.println("onNext count: " + busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 9); + assertTrue(busyObserver.onError); + // no onCompleted because onError was invoked + assertFalse(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); + } + + /** + * A non-realistic use case that tries to expose thread-safety issues by throwing lots of out-of-order + * events on many threads. + * + * @param w + * @param tw + */ + @Test + public void runConcurrencyTest() { + ExecutorService tp = Executors.newFixedThreadPool(20); + try { + TestConcurrencyObserver tw = new TestConcurrencyObserver(); + SafeObservableSubscription s = new SafeObservableSubscription(); + SynchronizedObserver w = new SynchronizedObserver(tw, s); + + Future f1 = tp.submit(new OnNextThread(w, 12000)); + Future f2 = tp.submit(new OnNextThread(w, 5000)); + Future f3 = tp.submit(new OnNextThread(w, 75000)); + Future f4 = tp.submit(new OnNextThread(w, 13500)); + Future f5 = tp.submit(new OnNextThread(w, 22000)); + Future f6 = tp.submit(new OnNextThread(w, 15000)); + Future f7 = tp.submit(new OnNextThread(w, 7500)); + Future f8 = tp.submit(new OnNextThread(w, 23500)); + + Future f10 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f1, f2, f3, f4)); + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // ignore + } + Future f11 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f12 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f13 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f14 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + // // the next 4 onError events should wait on same as f10 + Future f15 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f16 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f17 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f18 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + + waitOnThreads(f1, f2, f3, f4, f5, f6, f7, f8, f10, f11, f12, f13, f14, f15, f16, f17, f18); + @SuppressWarnings("unused") + int numNextEvents = tw.assertEvents(null); // no check of type since we don't want to test barging results here, just interleaving behavior + // System.out.println("Number of events executed: " + numNextEvents); + } catch (Throwable e) { + fail("Concurrency test failed: " + e.getMessage()); + e.printStackTrace(); + } finally { + tp.shutdown(); + try { + tp.awaitTermination(5000, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + private static void waitOnThreads(Future... futures) { + for (Future f : futures) { + try { + f.get(10, TimeUnit.SECONDS); + } catch (Throwable e) { + System.err.println("Failed while waiting on future."); + e.printStackTrace(); + } + } + } + + /** + * A thread that will pass data to onNext + */ + public static class OnNextThread implements Runnable { + + private final Observer Observer; + private final int numStringsToSend; + + OnNextThread(Observer Observer, int numStringsToSend) { + this.Observer = Observer; + this.numStringsToSend = numStringsToSend; + } + + @Override + public void run() { + for (int i = 0; i < numStringsToSend; i++) { + Observer.onNext("aString"); + } + } + } + + /** + * A thread that will call onError or onNext + */ + public static class CompletionThread implements Runnable { + + private final Observer Observer; + private final TestConcurrencyObserverEvent event; + private final Future[] waitOnThese; + + CompletionThread(Observer Observer, TestConcurrencyObserverEvent event, Future... waitOnThese) { + this.Observer = Observer; + this.event = event; + this.waitOnThese = waitOnThese; + } + + @Override + public void run() { + /* if we have 'waitOnThese' futures, we'll wait on them before proceeding */ + if (waitOnThese != null) { + for (Future f : waitOnThese) { + try { + f.get(); + } catch (Throwable e) { + System.err.println("Error while waiting on future in CompletionThread"); + } + } + } + + /* send the event */ + if (event == TestConcurrencyObserverEvent.onError) { + Observer.onError(new RuntimeException("mocked exception")); + } else if (event == TestConcurrencyObserverEvent.onCompleted) { + Observer.onCompleted(); + + } else { + throw new IllegalArgumentException("Expecting either onError or onCompleted"); + } + } + } + + private static enum TestConcurrencyObserverEvent { + onCompleted, onError, onNext + } + + private static class TestConcurrencyObserver implements Observer { + + /** + * used to store the order and number of events received + */ + private final LinkedBlockingQueue events = new LinkedBlockingQueue(); + private final int waitTime; + + @SuppressWarnings("unused") + public TestConcurrencyObserver(int waitTimeInNext) { + this.waitTime = waitTimeInNext; + } + + public TestConcurrencyObserver() { + this.waitTime = 0; + } + + @Override + public void onCompleted() { + events.add(TestConcurrencyObserverEvent.onCompleted); + } + + @Override + public void onError(Throwable e) { + events.add(TestConcurrencyObserverEvent.onError); + } + + @Override + public void onNext(String args) { + events.add(TestConcurrencyObserverEvent.onNext); + // do some artificial work to make the thread scheduling/timing vary + int s = 0; + for (int i = 0; i < 20; i++) { + s += s * i; + } + + if (waitTime > 0) { + try { + Thread.sleep(waitTime); + } catch (InterruptedException e) { + // ignore + } + } + } + + /** + * Assert the order of events is correct and return the number of onNext executions. + * + * @param expectedEndingEvent + * @return int count of onNext calls + * @throws IllegalStateException If order of events was invalid. + */ + public int assertEvents(TestConcurrencyObserverEvent expectedEndingEvent) throws IllegalStateException { + int nextCount = 0; + boolean finished = false; + for (TestConcurrencyObserverEvent e : events) { + if (e == TestConcurrencyObserverEvent.onNext) { + if (finished) { + // already finished, we shouldn't get this again + throw new IllegalStateException("Received onNext but we're already finished."); + } + nextCount++; + } else if (e == TestConcurrencyObserverEvent.onError) { + if (finished) { + // already finished, we shouldn't get this again + throw new IllegalStateException("Received onError but we're already finished."); + } + if (expectedEndingEvent != null && TestConcurrencyObserverEvent.onError != expectedEndingEvent) { + throw new IllegalStateException("Received onError ending event but expected " + expectedEndingEvent); + } + finished = true; + } else if (e == TestConcurrencyObserverEvent.onCompleted) { + if (finished) { + // already finished, we shouldn't get this again + throw new IllegalStateException("Received onCompleted but we're already finished."); + } + if (expectedEndingEvent != null && TestConcurrencyObserverEvent.onCompleted != expectedEndingEvent) { + throw new IllegalStateException("Received onCompleted ending event but expected " + expectedEndingEvent); + } + finished = true; + } + } + + return nextCount; + } + + } + + /** + * This spawns a single thread for the subscribe execution + */ + private static class TestSingleThreadedObservable implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + private Thread t = null; + + public TestSingleThreadedObservable(final Subscription s, final String... values) { + this.s = s; + this.values = values; + + } + + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestSingleThreadedObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestSingleThreadedObservable thread"); + for (String s : values) { + System.out.println("TestSingleThreadedObservable onNext: " + s); + observer.onNext(s); + } + observer.onCompleted(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + }); + System.out.println("starting TestSingleThreadedObservable thread"); + t.start(); + System.out.println("done starting TestSingleThreadedObservable thread"); + return s; + } + + public void waitToFinish() { + try { + t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + + /** + * This spawns a thread for the subscription, then a separate thread for each onNext call. + */ + private static class TestMultiThreadedObservable implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + Thread t = null; + AtomicInteger threadsRunning = new AtomicInteger(); + AtomicInteger maxConcurrentThreads = new AtomicInteger(); + ExecutorService threadPool; + + public TestMultiThreadedObservable(Subscription s, String... values) { + this.s = s; + this.values = values; + this.threadPool = Executors.newCachedThreadPool(); + } + + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestMultiThreadedObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestMultiThreadedObservable thread"); + for (final String s : values) { + threadPool.execute(new Runnable() { + + @Override + public void run() { + threadsRunning.incrementAndGet(); + try { + // perform onNext call + System.out.println("TestMultiThreadedObservable onNext: " + s); + if (s == null) { + // force an error + throw new NullPointerException(); + } + observer.onNext(s); + // capture 'maxThreads' + int concurrentThreads = threadsRunning.get(); + int maxThreads = maxConcurrentThreads.get(); + if (concurrentThreads > maxThreads) { + maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + } + } catch (Throwable e) { + observer.onError(e); + } finally { + threadsRunning.decrementAndGet(); + } + } + }); + } + // we are done spawning threads + threadPool.shutdown(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + + // wait until all threads are done, then mark it as COMPLETED + try { + // wait for all the threads to finish + threadPool.awaitTermination(2, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + observer.onCompleted(); + } + }); + System.out.println("starting TestMultiThreadedObservable thread"); + t.start(); + System.out.println("done starting TestMultiThreadedObservable thread"); + return s; + } + + public void waitToFinish() { + try { + t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + private static class BusyObserver implements Observer { + volatile boolean onCompleted = false; + volatile boolean onError = false; + AtomicInteger onNextCount = new AtomicInteger(); + AtomicInteger threadsRunning = new AtomicInteger(); + AtomicInteger maxConcurrentThreads = new AtomicInteger(); + + @Override + public void onCompleted() { + threadsRunning.incrementAndGet(); + + System.out.println(">>> BusyObserver received onCompleted"); + onCompleted = true; + + int concurrentThreads = threadsRunning.get(); + int maxThreads = maxConcurrentThreads.get(); + if (concurrentThreads > maxThreads) { + maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + } + threadsRunning.decrementAndGet(); + } + + @Override + public void onError(Throwable e) { + threadsRunning.incrementAndGet(); + + System.out.println(">>> BusyObserver received onError: " + e.getMessage()); + onError = true; + + int concurrentThreads = threadsRunning.get(); + int maxThreads = maxConcurrentThreads.get(); + if (concurrentThreads > maxThreads) { + maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + } + threadsRunning.decrementAndGet(); + } + + @Override + public void onNext(String args) { + threadsRunning.incrementAndGet(); + try { + onNextCount.incrementAndGet(); + System.out.println(">>> BusyObserver received onNext: " + args); + try { + // simulate doing something computational + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } finally { + // capture 'maxThreads' + int concurrentThreads = threadsRunning.get(); + int maxThreads = maxConcurrentThreads.get(); + if (concurrentThreads > maxThreads) { + maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + } + threadsRunning.decrementAndGet(); + } + } + + } + + private static class ExternalBusyThread extends Thread { + + private BusyObserver observer; + private Object lock; + private int lockTimes; + private int waitTime; + public volatile boolean fail; + + public ExternalBusyThread(BusyObserver observer, Object lock, int lockTimes, int waitTime) { + this.observer = observer; + this.lock = lock; + this.lockTimes = lockTimes; + this.waitTime = waitTime; + this.fail = false; + } + + @Override + public void run() { + Random r = new Random(); + for (int i = 0; i < lockTimes; i++) { + synchronized (lock) { + int oldOnNextCount = observer.onNextCount.get(); + boolean oldOnCompleted = observer.onCompleted; + boolean oldOnError = observer.onError; + try { + Thread.sleep(r.nextInt(waitTime)); + } catch (InterruptedException e) { + // ignore + } + // Since we own the lock, onNextCount, onCompleted and + // onError must not be changed. + int newOnNextCount = observer.onNextCount.get(); + boolean newOnCompleted = observer.onCompleted; + boolean newOnError = observer.onError; + if (oldOnNextCount != newOnNextCount) { + System.out.println(">>> ExternalBusyThread received different onNextCount: " + + oldOnNextCount + + " -> " + + newOnNextCount); + fail = true; + break; + } + if (oldOnCompleted != newOnCompleted) { + System.out.println(">>> ExternalBusyThread received different onCompleted: " + + oldOnCompleted + + " -> " + + newOnCompleted); + fail = true; + break; + } + if (oldOnError != newOnError) { + System.out.println(">>> ExternalBusyThread received different onError: " + + oldOnError + + " -> " + + newOnError); + fail = true; + break; + } + } + } + } + + } } diff --git a/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java b/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java index 1def236246..3ea17bf152 100644 --- a/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java +++ b/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java @@ -1,7 +1,77 @@ package rx.plugins; -import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; -@Ignore("WIP") public class RxJavaPluginsTest { + + @Test + public void testErrorHandlerDefaultImpl() { + RxJavaErrorHandler impl = new RxJavaPlugins().getErrorHandler(); + assertTrue(impl instanceof RxJavaErrorHandlerDefault); + } + + @Test + public void testErrorHandlerViaRegisterMethod() { + RxJavaPlugins p = new RxJavaPlugins(); + p.registerErrorHandler(new RxJavaErrorHandlerTestImpl()); + RxJavaErrorHandler impl = p.getErrorHandler(); + assertTrue(impl instanceof RxJavaErrorHandlerTestImpl); + } + + @Test + public void testErrorHandlerViaProperty() { + try { + RxJavaPlugins p = new RxJavaPlugins(); + String fullClass = getFullClassNameForTestClass(RxJavaErrorHandlerTestImpl.class); + System.setProperty("rxjava.plugin.RxJavaErrorHandler.implementation", fullClass); + RxJavaErrorHandler impl = p.getErrorHandler(); + assertTrue(impl instanceof RxJavaErrorHandlerTestImpl); + } finally { + System.clearProperty("rxjava.plugin.RxJavaErrorHandler.implementation"); + } + } + + // inside test so it is stripped from Javadocs + public static class RxJavaErrorHandlerTestImpl extends RxJavaErrorHandler { + // just use defaults + } + + @Test + public void testObservableExecutionHookDefaultImpl() { + RxJavaPlugins p = new RxJavaPlugins(); + RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); + assertTrue(impl instanceof RxJavaObservableExecutionHookDefault); + } + + @Test + public void testObservableExecutionHookViaRegisterMethod() { + RxJavaPlugins p = new RxJavaPlugins(); + p.registerObservableExecutionHook(new RxJavaObservableExecutionHookTestImpl()); + RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); + assertTrue(impl instanceof RxJavaObservableExecutionHookTestImpl); + } + + @Test + public void testObservableExecutionHookViaProperty() { + try { + RxJavaPlugins p = new RxJavaPlugins(); + String fullClass = getFullClassNameForTestClass(RxJavaObservableExecutionHookTestImpl.class); + System.setProperty("rxjava.plugin.RxJavaObservableExecutionHook.implementation", fullClass); + RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); + assertTrue(impl instanceof RxJavaObservableExecutionHookTestImpl); + } finally { + System.clearProperty("rxjava.plugin.RxJavaObservableExecutionHook.implementation"); + } + } + + // inside test so it is stripped from Javadocs + public static class RxJavaObservableExecutionHookTestImpl extends RxJavaObservableExecutionHook { + // just use defaults + } + + private static String getFullClassNameForTestClass(Class cls) { + return RxJavaPlugins.class.getPackage().getName() + "." + RxJavaPluginsTest.class.getSimpleName() + "$" + cls.getSimpleName(); + } } diff --git a/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java index bfe0aa0bb0..fb5220640b 100644 --- a/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java @@ -1,7 +1,135 @@ package rx.subjects; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Action1; +import rx.util.functions.Func0; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; -@Ignore("WIP") public class AsyncSubjectTest { + + + private final Throwable testException = new Throwable(); + + @Test + public void testNeverCompleted() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + + assertNeverCompletedObserver(aObserver); + } + + private void assertNeverCompletedObserver(Observer aObserver) { + verify(aObserver, Mockito.never()).onNext(anyString()); + verify(aObserver, Mockito.never()).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testCompleted() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onCompleted(); + + assertCompletedObserver(aObserver); + } + + private void assertCompletedObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testError() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onError(testException); + subject.onNext("four"); + subject.onError(new Throwable()); + subject.onCompleted(); + + assertErrorObserver(aObserver); + } + + private void assertErrorObserver(Observer aObserver) { + verify(aObserver, Mockito.never()).onNext(anyString()); + verify(aObserver, times(1)).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testUnsubscribeBeforeCompleted() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Subscription subscription = subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + + subscription.unsubscribe(); + assertNoOnNextEventsReceived(aObserver); + + subject.onNext("three"); + subject.onCompleted(); + + assertNoOnNextEventsReceived(aObserver); + } + + private void assertNoOnNextEventsReceived(Observer aObserver) { + verify(aObserver, Mockito.never()).onNext(anyString()); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testUnsubscribe() { + UnsubscribeTester.test( + new Func0>() { + @Override + public AsyncSubject call() { + return AsyncSubject.create(); + } + }, new Action1>() { + @Override + public void call(AsyncSubject DefaultSubject) { + DefaultSubject.onCompleted(); + } + }, new Action1>() { + @Override + public void call(AsyncSubject DefaultSubject) { + DefaultSubject.onError(new Throwable()); + } + }, + null + ); + } } diff --git a/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java index ffee087158..eae3ffbc06 100644 --- a/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java @@ -1,7 +1,135 @@ package rx.subjects; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import rx.Observer; +import rx.util.functions.Action1; +import rx.util.functions.Func0; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; -@Ignore("WIP") public class BehaviorSubjectTest { + + + private final Throwable testException = new Throwable(); + + @Test + public void testThatObserverReceivesDefaultValueIfNothingWasPublished() { + BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + + assertReceivedAllEvents(aObserver); + } + + private void assertReceivedAllEvents(Observer aObserver) { + verify(aObserver, times(1)).onNext("default"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testThatObserverDoesNotReceiveDefaultValueIfSomethingWasPublished() { + BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); + + subject.onNext("one"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("two"); + subject.onNext("three"); + + assertDidNotReceiveTheDefaultValue(aObserver); + } + + private void assertDidNotReceiveTheDefaultValue(Observer aObserver) { + verify(aObserver, Mockito.never()).onNext("default"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testCompleted() { + BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onCompleted(); + + assertCompletedObserver(aObserver); + } + + private void assertCompletedObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("default"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testCompletedAfterError() { + BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onError(testException); + subject.onNext("two"); + subject.onCompleted(); + + assertErrorObserver(aObserver); + } + + private void assertErrorObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("default"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onError(testException); + } + + @Test + public void testUnsubscribe() { + UnsubscribeTester.test( + new Func0>() { + @Override + public BehaviorSubject call() { + return BehaviorSubject.createWithDefaultValue("default"); + } + }, new Action1>() { + @Override + public void call(BehaviorSubject DefaultSubject) { + DefaultSubject.onCompleted(); + } + }, new Action1>() { + @Override + public void call(BehaviorSubject DefaultSubject) { + DefaultSubject.onError(new Throwable()); + } + }, new Action1>() { + @Override + public void call(BehaviorSubject DefaultSubject) { + DefaultSubject.onNext("one"); + } + } + ); + } } diff --git a/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java index 25cebbf510..da6abf7c44 100644 --- a/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java @@ -1,7 +1,364 @@ package rx.subjects; -import org.junit.Ignore; +import junit.framework.Assert; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; +import rx.Notification; +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Action1; +import rx.util.functions.Func0; +import rx.util.functions.Func1; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; -@Ignore("WIP") public class PublishSubjectTest { + + @Test + public void test() { + PublishSubject subject = PublishSubject.create(); + final AtomicReference>> actualRef = new AtomicReference>>(); + + Observable>> wNotificationsList = subject.materialize().toList(); + wNotificationsList.subscribe(new Action1>>() { + @Override + public void call(List> actual) { + actualRef.set(actual); + } + }); + + Subscription sub = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(final Observer observer) { + final AtomicBoolean stop = new AtomicBoolean(false); + new Thread() { + @Override + public void run() { + int i = 1; + while (!stop.get()) { + observer.onNext(i++); + } + observer.onCompleted(); + } + }.start(); + return new Subscription() { + @Override + public void unsubscribe() { + stop.set(true); + } + }; + } + }).subscribe(subject); + // the subject has received an onComplete from the first subscribe because + // it is synchronous and the next subscribe won't do anything. + Observable.from(-1, -2, -3).subscribe(subject); + + List> expected = new ArrayList>(); + expected.add(new Notification(-1)); + expected.add(new Notification(-2)); + expected.add(new Notification(-3)); + expected.add(new Notification()); + Assert.assertTrue(actualRef.get().containsAll(expected)); + + sub.unsubscribe(); + } + + private final Throwable testException = new Throwable(); + + @Test + public void testCompleted() { + PublishSubject subject = PublishSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onCompleted(); + + @SuppressWarnings("unchecked") + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); + + subject.onNext("four"); + subject.onCompleted(); + subject.onError(new Throwable()); + + assertCompletedObserver(aObserver); + // todo bug? assertNeverObserver(anotherObserver); + } + + private void assertCompletedObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testError() { + PublishSubject subject = PublishSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onError(testException); + + @SuppressWarnings("unchecked") + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); + + subject.onNext("four"); + subject.onError(new Throwable()); + subject.onCompleted(); + + assertErrorObserver(aObserver); + // todo bug? assertNeverObserver(anotherObserver); + } + + private void assertErrorObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, times(1)).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testSubscribeMidSequence() { + PublishSubject subject = PublishSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + + assertObservedUntilTwo(aObserver); + + @SuppressWarnings("unchecked") + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); + + subject.onNext("three"); + subject.onCompleted(); + + assertCompletedObserver(aObserver); + assertCompletedStartingWithThreeObserver(anotherObserver); + } + + private void assertCompletedStartingWithThreeObserver(Observer aObserver) { + verify(aObserver, Mockito.never()).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testUnsubscribeFirstObserver() { + PublishSubject subject = PublishSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Subscription subscription = subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + + subscription.unsubscribe(); + assertObservedUntilTwo(aObserver); + + @SuppressWarnings("unchecked") + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); + + subject.onNext("three"); + subject.onCompleted(); + + assertObservedUntilTwo(aObserver); + assertCompletedStartingWithThreeObserver(anotherObserver); + } + + private void assertObservedUntilTwo(Observer aObserver) { + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testUnsubscribe() { + UnsubscribeTester.test( + new Func0>() { + @Override + public PublishSubject call() { + return PublishSubject.create(); + } + }, new Action1>() { + @Override + public void call(PublishSubject DefaultSubject) { + DefaultSubject.onCompleted(); + } + }, new Action1>() { + @Override + public void call(PublishSubject DefaultSubject) { + DefaultSubject.onError(new Throwable()); + } + }, new Action1>() { + @Override + public void call(PublishSubject DefaultSubject) { + DefaultSubject.onNext("one"); + } + } + ); + } + + @Test + public void testNestedSubscribe() { + final PublishSubject s = PublishSubject.create(); + + final AtomicInteger countParent = new AtomicInteger(); + final AtomicInteger countChildren = new AtomicInteger(); + final AtomicInteger countTotal = new AtomicInteger(); + + final ArrayList list = new ArrayList(); + + s.mapMany(new Func1>() { + + @Override + public Observable call(final Integer v) { + countParent.incrementAndGet(); + + // then subscribe to subject again (it will not receive the previous value) + return s.map(new Func1() { + + @Override + public String call(Integer v2) { + countChildren.incrementAndGet(); + return "Parent: " + v + " Child: " + v2; + } + + }); + } + + }).subscribe(new Action1() { + + @Override + public void call(String v) { + countTotal.incrementAndGet(); + list.add(v); + } + + }); + + for (int i = 0; i < 10; i++) { + s.onNext(i); + } + s.onCompleted(); + + // System.out.println("countParent: " + countParent.get()); + // System.out.println("countChildren: " + countChildren.get()); + // System.out.println("countTotal: " + countTotal.get()); + + // 9+8+7+6+5+4+3+2+1+0 == 45 + assertEquals(45, list.size()); + } + + /** + * Should be able to unsubscribe all Observers, have it stop emitting, then subscribe new ones and it start emitting again. + */ + @Test + public void testReSubscribe() { + final PublishSubject ps = PublishSubject.create(); + + Observer o1 = mock(Observer.class); + Subscription s1 = ps.subscribe(o1); + + // emit + ps.onNext(1); + + // validate we got it + InOrder inOrder1 = inOrder(o1); + inOrder1.verify(o1, times(1)).onNext(1); + inOrder1.verifyNoMoreInteractions(); + + // unsubscribe + s1.unsubscribe(); + + // emit again but nothing will be there to receive it + ps.onNext(2); + + Observer o2 = mock(Observer.class); + Subscription s2 = ps.subscribe(o2); + + // emit + ps.onNext(3); + + // validate we got it + InOrder inOrder2 = inOrder(o2); + inOrder2.verify(o2, times(1)).onNext(3); + inOrder2.verifyNoMoreInteractions(); + + s2.unsubscribe(); + } + + /** + * Even if subject received an onError/onCompleted, new subscriptions should be able to restart it. + */ + @Test + public void testReSubscribeAfterTerminalState() { + final PublishSubject ps = PublishSubject.create(); + + Observer o1 = mock(Observer.class); + Subscription s1 = ps.subscribe(o1); + + // emit + ps.onNext(1); + + // validate we got it + InOrder inOrder1 = inOrder(o1); + inOrder1.verify(o1, times(1)).onNext(1); + inOrder1.verifyNoMoreInteractions(); + + // unsubscribe + s1.unsubscribe(); + + ps.onCompleted(); + + // emit again but nothing will be there to receive it + ps.onNext(2); + + Observer o2 = mock(Observer.class); + Subscription s2 = ps.subscribe(o2); + + // emit + ps.onNext(3); + + // validate we got it + InOrder inOrder2 = inOrder(o2); + inOrder2.verify(o2, times(1)).onNext(3); + inOrder2.verifyNoMoreInteractions(); + + s2.unsubscribe(); + } } diff --git a/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java b/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java index 1e722e5e5e..dc0298a7e3 100644 --- a/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java @@ -1,7 +1,169 @@ package rx.subjects; -import org.junit.Ignore; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Action1; +import rx.util.functions.Func0; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; -@Ignore("WIP") public class ReplaySubjectTest { + + private final Throwable testException = new Throwable(); + + @SuppressWarnings("unchecked") + @Test + public void testCompleted() { + ReplaySubject subject = ReplaySubject.create(); + + Observer o1 = mock(Observer.class); + subject.subscribe(o1); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onCompleted(); + + subject.onNext("four"); + subject.onCompleted(); + subject.onError(new Throwable()); + + assertCompletedObserver(o1); + + // assert that subscribing a 2nd time gets the same data + Observer o2 = mock(Observer.class); + subject.subscribe(o2); + assertCompletedObserver(o2); + } + + private void assertCompletedObserver(Observer aObserver) { + InOrder inOrder = inOrder(aObserver); + + inOrder.verify(aObserver, times(1)).onNext("one"); + inOrder.verify(aObserver, times(1)).onNext("two"); + inOrder.verify(aObserver, times(1)).onNext("three"); + inOrder.verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @SuppressWarnings("unchecked") + @Test + public void testError() { + ReplaySubject subject = ReplaySubject.create(); + + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onError(testException); + + subject.onNext("four"); + subject.onError(new Throwable()); + subject.onCompleted(); + + assertErrorObserver(aObserver); + + aObserver = mock(Observer.class); + subject.subscribe(aObserver); + assertErrorObserver(aObserver); + } + + private void assertErrorObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, times(1)).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @SuppressWarnings("unchecked") + @Test + public void testSubscribeMidSequence() { + ReplaySubject subject = ReplaySubject.create(); + + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + + assertObservedUntilTwo(aObserver); + + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); + assertObservedUntilTwo(anotherObserver); + + subject.onNext("three"); + subject.onCompleted(); + + assertCompletedObserver(aObserver); + assertCompletedObserver(anotherObserver); + } + + @SuppressWarnings("unchecked") + @Test + public void testUnsubscribeFirstObserver() { + ReplaySubject subject = ReplaySubject.create(); + + Observer aObserver = mock(Observer.class); + Subscription subscription = subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + + subscription.unsubscribe(); + assertObservedUntilTwo(aObserver); + + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); + assertObservedUntilTwo(anotherObserver); + + subject.onNext("three"); + subject.onCompleted(); + + assertObservedUntilTwo(aObserver); + assertCompletedObserver(anotherObserver); + } + + private void assertObservedUntilTwo(Observer aObserver) { + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testUnsubscribe() { + UnsubscribeTester.test( + new Func0>() { + @Override + public ReplaySubject call() { + return ReplaySubject.create(); + } + }, new Action1>() { + @Override + public void call(ReplaySubject repeatSubject) { + repeatSubject.onCompleted(); + } + }, new Action1>() { + @Override + public void call(ReplaySubject repeatSubject) { + repeatSubject.onError(new Throwable()); + } + }, new Action1>() { + @Override + public void call(ReplaySubject repeatSubject) { + repeatSubject.onNext("one"); + } + } + ); + } } diff --git a/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java index 43830564ce..3bb8b718a0 100644 --- a/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java +++ b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java @@ -1,7 +1,70 @@ package rx.subscriptions; -import org.junit.Ignore; +import org.junit.Test; +import rx.Subscription; +import rx.util.CompositeException; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; -@Ignore("WIP") public class CompositeSubscriptionTest { + + @Test + public void testSuccess() { + final AtomicInteger counter = new AtomicInteger(); + CompositeSubscription s = new CompositeSubscription(); + s.add(new Subscription() { + + @Override + public void unsubscribe() { + counter.incrementAndGet(); + } + }); + + s.add(new Subscription() { + + @Override + public void unsubscribe() { + counter.incrementAndGet(); + } + }); + + s.unsubscribe(); + + assertEquals(2, counter.get()); + } + + @Test + public void testException() { + final AtomicInteger counter = new AtomicInteger(); + CompositeSubscription s = new CompositeSubscription(); + s.add(new Subscription() { + + @Override + public void unsubscribe() { + throw new RuntimeException("failed on first one"); + } + }); + + s.add(new Subscription() { + + @Override + public void unsubscribe() { + counter.incrementAndGet(); + } + }); + + try { + s.unsubscribe(); + fail("Expecting an exception"); + } catch (CompositeException e) { + // we expect this + assertEquals(1, e.getExceptions().size()); + } + + // we should still have unsubscribed to the second one + assertEquals(1, counter.get()); + } } diff --git a/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java b/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java index 17bb5b4619..5ac0242a95 100644 --- a/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java +++ b/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java @@ -1,7 +1,20 @@ package rx.subscriptions; -import org.junit.Ignore; +import org.junit.Test; +import rx.Subscription; +import rx.util.functions.Action0; + +import static org.mockito.Mockito.*; +import static rx.subscriptions.Subscriptions.create; -@Ignore("WIP") public class SubscriptionsTest { + + @Test + public void testUnsubscribeOnlyOnce() { + Action0 unsubscribe = mock(Action0.class); + Subscription subscription = create(unsubscribe); + subscription.unsubscribe(); + subscription.unsubscribe(); + verify(unsubscribe, times(1)).call(); + } } diff --git a/rxjava-core/src/test/java/rx/test/OperatorTester.java b/rxjava-core/src/test/java/rx/test/OperatorTester.java new file mode 100644 index 0000000000..ab64251583 --- /dev/null +++ b/rxjava-core/src/test/java/rx/test/OperatorTester.java @@ -0,0 +1,94 @@ +/** + * Copyright 2013 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.test; + +import rx.Scheduler; +import rx.Subscription; +import rx.util.functions.Action0; +import rx.util.functions.Func2; + +import java.util.concurrent.TimeUnit; + +/** + * Common utility functions for testing operator implementations. + */ +public class OperatorTester { + /* + * This is purposefully package-only so it does not leak into the public API outside of this package. + * + * This package is implementation details and not part of the Javadocs and thus can change without breaking backwards compatibility. + * + * benjchristensen => I'm procrastinating the decision of where and how these types of classes (see rx.subjects.UnsubscribeTester) should exist. + * If they are only for internal implementations then I don't want them as part of the API. + * If they are truly useful for everyone to use then an "rx.testing" package may make sense. + */ + + private OperatorTester() { + } + + /** + * Used for mocking of Schedulers since many Scheduler implementations are static/final. + * + * @param underlying + * @return + */ + public static Scheduler forwardingScheduler(Scheduler underlying) { + return new ForwardingScheduler(underlying); + } + + public static class ForwardingScheduler extends Scheduler { + private final Scheduler underlying; + + public ForwardingScheduler(Scheduler underlying) { + this.underlying = underlying; + } + + @Override + public Subscription schedule(Action0 action) { + return underlying.schedule(action); + } + + @Override + public Subscription schedule(T state, Func2 action) { + return underlying.schedule(state, action); + } + + @Override + public Subscription schedule(Action0 action, long dueTime, TimeUnit unit) { + return underlying.schedule(action, dueTime, unit); + } + + @Override + public Subscription schedule(T state, Func2 action, long dueTime, TimeUnit unit) { + return underlying.schedule(state, action, dueTime, unit); + } + + @Override + public Subscription schedulePeriodically(Action0 action, long initialDelay, long period, TimeUnit unit) { + return underlying.schedulePeriodically(action, initialDelay, period, unit); + } + + @Override + public Subscription schedulePeriodically(T state, Func2 action, long initialDelay, long period, TimeUnit unit) { + return underlying.schedulePeriodically(state, action, initialDelay, period, unit); + } + + @Override + public long now() { + return underlying.now(); + } + } +} \ No newline at end of file diff --git a/rxjava-core/src/test/java/rx/util/RangeTest.java b/rxjava-core/src/test/java/rx/util/RangeTest.java index 640678b5c3..03ec6eef19 100644 --- a/rxjava-core/src/test/java/rx/util/RangeTest.java +++ b/rxjava-core/src/test/java/rx/util/RangeTest.java @@ -1,7 +1,50 @@ package rx.util; -import org.junit.Ignore; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; -@Ignore("WIP") public class RangeTest { + + @Test + public void testSimpleRange() { + assertEquals(Arrays.asList(1, 2, 3, 4), toList(Range.create(1, 5))); + } + + @Test + public void testRangeWithStep() { + assertEquals(Arrays.asList(1, 3, 5, 7, 9), toList(Range.createWithStep(1, 10, 2))); + } + + @Test + public void testRangeWithCount() { + assertEquals(Arrays.asList(1, 2, 3, 4, 5), toList(Range.createWithCount(1, 5))); + } + + @Test + public void testRangeWithCount2() { + assertEquals(Arrays.asList(2, 3, 4, 5), toList(Range.createWithCount(2, 4))); + } + + @Test + public void testRangeWithCount3() { + assertEquals(Arrays.asList(0, 1, 2, 3), toList(Range.createWithCount(0, 4))); + } + + @Test + public void testRangeWithCount4() { + assertEquals(Arrays.asList(10, 11, 12, 13, 14), toList(Range.createWithCount(10, 5))); + } + + private static List toList(Iterable iterable) { + List result = new ArrayList(); + for (T element : iterable) { + result.add(element); + } + return result; + } } From 97ab170928f91847de51d8094a0bcf363de82b22 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 4 Nov 2013 19:52:04 -0800 Subject: [PATCH 224/333] Fix ambiguous imports Mockito.* and Observable.* both have a never() method in them. --- .../rx/operators/OperationDistinctTest.java | 56 +++++++++---------- .../OperationDistinctUntilChangedTest.java | 56 +++++++++---------- .../OperationFirstOrDefaultTest.java | 30 +++++----- .../rx/operators/OperationSkipWhileTest.java | 24 ++++---- 4 files changed, 79 insertions(+), 87 deletions(-) diff --git a/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java b/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java index 740ba9429a..f1c91c0fed 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java @@ -1,23 +1,21 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.*; +import static rx.operators.OperationDistinct.*; + +import java.util.Comparator; + import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mock; + import rx.Observable; import rx.Observer; import rx.util.functions.Func1; -import java.util.Comparator; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.never; -import static org.mockito.MockitoAnnotations.initMocks; -import static rx.Observable.*; -import static rx.operators.OperationDistinct.distinct; - public class OperationDistinctTest { @Mock @@ -50,8 +48,8 @@ public void before() { @Test public void testDistinctOfNone() { - Observable src = empty(); - create(distinct(src)).subscribe(w); + Observable src = Observable.empty(); + Observable.create(distinct(src)).subscribe(w); verify(w, never()).onNext(anyString()); verify(w, never()).onError(any(Throwable.class)); @@ -60,8 +58,8 @@ public void testDistinctOfNone() { @Test public void testDistinctOfNoneWithKeySelector() { - Observable src = empty(); - create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + Observable src = Observable.empty(); + Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); verify(w, never()).onNext(anyString()); verify(w, never()).onError(any(Throwable.class)); @@ -70,8 +68,8 @@ public void testDistinctOfNoneWithKeySelector() { @Test public void testDistinctOfNormalSource() { - Observable src = from("a", "b", "c", "c", "c", "b", "b", "a", "e"); - create(distinct(src)).subscribe(w); + Observable src = Observable.from("a", "b", "c", "c", "c", "b", "b", "a", "e"); + Observable.create(distinct(src)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); @@ -85,8 +83,8 @@ public void testDistinctOfNormalSource() { @Test public void testDistinctOfNormalSourceWithKeySelector() { - Observable src = from("a", "B", "c", "C", "c", "B", "b", "a", "E"); - create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + Observable src = Observable.from("a", "B", "c", "C", "c", "B", "b", "a", "E"); + Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); @@ -100,8 +98,8 @@ public void testDistinctOfNormalSourceWithKeySelector() { @Test public void testDistinctOfNormalSourceWithComparator() { - Observable src = from("1", "12", "123", "aaa", "321", "12", "21", "1", "12345"); - create(distinct(src, COMPARE_LENGTH)).subscribe(w); + Observable src = Observable.from("1", "12", "123", "aaa", "321", "12", "21", "1", "12345"); + Observable.create(distinct(src, COMPARE_LENGTH)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("1"); @@ -115,8 +113,8 @@ public void testDistinctOfNormalSourceWithComparator() { @Test public void testDistinctOfNormalSourceWithKeySelectorAndComparator() { - Observable src = from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd"); - create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); + Observable src = Observable.from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd"); + Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); @@ -130,13 +128,13 @@ public void testDistinctOfNormalSourceWithKeySelectorAndComparator() { @Test public void testDistinctOfNormalSourceWithKeySelectorAndComparatorAndTwoSubscriptions() { - Observable src = from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd"); - create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); + Observable src = Observable.from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd"); + Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); inOrder.verify(w, times(1)).onNext("x"); - create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2); + Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2); inOrder.verify(w, times(1)).onNext("abc"); inOrder.verify(w, times(1)).onNext("abcd"); inOrder.verify(w, times(1)).onCompleted(); @@ -155,8 +153,8 @@ public void testDistinctOfNormalSourceWithKeySelectorAndComparatorAndTwoSubscrip @Test public void testDistinctOfSourceWithNulls() { - Observable src = from(null, "a", "a", null, null, "b", null); - create(distinct(src)).subscribe(w); + Observable src = Observable.from(null, "a", "a", null, null, "b", null); + Observable.create(distinct(src)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext(null); @@ -169,8 +167,8 @@ public void testDistinctOfSourceWithNulls() { @Test public void testDistinctOfSourceWithExceptionsFromKeySelector() { - Observable src = from("a", "b", null, "c"); - create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + Observable src = Observable.from("a", "b", null, "c"); + Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); diff --git a/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java b/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java index e39a123f3d..2ea0167529 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java @@ -1,23 +1,21 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.*; +import static rx.operators.OperationDistinctUntilChanged.*; + +import java.util.Comparator; + import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mock; + import rx.Observable; import rx.Observer; import rx.util.functions.Func1; -import java.util.Comparator; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.never; -import static org.mockito.MockitoAnnotations.initMocks; -import static rx.Observable.*; -import static rx.operators.OperationDistinctUntilChanged.distinctUntilChanged; - public class OperationDistinctUntilChangedTest { @Mock @@ -50,8 +48,8 @@ public void before() { @Test public void testDistinctUntilChangedOfNone() { - Observable src = empty(); - create(distinctUntilChanged(src)).subscribe(w); + Observable src = Observable.empty(); + Observable.create(distinctUntilChanged(src)).subscribe(w); verify(w, never()).onNext(anyString()); verify(w, never()).onError(any(Throwable.class)); @@ -60,8 +58,8 @@ public void testDistinctUntilChangedOfNone() { @Test public void testDistinctUntilChangedOfNoneWithKeySelector() { - Observable src = empty(); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + Observable src = Observable.empty(); + Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); verify(w, never()).onNext(anyString()); verify(w, never()).onError(any(Throwable.class)); @@ -70,8 +68,8 @@ public void testDistinctUntilChangedOfNoneWithKeySelector() { @Test public void testDistinctUntilChangedOfNormalSource() { - Observable src = from("a", "b", "c", "c", "c", "b", "b", "a", "e"); - create(distinctUntilChanged(src)).subscribe(w); + Observable src = Observable.from("a", "b", "c", "c", "c", "b", "b", "a", "e"); + Observable.create(distinctUntilChanged(src)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); @@ -87,8 +85,8 @@ public void testDistinctUntilChangedOfNormalSource() { @Test public void testDistinctUntilChangedOfNormalSourceWithKeySelector() { - Observable src = from("a", "b", "c", "C", "c", "B", "b", "a", "e"); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + Observable src = Observable.from("a", "b", "c", "C", "c", "B", "b", "a", "e"); + Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); @@ -104,8 +102,8 @@ public void testDistinctUntilChangedOfNormalSourceWithKeySelector() { @Test public void testDistinctUntilChangedOfSourceWithNulls() { - Observable src = from(null, "a", "a", null, null, "b", null, null); - create(distinctUntilChanged(src)).subscribe(w); + Observable src = Observable.from(null, "a", "a", null, null, "b", null, null); + Observable.create(distinctUntilChanged(src)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext(null); @@ -120,8 +118,8 @@ public void testDistinctUntilChangedOfSourceWithNulls() { @Test public void testDistinctUntilChangedOfSourceWithExceptionsFromKeySelector() { - Observable src = from("a", "b", null, "c"); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); + Observable src = Observable.from("a", "b", null, "c"); + Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); @@ -133,8 +131,8 @@ public void testDistinctUntilChangedOfSourceWithExceptionsFromKeySelector() { @Test public void testDistinctUntilChangedWithComparator() { - Observable src = from("a", "b", "c", "aa", "bb", "c", "ddd"); - create(distinctUntilChanged(src, COMPARE_LENGTH)).subscribe(w); + Observable src = Observable.from("a", "b", "c", "aa", "bb", "c", "ddd"); + Observable.create(distinctUntilChanged(src, COMPARE_LENGTH)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); inOrder.verify(w, times(1)).onNext("aa"); @@ -147,8 +145,8 @@ public void testDistinctUntilChangedWithComparator() { @Test public void testDistinctUntilChangedWithComparatorAndKeySelector() { - Observable src = from("a", "b", "x", "aa", "bb", "c", "ddd"); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); + Observable src = Observable.from("a", "b", "x", "aa", "bb", "c", "ddd"); + Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); inOrder.verify(w, times(1)).onNext("x"); @@ -161,12 +159,12 @@ public void testDistinctUntilChangedWithComparatorAndKeySelector() { @Test public void testDistinctUntilChangedWithComparatorAndKeySelectorandTwoSubscriptions() { - Observable src = from("a", "b", "x", "aa", "bb", "c", "ddd"); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); + Observable src = Observable.from("a", "b", "x", "aa", "bb", "c", "ddd"); + Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); inOrder.verify(w, times(1)).onNext("x"); - create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2); + Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2); inOrder.verify(w, times(1)).onNext("c"); inOrder.verify(w, times(1)).onNext("ddd"); inOrder.verify(w, times(1)).onCompleted(); diff --git a/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java b/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java index 344ab98578..f645fd785e 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java @@ -1,20 +1,18 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.*; +import static rx.operators.OperationFirstOrDefault.*; + import org.junit.Before; import org.junit.Test; import org.mockito.Mock; + import rx.Observable; import rx.Observer; import rx.util.functions.Func1; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.*; -import static org.mockito.MockitoAnnotations.initMocks; -import static rx.Observable.*; -import static rx.operators.OperationFirstOrDefault.firstOrDefault; - public class OperationFirstOrDefaultTest { @Mock @@ -34,8 +32,8 @@ public void before() { @Test public void testFirstOrElseOfNone() { - Observable src = empty(); - create(firstOrDefault(src, "default")).subscribe(w); + Observable src = Observable.empty(); + Observable.create(firstOrDefault(src, "default")).subscribe(w); verify(w, times(1)).onNext(anyString()); verify(w, times(1)).onNext("default"); @@ -45,8 +43,8 @@ public void testFirstOrElseOfNone() { @Test public void testFirstOrElseOfSome() { - Observable src = from("a", "b", "c"); - create(firstOrDefault(src, "default")).subscribe(w); + Observable src = Observable.from("a", "b", "c"); + Observable.create(firstOrDefault(src, "default")).subscribe(w); verify(w, times(1)).onNext(anyString()); verify(w, times(1)).onNext("a"); @@ -56,8 +54,8 @@ public void testFirstOrElseOfSome() { @Test public void testFirstOrElseWithPredicateOfNoneMatchingThePredicate() { - Observable src = from("a", "b", "c"); - create(firstOrDefault(src, IS_D, "default")).subscribe(w); + Observable src = Observable.from("a", "b", "c"); + Observable.create(firstOrDefault(src, IS_D, "default")).subscribe(w); verify(w, times(1)).onNext(anyString()); verify(w, times(1)).onNext("default"); @@ -67,8 +65,8 @@ public void testFirstOrElseWithPredicateOfNoneMatchingThePredicate() { @Test public void testFirstOrElseWithPredicateOfSome() { - Observable src = from("a", "b", "c", "d", "e", "f"); - create(firstOrDefault(src, IS_D, "default")).subscribe(w); + Observable src = Observable.from("a", "b", "c", "d", "e", "f"); + Observable.create(firstOrDefault(src, IS_D, "default")).subscribe(w); verify(w, times(1)).onNext(anyString()); verify(w, times(1)).onNext("d"); diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java index f0d8391928..0c97f00c9b 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java @@ -1,19 +1,17 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationSkipWhile.*; + import org.junit.Test; import org.mockito.InOrder; + import rx.Observable; import rx.Observer; import rx.util.functions.Func1; import rx.util.functions.Func2; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.*; -import static rx.Observable.create; -import static rx.operators.OperationSkipWhile.skipWhile; -import static rx.operators.OperationSkipWhile.skipWhileWithIndex; - public class OperationSkipWhileTest { @SuppressWarnings("unchecked") @@ -37,7 +35,7 @@ public Boolean call(Integer value, Integer index) { @Test public void testSkipWithIndex() { Observable src = Observable.from(1, 2, 3, 4, 5); - create(skipWhileWithIndex(src, INDEX_LESS_THAN_THREE)).subscribe(w); + Observable.create(skipWhileWithIndex(src, INDEX_LESS_THAN_THREE)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext(4); @@ -49,7 +47,7 @@ public void testSkipWithIndex() { @Test public void testSkipEmpty() { Observable src = Observable.empty(); - create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); verify(w, never()).onNext(anyInt()); verify(w, never()).onError(any(Throwable.class)); verify(w, times(1)).onCompleted(); @@ -58,7 +56,7 @@ public void testSkipEmpty() { @Test public void testSkipEverything() { Observable src = Observable.from(1, 2, 3, 4, 3, 2, 1); - create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); verify(w, never()).onNext(anyInt()); verify(w, never()).onError(any(Throwable.class)); verify(w, times(1)).onCompleted(); @@ -67,7 +65,7 @@ public void testSkipEverything() { @Test public void testSkipNothing() { Observable src = Observable.from(5, 3, 1); - create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext(5); @@ -80,7 +78,7 @@ public void testSkipNothing() { @Test public void testSkipSome() { Observable src = Observable.from(1, 2, 3, 4, 5, 3, 1, 5); - create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext(5); @@ -94,7 +92,7 @@ public void testSkipSome() { @Test public void testSkipError() { Observable src = Observable.from(1, 2, 42, 5, 3, 1); - create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, never()).onNext(anyInt()); From 6ec81dfcb52e1444960e7f349100f56389bdeb4a Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 4 Nov 2013 20:01:01 -0800 Subject: [PATCH 225/333] Organize, Format, Comments and Cleanup Also included comment => /* package accessible for unit tests */ for private classes made package public to enable unit testing. --- rxjava-core/src/main/java/rx/Observable.java | 204 +-- rxjava-core/src/main/java/rx/Observer.java | 16 +- rxjava-core/src/main/java/rx/Scheduler.java | 8 +- .../concurrency/CurrentThreadScheduler.java | 16 +- .../rx/concurrency/ExecutorScheduler.java | 8 +- .../rx/concurrency/ImmediateScheduler.java | 42 +- .../java/rx/concurrency/SleepingAction.java | 12 +- .../rx/observables/BlockingObservable.java | 17 +- .../rx/observables/ConnectableObservable.java | 12 +- .../rx/observables/GroupedObservable.java | 22 +- .../java/rx/operators/ChunkedOperation.java | 2 +- .../main/java/rx/operators/OperationAll.java | 6 +- .../main/java/rx/operators/OperationAny.java | 12 +- .../java/rx/operators/OperationAverage.java | 5 +- .../java/rx/operators/OperationBuffer.java | 18 +- .../java/rx/operators/OperationCache.java | 4 +- .../rx/operators/OperationCombineLatest.java | 83 +- .../java/rx/operators/OperationConcat.java | 8 +- .../java/rx/operators/OperationDebounce.java | 6 +- .../rx/operators/OperationDefaultIfEmpty.java | 2 +- .../java/rx/operators/OperationDistinct.java | 46 +- .../OperationDistinctUntilChanged.java | 26 +- .../java/rx/operators/OperationElementAt.java | 4 +- .../java/rx/operators/OperationFinally.java | 16 +- .../rx/operators/OperationFirstOrDefault.java | 16 +- .../java/rx/operators/OperationGroupBy.java | 20 +- .../java/rx/operators/OperationInterval.java | 8 +- .../main/java/rx/operators/OperationMap.java | 12 +- .../java/rx/operators/OperationMerge.java | 8 +- .../operators/OperationMergeDelayError.java | 10 +- .../rx/operators/OperationMostRecent.java | 8 +- .../java/rx/operators/OperationMulticast.java | 8 +- .../main/java/rx/operators/OperationNext.java | 26 +- ...OperationOnErrorResumeNextViaFunction.java | 6 +- ...erationOnErrorResumeNextViaObservable.java | 8 +- .../rx/operators/OperationOnErrorReturn.java | 6 +- ...ionOnExceptionResumeNextViaObservable.java | 4 +- .../java/rx/operators/OperationParallel.java | 4 +- .../java/rx/operators/OperationRefCount.java | 6 +- .../java/rx/operators/OperationRetry.java | 4 +- .../java/rx/operators/OperationSample.java | 32 +- .../main/java/rx/operators/OperationScan.java | 21 +- .../main/java/rx/operators/OperationSkip.java | 4 +- .../java/rx/operators/OperationSkipLast.java | 12 +- .../java/rx/operators/OperationSkipWhile.java | 12 +- .../rx/operators/OperationSubscribeOn.java | 6 +- .../main/java/rx/operators/OperationSum.java | 1 + .../java/rx/operators/OperationSwitch.java | 21 +- .../rx/operators/OperationSynchronize.java | 6 +- .../main/java/rx/operators/OperationTake.java | 4 +- .../java/rx/operators/OperationTakeLast.java | 8 +- .../java/rx/operators/OperationTakeWhile.java | 4 +- .../rx/operators/OperationThrottleFirst.java | 6 +- .../rx/operators/OperationTimeInterval.java | 6 +- .../java/rx/operators/OperationTimeout.java | 16 +- .../rx/operators/OperationToIterator.java | 1 + .../OperationToObservableFuture.java | 8 +- .../operators/OperationToObservableList.java | 9 +- .../OperationToObservableSortedList.java | 12 +- .../java/rx/operators/OperationWindow.java | 18 +- .../main/java/rx/operators/OperationZip.java | 23 +- .../operators/SafeObservableSubscription.java | 7 +- .../java/rx/operators/ScheduledObserver.java | 3 +- .../rx/operators/SynchronizedObserver.java | 2 +- .../src/main/java/rx/package-info.java | 16 +- .../main/java/rx/plugins/RxJavaPlugins.java | 2 +- .../main/java/rx/subjects/AsyncSubject.java | 19 +- .../java/rx/subjects/BehaviorSubject.java | 24 +- .../main/java/rx/subjects/PublishSubject.java | 8 +- .../main/java/rx/subjects/ReplaySubject.java | 18 +- .../java/rx/subjects/UnsubscribeTester.java | 6 +- .../subscriptions/CompositeSubscription.java | 6 +- .../rx/subscriptions/SerialSubscription.java | 8 +- .../java/rx/subscriptions/Subscriptions.java | 6 +- .../src/main/java/rx/util/Closings.java | 3 +- .../src/main/java/rx/util/Openings.java | 3 +- .../src/main/java/rx/util/TimeInterval.java | 10 +- .../src/main/java/rx/util/Timestamped.java | 10 +- .../java/rx/util/functions/Functions.java | 5 +- .../src/main/java/rx/util/functions/Not.java | 12 +- rxjava-core/src/test/java/README.md | 6 - rxjava-core/src/test/java/rx/ConcatTests.java | 8 +- .../src/test/java/rx/IntervalDemo.java | 60 +- rxjava-core/src/test/java/rx/MergeTests.java | 9 +- .../src/test/java/rx/ObservableTests.java | 9 +- .../src/test/java/rx/RefCountTests.java | 18 +- .../src/test/java/rx/SchedulersTest.java | 910 ++++++------ .../src/test/java/rx/StartWithTests.java | 2 +- .../src/test/java/rx/TimeoutTests.java | 21 +- .../CurrentThreadSchedulerTest.java | 229 ++- .../concurrency/ImmediateSchedulerTest.java | 103 +- .../observables/BlockingObservableTest.java | 447 +++--- .../java/rx/operators/OperationAllTest.java | 149 +- .../java/rx/operators/OperationAnyTest.java | 344 ++--- .../rx/operators/OperationAverageTest.java | 199 ++- .../rx/operators/OperationBufferTest.java | 637 ++++---- .../java/rx/operators/OperationCacheTest.java | 102 +- .../java/rx/operators/OperationCastTest.java | 63 +- .../operators/OperationCombineLatestTest.java | 1174 +++++++-------- .../rx/operators/OperationConcatTest.java | 940 ++++++------ .../rx/operators/OperationDebounceTest.java | 262 ++-- .../OperationDefaultIfEmptyTest.java | 71 +- .../java/rx/operators/OperationDeferTest.java | 57 +- .../operators/OperationDematerializeTest.java | 97 +- .../rx/operators/OperationDistinctTest.java | 312 ++-- .../OperationDistinctUntilChangedTest.java | 318 ++-- .../rx/operators/OperationElementAtTest.java | 199 ++- .../rx/operators/OperationFilterTest.java | 45 +- .../rx/operators/OperationFinallyTest.java | 56 +- .../OperationFirstOrDefaultTest.java | 96 +- .../rx/operators/OperationGroupByTest.java | 547 +++---- .../rx/operators/OperationIntervalTest.java | 301 ++-- .../java/rx/operators/OperationMapTest.java | 487 +++--- .../operators/OperationMaterializeTest.java | 231 +-- .../OperationMergeDelayErrorTest.java | 826 +++++------ .../java/rx/operators/OperationMergeTest.java | 723 ++++----- .../rx/operators/OperationMostRecentTest.java | 76 +- .../rx/operators/OperationMulticastTest.java | 123 +- .../java/rx/operators/OperationNextTest.java | 462 +++--- .../rx/operators/OperationObserveOnTest.java | 93 +- ...ationOnErrorResumeNextViaFunctionTest.java | 278 ++-- ...ionOnErrorResumeNextViaObservableTest.java | 207 +-- .../operators/OperationOnErrorReturnTest.java | 200 +-- ...nExceptionResumeNextViaObservableTest.java | 389 ++--- .../rx/operators/OperationParallelTest.java | 71 +- .../java/rx/operators/OperationRetryTest.java | 197 +-- .../rx/operators/OperationSampleTest.java | 136 +- .../java/rx/operators/OperationScanTest.java | 178 ++- .../rx/operators/OperationSkipLastTest.java | 177 +-- .../java/rx/operators/OperationSkipTest.java | 67 +- .../rx/operators/OperationSkipWhileTest.java | 165 ++- .../operators/OperationSubscribeOnTest.java | 42 +- .../java/rx/operators/OperationSumTest.java | 205 ++- .../rx/operators/OperationSwitchTest.java | 706 ++++----- .../operators/OperationSynchronizeTest.java | 359 ++--- .../rx/operators/OperationTakeLastTest.java | 175 +-- .../java/rx/operators/OperationTakeTest.java | 375 +++-- .../rx/operators/OperationTakeUntilTest.java | 291 ++-- .../rx/operators/OperationTakeWhileTest.java | 352 ++--- .../operators/OperationThrottleFirstTest.java | 199 +-- .../operators/OperationTimeIntervalTest.java | 88 +- .../OperationToObservableFutureTest.java | 77 +- .../OperationToObservableIterableTest.java | 40 +- .../OperationToObservableListTest.java | 87 +- .../OperationToObservableSortedListTest.java | 79 +- .../rx/operators/OperationWindowTest.java | 576 ++++---- .../java/rx/operators/OperationZipTest.java | 1101 +++++++------- .../SafeObservableSubscriptionTest.java | 24 +- .../operators/SynchronizedObserverTest.java | 1314 +++++++++-------- .../java/rx/plugins/RxJavaPluginsTest.java | 120 +- .../java/rx/subjects/AsyncSubjectTest.java | 245 ++- .../java/rx/subjects/BehaviorSubjectTest.java | 248 ++-- .../java/rx/subjects/PublishSubjectTest.java | 620 ++++---- .../java/rx/subjects/ReplaySubjectTest.java | 297 ++-- .../CompositeSubscriptionTest.java | 116 +- .../SerialSubscriptionTests.java | 11 +- .../rx/subscriptions/SubscriptionsTest.java | 23 +- .../src/test/java/rx/test/OperatorTester.java | 98 +- .../src/test/java/rx/util/RangeTest.java | 76 +- 159 files changed, 11019 insertions(+), 10949 deletions(-) delete mode 100644 rxjava-core/src/test/java/README.md diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 8cf4c951ed..8ba20d1ca2 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; @@ -287,7 +286,7 @@ private Subscription protectivelyWrapAndSubscribe(Observer o) { /** * An {@link Observer} must call an Observable's {@code subscribe} method in order to * receive items and notifications from the Observable. - * + * * @param onNext */ public Subscription subscribe(final Action1 onNext) { @@ -324,7 +323,7 @@ public void onNext(T args) { /** * An {@link Observer} must call an Observable's {@code subscribe} method in order to * receive items and notifications from the Observable. - * + * * @param onNext * @param scheduler */ @@ -369,7 +368,7 @@ public void onNext(T args) { /** * An {@link Observer} must call an Observable's {@code subscribe} method in order to * receive items and notifications from the Observable. - * + * * @param onNext * @param onError * @param scheduler @@ -381,7 +380,7 @@ public Subscription subscribe(final Action1 onNext, final Action1 Observable empty() { * its {@link Observer#onCompleted onCompleted} method with the specified scheduler. *

    * + * * @param scheduler * the scheduler to call the {@link Observer#onCompleted onCompleted} method. * @param @@ -571,7 +571,7 @@ public static Observable empty() { * @see MSDN: Observable.Empty Method (IScheduler) */ public static Observable empty(Scheduler scheduler) { - return Observable.empty().subscribeOn(scheduler); + return Observable. empty().subscribeOn(scheduler); } /** @@ -594,7 +594,7 @@ public static Observable error(Throwable exception) { * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method with the specified scheduler. *

    * - * + * * @param exception * the particular error to report * @param scheduler @@ -626,7 +626,7 @@ public static Observable error(Throwable exception, Scheduler scheduler) public static Observable from(Iterable iterable) { return create(OperationToObservableIterable.toObservableIterable(iterable)); } - + /** * Converts an Array into an Observable. *

    @@ -906,7 +906,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8, t9)); } - + /** * Converts a series of items into an Observable. *

    @@ -1018,7 +1018,7 @@ public static Observable just(T value) { * Returns an Observable that emits a single item and then completes on a specified scheduler. *

    * This is a scheduler version of {@link Observable#just(Object)}. - * + * * @param value * the item to pass to the {@link Observer}'s {@link Observer#onNext onNext} method * @param scheduler @@ -1049,7 +1049,7 @@ public static Observable just(T value, Scheduler scheduler) { public static Observable merge(Observable> source) { return create(OperationMerge.merge(source)); } - + /** * Flattens a series of Observables into one Observable, without any transformation. *

    @@ -1095,7 +1095,7 @@ public static Observable merge(Observable t1, Observable Observable merge(Observable t1, Observable t2, Observable t3) { return create(OperationMerge.merge(t1, t2, t3)); } - + /** * Flattens a series of Observables into one Observable, without any transformation. *

    @@ -1121,7 +1121,7 @@ public static Observable merge(Observable t1, Observable Observable merge(Observable t1, Observable t2, Observable t3, Observable t4) { return create(OperationMerge.merge(t1, t2, t3, t4)); } - + /** * Flattens a series of Observables into one Observable, without any transformation. *

    @@ -1149,7 +1149,7 @@ public static Observable merge(Observable t1, Observable Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { return create(OperationMerge.merge(t1, t2, t3, t4, t5)); } - + /** * Flattens a series of Observables into one Observable, without any transformation. *

    @@ -1179,7 +1179,7 @@ public static Observable merge(Observable t1, Observable Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6)); } - + /** * Flattens a series of Observables into one Observable, without any transformation. *

    @@ -1211,7 +1211,7 @@ public static Observable merge(Observable t1, Observable Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6, t7)); } - + /** * Flattens a series of Observables into one Observable, without any transformation. *

    @@ -1245,7 +1245,7 @@ public static Observable merge(Observable t1, Observable Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6, t7, t8)); } - + /** * Flattens a series of Observables into one Observable, without any transformation. *

    @@ -1297,7 +1297,7 @@ public static Observable merge(Observable t1, Observable Observable concat(Observable> observables) { return create(OperationConcat.concat(observables)); } - + /** * Returns an Observable that emits the items emitted by two or more Observables, one after the * other. @@ -1567,7 +1567,7 @@ public static Observable mergeDelayError(Observable Observable mergeDelayError(Observable t1, Observable t2) { return create(OperationMergeDelayError.mergeDelayError(t1, t2)); } - + /** * This behaves like {@link #merge(Observable, Observable, Observable)} except that if any of the merged Observables * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will @@ -1597,7 +1597,7 @@ public static Observable mergeDelayError(Observable t1, Obse public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3) { return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3)); } - + /** * This behaves like {@link #merge(Observable, Observable, Observable, Observable)} except that if any of the merged Observables * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will @@ -1629,7 +1629,7 @@ public static Observable mergeDelayError(Observable t1, Obse public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4) { return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4)); } - + /** * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will @@ -1663,7 +1663,7 @@ public static Observable mergeDelayError(Observable t1, Obse public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5)); } - + /** * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will @@ -1699,7 +1699,7 @@ public static Observable mergeDelayError(Observable t1, Obse public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6)); } - + /** * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will @@ -1737,7 +1737,7 @@ public static Observable mergeDelayError(Observable t1, Obse public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6, t7)); } - + /** * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will @@ -1777,7 +1777,7 @@ public static Observable mergeDelayError(Observable t1, Obse public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6, t7, t8)); } - + /** * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will @@ -1866,7 +1866,6 @@ public static Observable switchDo(Observable Observable switchOnNext(Observable> sequenceOfSequences) { return create(OperationSwitch.switchDo(sequenceOfSequences)); } - /** * Accepts an Observable and wraps it in another Observable that ensures that the resulting @@ -1894,7 +1893,7 @@ public Observable synchronize() { * A well-behaved Observable does not interleave its invocations of the {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, and {@link Observer#onError onError} methods of * its {@link Observer}s; it invokes {@code onCompleted} or {@code onError} only once; and it never invokes {@code onNext} after invoking either {@code onCompleted} or {@code onError}. * {@code synchronize} enforces this, and the Observable it returns invokes {@code onNext} and {@code onCompleted} or {@code onError} synchronously. - * + * * @param lock * The lock object to synchronize each observer call on * @return an Observable that is a chronologically well-behaved version of the source @@ -1905,18 +1904,18 @@ public Observable synchronize(Object lock) { } /** - * @deprecated Replaced with instance method. + * @deprecated Replaced with instance method. */ @Deprecated public static Observable synchronize(Observable source) { return create(OperationSynchronize.synchronize(source)); } - + /** * Emits an item each time interval (containing a sequential number). *

    * - * + * * @param interval * Interval size in time units (see below). * @param unit @@ -1927,12 +1926,12 @@ public static Observable synchronize(Observable source) { public static Observable interval(long interval, TimeUnit unit) { return create(OperationInterval.interval(interval, unit)); } - + /** * Emits an item each time interval (containing a sequential number). *

    * - * + * * @param interval * Interval size in time units (see below). * @param unit @@ -1945,7 +1944,7 @@ public static Observable interval(long interval, TimeUnit unit) { public static Observable interval(long interval, TimeUnit unit, Scheduler scheduler) { return create(OperationInterval.interval(interval, unit, scheduler)); } - + /** * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. *

    @@ -3035,8 +3034,7 @@ public Observable> window(long timespan, long timeshift, TimeUnit /** * Returns an Observable that emits the results of a function of your choosing applied to * combinations of N items emitted, in sequence, by N other Observables as provided by an Iterable. - *

    - * {@code zip} applies this function in strict sequence, so the first item emitted by the + *

    {@code zip} applies this function in strict sequence, so the first item emitted by the * new Observable will be the result of the function applied to the first item emitted by * all of the Observalbes; the second item emitted by the new Observable will be the result of * the function applied to the second item emitted by each of those Observables; and so forth. @@ -3139,7 +3137,7 @@ public Observable distinctUntilChanged(Func1 keyS public Observable distinct() { return create(OperationDistinct.distinct(this)); } - + /** * Returns an Observable that forwards all items emitted from the source Observable that are distinct according * to a key selector function. @@ -3155,7 +3153,7 @@ public Observable distinct() { public Observable distinct(Func1 keySelector) { return create(OperationDistinct.distinct(this, keySelector)); } - + /** * Returns the element at a specified index in a sequence. * @@ -3193,9 +3191,9 @@ public Observable elementAt(int index) { public Observable elementAtOrDefault(int index, T defaultValue) { return create(OperationElementAt.elementAtOrDefault(this, index, defaultValue)); } - + /** - * Returns an {@link Observable} that emits true if any element of the source {@link Observable} satisfies + * Returns an {@link Observable} that emits true if any element of the source {@link Observable} satisfies * the given condition, otherwise false. Note: always emit false if the source {@link Observable} is empty. *

    * In Rx.Net this is the any operator but renamed in RxJava to better match Java naming idioms. @@ -3209,10 +3207,10 @@ public Observable elementAtOrDefault(int index, T defaultValue) { public Observable exists(Func1 predicate) { return create(OperationAny.exists(this, predicate)); } - + /** * Determines whether an observable sequence contains a specified element. - * + * * @param element * The element to search in the sequence. * @return an Observable that emits if the element is in the source sequence. @@ -3531,8 +3529,8 @@ public Observable reduce(Func2 accumulator) { * Returns an Observable that counts the total number of elements in the source Observable. *

    * - * - * @return an Observable emitting the number of counted elements of the source Observable + * + * @return an Observable emitting the number of counted elements of the source Observable * as its single item. * @see MSDN: Observable.Count */ @@ -3544,22 +3542,22 @@ public Integer call(Integer t1, T t2) { } }); } - + /** * Returns an Observable that sums up the elements in the source Observable. *

    * - * + * * @param source - * Source observable to compute the sum of. - * @return an Observable emitting the sum of all the elements of the source Observable + * Source observable to compute the sum of. + * @return an Observable emitting the sum of all the elements of the source Observable * as its single item. * @see MSDN: Observable.Sum */ public static Observable sum(Observable source) { return OperationSum.sum(source); } - + /** * @see #sum(Observable) * @see MSDN: Observable.Sum @@ -3567,7 +3565,7 @@ public static Observable sum(Observable source) { public static Observable sumLongs(Observable source) { return OperationSum.sumLongs(source); } - + /** * @see #sum(Observable) * @see MSDN: Observable.Sum @@ -3575,7 +3573,7 @@ public static Observable sumLongs(Observable source) { public static Observable sumFloats(Observable source) { return OperationSum.sumFloats(source); } - + /** * @see #sum(Observable) * @see MSDN: Observable.Sum @@ -3583,23 +3581,23 @@ public static Observable sumFloats(Observable source) { public static Observable sumDoubles(Observable source) { return OperationSum.sumDoubles(source); } - + /** * Returns an Observable that computes the average of all elements in the source Observable. * For an empty source, it causes an ArithmeticException. *

    * - * + * * @param source - * Source observable to compute the average of. - * @return an Observable emitting the averageof all the elements of the source Observable + * Source observable to compute the average of. + * @return an Observable emitting the averageof all the elements of the source Observable * as its single item. * @see MSDN: Observable.Average */ public static Observable average(Observable source) { return OperationAverage.average(source); } - + /** * @see #average(Observable) * @see MSDN: Observable.Average @@ -3636,7 +3634,7 @@ public static Observable averageDoubles(Observable source) { public ConnectableObservable replay() { return OperationMulticast.multicast(this, ReplaySubject. create()); } - + /** * Retry subscription to origin Observable upto given retry count. *

    @@ -3668,6 +3666,7 @@ public Observable retry(int retryCount) { *

    * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. + * * @return Observable with retry logic. */ public Observable retry() { @@ -3714,11 +3713,11 @@ public Observable parallel(Func1, Observable> f) { * a {@link Scheduler} to perform the work on. * @return an Observable with the output of the {@link Func1} executed on a {@link Scheduler} */ - + public Observable parallel(final Func1, Observable> f, final Scheduler s) { return OperationParallel.parallel(this, f, s); } - + /** * Returns a {@link ConnectableObservable}, which waits until its {@link ConnectableObservable#connect connect} method is called before it begins emitting * items to those {@link Observer}s that have subscribed to it. @@ -3734,6 +3733,7 @@ public ConnectableObservable publish() { /** * Returns a {@link ConnectableObservable} that shares a single subscription that contains the last notification only. + * * @return a {@link ConnectableObservable} */ public ConnectableObservable publishLast() { @@ -3973,19 +3973,19 @@ public Observable firstOrDefault(Func1 predicate, T defau /** * Returns the elements of the specified sequence or the specified default * value in a singleton sequence if the sequence is empty. - * + * * @param defaultValue * The value to return if the sequence is empty. * @return An observable sequence that contains the specified default value * if the source is empty; otherwise, the elements of the source * itself. - * + * * @see MSDN: Observable.DefaultIfEmpty */ public Observable defaultIfEmpty(T defaultValue) { return create(OperationDefaultIfEmpty.defaultIfEmpty(this, defaultValue)); } - + /** * Returns an Observable that emits only the first num items emitted by the source * Observable. @@ -4051,7 +4051,7 @@ public Observable takeWhileWithIndex(final Func2 takeFirst() { return first(); } - + /** * Returns an Observable that emits only the very first item emitted by the source Observable * that satisfies a given condition. @@ -4068,7 +4068,7 @@ public Observable takeFirst() { public Observable takeFirst(Func1 predicate) { return first(predicate); } - + /** * Returns an Observable that emits only the last count items emitted by the source * Observable. @@ -4108,13 +4108,13 @@ public Observable takeUntil(Observable other) { * condition holds true. Emits all further source items as soon as the condition becomes false. *

    * - * + * * @param predicate * A function to test each item emitted from the source Observable for a condition. * It receives the emitted item as first parameter and the index of the emitted item as * second parameter. * @return an Observable that emits all items from the source Observable as soon as the condition - * becomes false. + * becomes false. * @see MSDN: Observable.SkipWhile */ public Observable skipWhileWithIndex(Func2 predicate) { @@ -4126,11 +4126,11 @@ public Observable skipWhileWithIndex(Func2 predi * condition holds true. Emits all further source items as soon as the condition becomes false. *

    * - * + * * @param predicate * A function to test each item emitted from the source Observable for a condition. * @return an Observable that emits all items from the source Observable as soon as the condition - * becomes false. + * becomes false. * @see MSDN: Observable.SkipWhile */ public Observable skipWhile(Func1 predicate) { @@ -4145,16 +4145,16 @@ public Observable skipWhile(Func1 predicate) { * count elements. As more elements are received, elements are taken from * the front of the queue and produced on the result sequence. This causes * elements to be delayed. - * + * * @param count * number of elements to bypass at the end of the source * sequence. * @return An observable sequence containing the source sequence elements * except for the bypassed ones at the end. - * + * * @throws IndexOutOfBoundsException * count is less than zero. - * + * * @see MSDN: Observable.SkipLast */ public Observable skipLast(int count) { @@ -4225,7 +4225,7 @@ public Observable> toSortedList(Func2 sor public Observable startWith(Iterable values) { return concat(Observable. from(values), this); } - + /** * Emit a specified set of items before beginning to emit items from the source Observable. *

    @@ -4238,7 +4238,7 @@ public Observable startWith(Iterable values) { public Observable startWith(T t1) { return concat(Observable. from(t1), this); } - + /** * Emit a specified set of items before beginning to emit items from the source Observable. *

    @@ -4253,7 +4253,7 @@ public Observable startWith(T t1) { public Observable startWith(T t1, T t2) { return concat(Observable. from(t1, t2), this); } - + /** * Emit a specified set of items before beginning to emit items from the source Observable. *

    @@ -4270,7 +4270,7 @@ public Observable startWith(T t1, T t2) { public Observable startWith(T t1, T t2, T t3) { return concat(Observable. from(t1, t2, t3), this); } - + /** * Emit a specified set of items before beginning to emit items from the source Observable. *

    @@ -4289,7 +4289,7 @@ public Observable startWith(T t1, T t2, T t3) { public Observable startWith(T t1, T t2, T t3, T t4) { return concat(Observable. from(t1, t2, t3, t4), this); } - + /** * Emit a specified set of items before beginning to emit items from the source Observable. *

    @@ -4310,7 +4310,7 @@ public Observable startWith(T t1, T t2, T t3, T t4) { public Observable startWith(T t1, T t2, T t3, T t4, T t5) { return concat(Observable. from(t1, t2, t3, t4, t5), this); } - + /** * Emit a specified set of items before beginning to emit items from the source Observable. *

    @@ -4333,7 +4333,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5) { public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { return concat(Observable. from(t1, t2, t3, t4, t5, t6), this); } - + /** * Emit a specified set of items before beginning to emit items from the source Observable. *

    @@ -4358,7 +4358,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7), this); } - + /** * Emit a specified set of items before beginning to emit items from the source Observable. *

    @@ -4385,7 +4385,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8), this); } - + /** * Emit a specified set of items before beginning to emit items from the source Observable. *

    @@ -4454,7 +4454,7 @@ public Observable> groupBy(final Func1 Observable> groupBy(final Func1 keySelector) { return create(OperationGroupBy.groupBy(this, keySelector)); } - + /** * Returns an {@link Observable} that emits true if the source {@link Observable} is empty, otherwise false. *

    @@ -4466,7 +4466,7 @@ public Observable> groupBy(final Func1 isEmpty() { return create(OperationAny.isEmpty(this)); } - + /** * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking * operators). @@ -4479,16 +4479,14 @@ public BlockingObservable toBlockingObservable() { /** * Converts the elements of an observable sequence to the specified type. - * + * * @param klass * The target class type which the elements will be converted to. - * + * * @return An observable sequence that contains each element of the source * sequence converted to the specified type. - * - * @see MSDN: - * Observable.Cast + * + * @see MSDN: Observable.Cast */ public Observable cast(final Class klass) { return create(OperationCast.cast(this, klass)); @@ -4497,14 +4495,14 @@ public Observable cast(final Class klass) { /** * Filters the elements of an observable sequence based on the specified * type. - * + * * @param klass * The class type to filter the elements in the source sequence * on. - * + * * @return An observable sequence that contains elements from the input * sequence of type klass. - * + * * @see MSDN: Observable.OfType */ public Observable ofType(final Class klass) { @@ -4517,9 +4515,9 @@ public Boolean call(T t) { /** * Ignores all values in an observable sequence and only calls onCompleted or onError method. - * + * * @return An empty observable sequence that only call onCompleted, or onError method. - * + * * @see MSDN: Observable.IgnoreElements */ public Observable ignoreElements() { @@ -4528,12 +4526,13 @@ public Observable ignoreElements() { /** * Returns either the observable sequence or an TimeoutException if timeout elapses. + * * @param timeout - * The timeout duration + * The timeout duration * @param timeUnit - * The time unit of the timeout + * The time unit of the timeout * @param scheduler - * The scheduler to run the timeout timers on. + * The scheduler to run the timeout timers on. * @return The source sequence with a TimeoutException in case of a timeout. */ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { @@ -4542,10 +4541,11 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler schedule /** * Returns either the observable sequence or an TimeoutException if timeout elapses. + * * @param timeout - * The timeout duration + * The timeout duration * @param timeUnit - * The time unit of the timeout + * The time unit of the timeout * @return The source sequence with a TimeoutException in case of a timeout. */ public Observable timeout(long timeout, TimeUnit timeUnit) { @@ -4554,7 +4554,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit) { /** * Records the time interval between consecutive elements in an observable sequence. - * + * * @return An observable sequence with time interval information on elements. * @see MSDN: Observable.TimeInterval */ @@ -4565,10 +4565,10 @@ public Observable> timeInterval() { /** * Records the time interval between consecutive elements in an observable * sequence, using the specified scheduler to compute time intervals. - * + * * @param scheduler * Scheduler used to compute time intervals. - * + * * @return An observable sequence with time interval information on elements. * @see MSDN: Observable.TimeInterval */ @@ -4582,7 +4582,7 @@ public Observable> timeInterval(Scheduler scheduler) { * For why this is being used see https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" * * NOTE: If strong reasons for not depending on package names comes up then the implementation of this method can change to looking for a marker interface. - * + * * @param o * @return {@code true} if the given function is an internal implementation, and {@code false} otherwise. */ diff --git a/rxjava-core/src/main/java/rx/Observer.java b/rxjava-core/src/main/java/rx/Observer.java index c18b3d4fde..3d35066ba4 100644 --- a/rxjava-core/src/main/java/rx/Observer.java +++ b/rxjava-core/src/main/java/rx/Observer.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -18,12 +18,12 @@ /** * Provides a mechanism for receiving push-based notifications. *

    - * After an Observer calls an {@link Observable}'s Observable.subscribe method, the {@link Observable} - * calls the Observer's onNext method to provide notifications. A well-behaved {@link Observable} will + * After an Observer calls an {@link Observable}'s Observable.subscribe method, the {@link Observable} calls the Observer's onNext method to provide notifications. A + * well-behaved {@link Observable} will * call an Observer's onCompleted closure exactly once or the Observer's onError closure exactly once. *

    * For more information see the RxJava Wiki - * + * * @param */ public interface Observer { @@ -39,7 +39,7 @@ public interface Observer { * Notifies the Observer that the {@link Observable} has experienced an error condition. *

    * If the {@link Observable} calls this closure, it will not thereafter call onNext or onCompleted. - * + * * @param e */ public void onError(Throwable e); @@ -50,7 +50,7 @@ public interface Observer { * The {@link Observable} calls this closure 1 or more times, unless it calls onError in which case this closure may never be called. *

    * The {@link Observable} will not call this closure again after it calls either onCompleted or onError. - * + * * @param args */ public void onNext(T args); diff --git a/rxjava-core/src/main/java/rx/Scheduler.java b/rxjava-core/src/main/java/rx/Scheduler.java index 3da38f41f2..b01872f226 100644 --- a/rxjava-core/src/main/java/rx/Scheduler.java +++ b/rxjava-core/src/main/java/rx/Scheduler.java @@ -15,6 +15,10 @@ */ package rx; +import java.util.Date; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + import rx.subscriptions.CompositeSubscription; import rx.subscriptions.MultipleAssignmentSubscription; import rx.subscriptions.Subscriptions; @@ -22,10 +26,6 @@ import rx.util.functions.Action1; import rx.util.functions.Func2; -import java.util.Date; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - /** * Represents an object that schedules units of work. *

    diff --git a/rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java b/rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java index a096ef06e2..9bf1f6ad91 100644 --- a/rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java +++ b/rxjava-core/src/main/java/rx/concurrency/CurrentThreadScheduler.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -15,14 +15,14 @@ */ package rx.concurrency; -import rx.Scheduler; -import rx.Subscription; -import rx.util.functions.Func2; - import java.util.PriorityQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import rx.Scheduler; +import rx.Subscription; +import rx.util.functions.Func2; + /** * Schedules work on the current thread but does not execute immediately. Work is put in a queue and executed after the current unit of work is completed. */ @@ -35,7 +35,7 @@ public static CurrentThreadScheduler getInstance() { private static final ThreadLocal> QUEUE = new ThreadLocal>(); - CurrentThreadScheduler() { + /* package accessible for unit tests */CurrentThreadScheduler() { } private final AtomicInteger counter = new AtomicInteger(0); diff --git a/rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java b/rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java index a1fd36179e..cee84eae44 100644 --- a/rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java +++ b/rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java @@ -48,7 +48,7 @@ public ExecutorScheduler(ScheduledExecutorService executor) { public Subscription schedulePeriodically(final T state, final Func2 action, long initialDelay, long period, TimeUnit unit) { if (executor instanceof ScheduledExecutorService) { final CompositeSubscription subscriptions = new CompositeSubscription(); - + ScheduledFuture f = ((ScheduledExecutorService) executor).scheduleAtFixedRate(new Runnable() { @Override public void run() { @@ -56,15 +56,15 @@ public void run() { subscriptions.add(s); } }, initialDelay, period, unit); - + subscriptions.add(Subscriptions.create(f)); return subscriptions; - + } else { return super.schedulePeriodically(state, action, initialDelay, period, unit); } } - + @Override public Subscription schedule(final T state, final Func2 action, long delayTime, TimeUnit unit) { final DiscardableAction discardableAction = new DiscardableAction(state, action); diff --git a/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java b/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java index d1d00355f4..c5a4cef9d4 100644 --- a/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java +++ b/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -15,35 +15,35 @@ */ package rx.concurrency; +import java.util.concurrent.TimeUnit; + import rx.Scheduler; import rx.Subscription; import rx.util.functions.Func2; -import java.util.concurrent.TimeUnit; - /** * Executes work immediately on the current thread. */ public final class ImmediateScheduler extends Scheduler { - private static final ImmediateScheduler INSTANCE = new ImmediateScheduler(); + private static final ImmediateScheduler INSTANCE = new ImmediateScheduler(); - public static ImmediateScheduler getInstance() { - return INSTANCE; - } + public static ImmediateScheduler getInstance() { + return INSTANCE; + } - ImmediateScheduler() { - } + /* package accessible for unit tests */ImmediateScheduler() { + } - @Override - public Subscription schedule(T state, Func2 action) { - return action.call(this, state); - } + @Override + public Subscription schedule(T state, Func2 action) { + return action.call(this, state); + } - @Override - public Subscription schedule(T state, Func2 action, long dueTime, TimeUnit unit) { - // since we are executing immediately on this thread we must cause this thread to sleep - long execTime = now() + unit.toMillis(dueTime); + @Override + public Subscription schedule(T state, Func2 action, long dueTime, TimeUnit unit) { + // since we are executing immediately on this thread we must cause this thread to sleep + long execTime = now() + unit.toMillis(dueTime); - return schedule(state, new SleepingAction(action, this, execTime)); - } + return schedule(state, new SleepingAction(action, this, execTime)); + } } diff --git a/rxjava-core/src/main/java/rx/concurrency/SleepingAction.java b/rxjava-core/src/main/java/rx/concurrency/SleepingAction.java index eea75639b8..925403e846 100644 --- a/rxjava-core/src/main/java/rx/concurrency/SleepingAction.java +++ b/rxjava-core/src/main/java/rx/concurrency/SleepingAction.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -30,16 +30,14 @@ public SleepingAction(Func2 scheduler.now()) { long delay = execTime - scheduler.now(); - if (delay> 0) { + if (delay > 0) { try { Thread.sleep(delay); - } - catch (InterruptedException e) { + } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } diff --git a/rxjava-core/src/main/java/rx/observables/BlockingObservable.java b/rxjava-core/src/main/java/rx/observables/BlockingObservable.java index b59f34d78c..36f372685e 100644 --- a/rxjava-core/src/main/java/rx/observables/BlockingObservable.java +++ b/rxjava-core/src/main/java/rx/observables/BlockingObservable.java @@ -15,18 +15,23 @@ */ package rx.observables; +import java.util.Iterator; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReference; + import rx.Observable; import rx.Observer; import rx.Subscription; -import rx.operators.*; +import rx.operators.OperationMostRecent; +import rx.operators.OperationNext; +import rx.operators.OperationToFuture; +import rx.operators.OperationToIterator; +import rx.operators.SafeObservableSubscription; +import rx.operators.SafeObserver; import rx.util.functions.Action1; import rx.util.functions.Func1; -import java.util.Iterator; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicReference; - /** * An extension of {@link Observable} that provides blocking operators. *

    diff --git a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java index 1595c31195..1826905eaf 100644 --- a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java +++ b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -19,7 +19,6 @@ import rx.Observer; import rx.Subscription; import rx.operators.OperationRefCount; -import rx.util.functions.Func1; /** * A ConnectableObservable resembles an ordinary {@link Observable}, except that it does not begin @@ -32,7 +31,7 @@ * For more information see * Connectable * Observable Operators at the RxJava Wiki - * + * * @param */ @@ -44,13 +43,14 @@ protected ConnectableObservable(OnSubscribeFunc onSubscribe) { /** * Call a ConnectableObservable's connect() method to instruct it to begin emitting the - * items from its underlying {@link Observable} to its {@link Observer}s. + * items from its underlying {@link Observable} to its {@link Observer}s. */ public abstract Subscription connect(); /** * Returns an observable sequence that stays connected to the source as long * as there is at least one subscription to the observable sequence. + * * @return a {@link Observable} */ public Observable refCount() { diff --git a/rxjava-core/src/main/java/rx/observables/GroupedObservable.java b/rxjava-core/src/main/java/rx/observables/GroupedObservable.java index 940d68e85b..f40e8c40af 100644 --- a/rxjava-core/src/main/java/rx/observables/GroupedObservable.java +++ b/rxjava-core/src/main/java/rx/observables/GroupedObservable.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -19,13 +19,14 @@ import rx.util.functions.Func1; /** - * An {@link Observable} that has been grouped by a key whose value can be obtained using - * {@link #getKey()}

    - * + * An {@link Observable} that has been grouped by a key whose value can be obtained using {@link #getKey()}

    + * * @see Observable#groupBy(Func1) - * - * @param the type of the key - * @param the type of the elements in the group + * + * @param + * the type of the key + * @param + * the type of the elements in the group */ public class GroupedObservable extends Observable { private final K key; @@ -37,11 +38,10 @@ public GroupedObservable(K key, OnSubscribeFunc onSubscribe) { /** * Returns the key the elements in this observable were grouped by. - * + * * @return the key the elements in this observable were grouped by */ public K getKey() { return key; } } - diff --git a/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java b/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java index d68501d122..cabc1309e6 100644 --- a/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java +++ b/rxjava-core/src/main/java/rx/operators/ChunkedOperation.java @@ -61,7 +61,7 @@ protected interface ChunkCreator { * * @param * The type of objects which this {@link Chunk} can hold. - * @param + * @param * The type of object being tracked by the {@link Chunk} */ protected abstract static class Chunk { diff --git a/rxjava-core/src/main/java/rx/operators/OperationAll.java b/rxjava-core/src/main/java/rx/operators/OperationAll.java index 908fd1f54e..f3e0f0ebcd 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAll.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAll.java @@ -15,14 +15,14 @@ */ package rx.operators; +import java.util.concurrent.atomic.AtomicBoolean; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.util.functions.Func1; -import java.util.concurrent.atomic.AtomicBoolean; - /** * Returns an Observable that emits a Boolean that indicates whether all items emitted by an * Observable satisfy a condition. @@ -41,13 +41,11 @@ private static class AllObservable implements OnSubscribeFunc { private final SafeObservableSubscription subscription = new SafeObservableSubscription(); - private AllObservable(Observable sequence, Func1 predicate) { this.sequence = sequence; this.predicate = predicate; } - @Override public Subscription onSubscribe(final Observer observer) { return subscription.wrap(sequence.subscribe(new AllObserver(observer))); diff --git a/rxjava-core/src/main/java/rx/operators/OperationAny.java b/rxjava-core/src/main/java/rx/operators/OperationAny.java index 5830fbb1f2..a06058bece 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAny.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAny.java @@ -15,16 +15,16 @@ */ package rx.operators; +import static rx.util.functions.Functions.*; + +import java.util.concurrent.atomic.AtomicBoolean; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.util.functions.Func1; -import java.util.concurrent.atomic.AtomicBoolean; - -import static rx.util.functions.Functions.alwaysTrue; - /** * Returns an {@link Observable} that emits true if any element of * an observable sequence satisfies a condition, otherwise false. @@ -41,7 +41,7 @@ public final class OperationAny { public static OnSubscribeFunc any(Observable source) { return new Any(source, alwaysTrue(), false); } - + public static OnSubscribeFunc isEmpty(Observable source) { return new Any(source, alwaysTrue(), true); } @@ -61,7 +61,7 @@ public static OnSubscribeFunc isEmpty(Observable sourc public static OnSubscribeFunc any(Observable source, Func1 predicate) { return new Any(source, predicate, false); } - + public static OnSubscribeFunc exists(Observable source, Func1 predicate) { return any(source, predicate); } diff --git a/rxjava-core/src/main/java/rx/operators/OperationAverage.java b/rxjava-core/src/main/java/rx/operators/OperationAverage.java index c130396f2a..3fbbd1f05e 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAverage.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAverage.java @@ -21,19 +21,20 @@ /** * A few operators for implementing the averaging operation. + * * @see MSDN: Observable.Average */ public final class OperationAverage { private static final class Tuple2 { private final T current; private final Integer count; - + private Tuple2(T v1, Integer v2) { current = v1; count = v2; } } - + public static Observable average(Observable source) { return source.reduce(new Tuple2(0, 0), new Func2, Integer, Tuple2>() { @Override diff --git a/rxjava-core/src/main/java/rx/operators/OperationBuffer.java b/rxjava-core/src/main/java/rx/operators/OperationBuffer.java index 03d2dc02b8..c9aba14411 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationBuffer.java +++ b/rxjava-core/src/main/java/rx/operators/OperationBuffer.java @@ -15,6 +15,9 @@ */ package rx.operators; +import java.util.List; +import java.util.concurrent.TimeUnit; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -26,9 +29,6 @@ import rx.util.functions.Func0; import rx.util.functions.Func1; -import java.util.List; -import java.util.concurrent.TimeUnit; - public final class OperationBuffer extends ChunkedOperation { private static Func0> bufferMaker() { @@ -65,7 +65,7 @@ public static OnSubscribeFunc> buffer(final Observable source, fi @Override public Subscription onSubscribe(Observer> observer) { - NonOverlappingChunks> buffers = new NonOverlappingChunks>(observer, OperationBuffer.bufferMaker()); + NonOverlappingChunks> buffers = new NonOverlappingChunks>(observer, OperationBuffer. bufferMaker()); ChunkCreator creator = new ObservableBasedSingleChunkCreator>(buffers, bufferClosingSelector); return source.subscribe(new ChunkObserver>(buffers, observer, creator)); } @@ -101,7 +101,7 @@ public static OnSubscribeFunc> buffer(final Observable source, fi return new OnSubscribeFunc>() { @Override public Subscription onSubscribe(final Observer> observer) { - OverlappingChunks> buffers = new OverlappingChunks>(observer, OperationBuffer.bufferMaker()); + OverlappingChunks> buffers = new OverlappingChunks>(observer, OperationBuffer. bufferMaker()); ChunkCreator creator = new ObservableBasedMultiChunkCreator>(buffers, bufferOpenings, bufferClosingSelector); return source.subscribe(new ChunkObserver>(buffers, observer, creator)); } @@ -156,7 +156,7 @@ public static OnSubscribeFunc> buffer(final Observable source, fi return new OnSubscribeFunc>() { @Override public Subscription onSubscribe(final Observer> observer) { - Chunks> chunks = new SizeBasedChunks>(observer, OperationBuffer.bufferMaker(), count); + Chunks> chunks = new SizeBasedChunks>(observer, OperationBuffer. bufferMaker(), count); ChunkCreator creator = new SkippingChunkCreator>(chunks, skip); return source.subscribe(new ChunkObserver>(chunks, observer, creator)); } @@ -211,7 +211,7 @@ public static OnSubscribeFunc> buffer(final Observable source, fi return new OnSubscribeFunc>() { @Override public Subscription onSubscribe(final Observer> observer) { - NonOverlappingChunks> buffers = new NonOverlappingChunks>(observer, OperationBuffer.bufferMaker()); + NonOverlappingChunks> buffers = new NonOverlappingChunks>(observer, OperationBuffer. bufferMaker()); ChunkCreator creator = new TimeBasedChunkCreator>(buffers, timespan, unit, scheduler); return source.subscribe(new ChunkObserver>(buffers, observer, creator)); } @@ -272,7 +272,7 @@ public static OnSubscribeFunc> buffer(final Observable source, fi return new OnSubscribeFunc>() { @Override public Subscription onSubscribe(final Observer> observer) { - Chunks> chunks = new TimeAndSizeBasedChunks>(observer, OperationBuffer.bufferMaker(), count, timespan, unit, scheduler); + Chunks> chunks = new TimeAndSizeBasedChunks>(observer, OperationBuffer. bufferMaker(), count, timespan, unit, scheduler); ChunkCreator creator = new SingleChunkCreator>(chunks); return source.subscribe(new ChunkObserver>(chunks, observer, creator)); } @@ -333,7 +333,7 @@ public static OnSubscribeFunc> buffer(final Observable source, fi return new OnSubscribeFunc>() { @Override public Subscription onSubscribe(final Observer> observer) { - OverlappingChunks> buffers = new TimeBasedChunks>(observer, OperationBuffer.bufferMaker(), timespan, unit, scheduler); + OverlappingChunks> buffers = new TimeBasedChunks>(observer, OperationBuffer. bufferMaker(), timespan, unit, scheduler); ChunkCreator creator = new TimeBasedChunkCreator>(buffers, timeshift, unit, scheduler); return source.subscribe(new ChunkObserver>(buffers, observer, creator)); } diff --git a/rxjava-core/src/main/java/rx/operators/OperationCache.java b/rxjava-core/src/main/java/rx/operators/OperationCache.java index 44749bbd15..71c681a179 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationCache.java +++ b/rxjava-core/src/main/java/rx/operators/OperationCache.java @@ -15,14 +15,14 @@ */ package rx.operators; +import java.util.concurrent.atomic.AtomicBoolean; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.subjects.ReplaySubject; -import java.util.concurrent.atomic.AtomicBoolean; - /** * This method has similar behavior to {@link Observable#replay()} except that this auto-subscribes * to the source Observable rather than returning a connectable Observable. diff --git a/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java b/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java index b87aaea0ba..8a581667ae 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java +++ b/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java @@ -15,12 +15,6 @@ */ package rx.operators; -import rx.Observable; -import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscription; -import rx.util.functions.*; - import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -28,6 +22,21 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Func2; +import rx.util.functions.Func3; +import rx.util.functions.Func4; +import rx.util.functions.Func5; +import rx.util.functions.Func6; +import rx.util.functions.Func7; +import rx.util.functions.Func8; +import rx.util.functions.Func9; +import rx.util.functions.FuncN; +import rx.util.functions.Functions; + /** * Returns an Observable that combines the emissions of multiple source observables. Once each * source Observable has emitted at least one item, combineLatest emits an item whenever any of @@ -41,12 +50,13 @@ public class OperationCombineLatest { /** * Combines the two given observables, emitting an event containing an aggregation of the latest values of each of the source observables * each time an event is received from one of the source observables, where the aggregation is defined by the given function. - * @param w0 - * The first source observable. - * @param w1 - * The second source observable. - * @param combineLatestFunction - * The aggregation function used to combine the source observable values. + * + * @param w0 + * The first source observable. + * @param w1 + * The second source observable. + * @param combineLatestFunction + * The aggregation function used to combine the source observable values. * @return A function from an observer to a subscription. This can be used to create an observable from. */ public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) { @@ -59,7 +69,7 @@ public static OnSubscribeFunc combineLatest(Observable OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, + public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Func3 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); a.addObserver(new CombineObserver(a, w0)); @@ -71,7 +81,7 @@ public static OnSubscribeFunc combineLatest(Observable OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, + public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Func4 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); a.addObserver(new CombineObserver(a, w0)); @@ -84,7 +94,7 @@ public static OnSubscribeFunc combineLatest(Observable OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, + public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Func5 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); a.addObserver(new CombineObserver(a, w0)); @@ -98,7 +108,7 @@ public static OnSubscribeFunc combineLatest(Observabl /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ - public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, + public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, Func6 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); a.addObserver(new CombineObserver(a, w0)); @@ -113,7 +123,7 @@ public static OnSubscribeFunc combineLatest(Obser /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ - public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, Observable w6, + public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, Observable w6, Func7 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); a.addObserver(new CombineObserver(a, w0)); @@ -129,7 +139,7 @@ public static OnSubscribeFunc combineLatest(O /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ - public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, Observable w6, Observable w7, + public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, Observable w6, Observable w7, Func8 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); a.addObserver(new CombineObserver(a, w0)); @@ -146,7 +156,8 @@ public static OnSubscribeFunc combineLate /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ - public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, Observable w6, Observable w7, Observable w8, + public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, Observable w6, Observable w7, + Observable w8, Func9 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); a.addObserver(new CombineObserver(a, w0)); @@ -161,7 +172,7 @@ public static OnSubscribeFunc combine return a; } - static class CombineObserver implements Observer { + /* package accessible for unit tests */static class CombineObserver implements Observer { final Observable w; final Aggregator a; private Subscription subscription; @@ -195,25 +206,25 @@ public void onNext(T args) { } /** - * Receive notifications from each of the observables we are reducing and execute the combineLatestFunction - * whenever we have received an event from one of the observables, as soon as each Observable has received + * Receive notifications from each of the observables we are reducing and execute the combineLatestFunction + * whenever we have received an event from one of the observables, as soon as each Observable has received * at least one event. */ - static class Aggregator implements OnSubscribeFunc { + /* package accessible for unit tests */static class Aggregator implements OnSubscribeFunc { private volatile Observer observer; private final FuncN combineLatestFunction; private final AtomicBoolean running = new AtomicBoolean(true); - + // Stores how many observers have already completed private final AtomicInteger numCompleted = new AtomicInteger(0); - + /** * The latest value from each observer. */ private final Map, Object> latestValue = new ConcurrentHashMap, Object>(); - + /** * Ordered list of observers to combine. * No synchronization is necessary as these can not be added or changed asynchronously. @@ -227,7 +238,8 @@ public Aggregator(FuncN combineLatestFunction) { /** * Receive notification of a Observer starting (meaning we should require it for aggregation) * - * @param w The observer to add. + * @param w + * The observer to add. */ void addObserver(CombineObserver w) { observers.add(w); @@ -236,7 +248,8 @@ void addObserver(CombineObserver w) { /** * Receive notification of a Observer completing its iterations. * - * @param w The observer that has completed. + * @param w + * The observer that has completed. */ void complete(CombineObserver w) { int completed = numCompleted.incrementAndGet(); @@ -280,22 +293,22 @@ void next(CombineObserver w, T arg) { // remember this as the latest value for this observer latestValue.put(w, arg); - + if (latestValue.size() < observers.size()) { - // we don't have a value yet for each observer to combine, so we don't have a combined value yet either - return; + // we don't have a value yet for each observer to combine, so we don't have a combined value yet either + return; } - + Object[] argsToCombineLatest = new Object[observers.size()]; int i = 0; for (CombineObserver _w : observers) { argsToCombineLatest[i++] = latestValue.get(_w); } - + try { R combinedValue = combineLatestFunction.call(argsToCombineLatest); observer.onNext(combinedValue); - } catch(Throwable ex) { + } catch (Throwable ex) { observer.onError(ex); } } @@ -305,7 +318,7 @@ public Subscription onSubscribe(Observer observer) { if (this.observer != null) { throw new IllegalStateException("Only one Observer can subscribe to this Observable."); } - + SafeObservableSubscription subscription = new SafeObservableSubscription(new Subscription() { @Override public void unsubscribe() { diff --git a/rxjava-core/src/main/java/rx/operators/OperationConcat.java b/rxjava-core/src/main/java/rx/operators/OperationConcat.java index 4488ddf177..9838a8073b 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationConcat.java +++ b/rxjava-core/src/main/java/rx/operators/OperationConcat.java @@ -15,15 +15,15 @@ */ package rx.operators; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; - /** * Returns an Observable that emits the items emitted by two or more Observables, one after the * other. diff --git a/rxjava-core/src/main/java/rx/operators/OperationDebounce.java b/rxjava-core/src/main/java/rx/operators/OperationDebounce.java index ebbcbc8ad1..3dbdd81f20 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDebounce.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDebounce.java @@ -15,6 +15,9 @@ */ package rx.operators; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -24,9 +27,6 @@ import rx.util.functions.Action0; import rx.util.functions.Func1; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - /** * This operation is used to filter out bursts of events. This is done by ignoring the events from an observable which are too * quickly followed up with other values. Values which are not followed up by other values within the specified timeout are published diff --git a/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java b/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java index cfe7f27a2f..7c08aca675 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java @@ -14,7 +14,7 @@ public class OperationDefaultIfEmpty { /** * Returns the elements of the specified sequence or the specified default * value in a singleton sequence if the sequence is empty. - * + * * @param source * The sequence to return the specified value for if it is empty. * @param defaultValue diff --git a/rxjava-core/src/main/java/rx/operators/OperationDistinct.java b/rxjava-core/src/main/java/rx/operators/OperationDistinct.java index 9c0bccce2c..d2db484254 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDistinct.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDistinct.java @@ -15,6 +15,12 @@ */ package rx.operators; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -24,18 +30,17 @@ import rx.util.functions.Func1; import rx.util.functions.Functions; -import java.util.*; - /** * Returns an Observable that emits all distinct items emitted by the source. * - * Be careful with this operation when using infinite or very large observables + * Be careful with this operation when using infinite or very large observables * as it has to store all distinct values it has received. */ public final class OperationDistinct { /** * Returns an Observable that emits all distinct items emitted by the source + * * @param source * The source Observable to emit the distinct items for. * @return A subscription function for creating the target Observable. @@ -43,9 +48,10 @@ public final class OperationDistinct { public static OnSubscribeFunc distinct(Observable source, Func1 keySelector) { return new Distinct(source, keySelector); } - + /** * Returns an Observable that emits all distinct items emitted by the source + * * @param source * The source Observable to emit the distinct items for. * @param equalityComparator @@ -53,11 +59,12 @@ public static OnSubscribeFunc distinct(Observable source, * @return A subscription function for creating the target Observable. */ public static OnSubscribeFunc distinct(Observable source, Comparator equalityComparator) { - return new DistinctWithComparator(source, Functions.identity(), equalityComparator); + return new DistinctWithComparator(source, Functions. identity(), equalityComparator); } - + /** * Returns an Observable that emits all distinct items emitted by the source + * * @param source * The source Observable to emit the distinct items for. * @param equalityComparator @@ -67,21 +74,22 @@ public static OnSubscribeFunc distinct(Observable source, Co public static OnSubscribeFunc distinct(Observable source, Func1 keySelector, Comparator equalityComparator) { return new DistinctWithComparator(source, keySelector, equalityComparator); } - + /** * Returns an Observable that emits all distinct items emitted by the source + * * @param source * The source Observable to emit the distinct items for. * @return A subscription function for creating the target Observable. */ public static OnSubscribeFunc distinct(Observable source) { - return new Distinct(source, Functions.identity()); + return new Distinct(source, Functions. identity()); } - + private static class Distinct implements OnSubscribeFunc { private final Observable source; private final Func1 keySelector; - + private Distinct(Observable source, Func1 keySelector) { this.source = source; this.keySelector = keySelector; @@ -91,7 +99,7 @@ private Distinct(Observable source, Func1 k public Subscription onSubscribe(final Observer observer) { final Subscription sourceSub = source.subscribe(new Observer() { private final Set emittedKeys = new HashSet(); - + @Override public void onCompleted() { observer.onCompleted(); @@ -111,7 +119,7 @@ public void onNext(T next) { } } }); - + return Subscriptions.create(new Action0() { @Override public void call() { @@ -120,12 +128,12 @@ public void call() { }); } } - + private static class DistinctWithComparator implements OnSubscribeFunc { private final Observable source; private final Func1 keySelector; private final Comparator equalityComparator; - + private DistinctWithComparator(Observable source, Func1 keySelector, Comparator equalityComparator) { this.source = source; this.keySelector = keySelector; @@ -135,10 +143,10 @@ private DistinctWithComparator(Observable source, Func1 observer) { final Subscription sourceSub = source.subscribe(new Observer() { - + // due to the totally arbitrary equality comparator, we can't use anything more efficient than lists here private final List emittedKeys = new ArrayList(); - + @Override public void onCompleted() { observer.onCompleted(); @@ -157,9 +165,9 @@ public void onNext(T next) { observer.onNext(next); } } - + private boolean alreadyEmitted(U newKey) { - for (U key: emittedKeys) { + for (U key : emittedKeys) { if (equalityComparator.compare(key, newKey) == 0) { return true; } @@ -167,7 +175,7 @@ private boolean alreadyEmitted(U newKey) { return false; } }); - + return Subscriptions.create(new Action0() { @Override public void call() { diff --git a/rxjava-core/src/main/java/rx/operators/OperationDistinctUntilChanged.java b/rxjava-core/src/main/java/rx/operators/OperationDistinctUntilChanged.java index d1ea86b653..eb1d6ed083 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDistinctUntilChanged.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDistinctUntilChanged.java @@ -15,6 +15,8 @@ */ package rx.operators; +import java.util.Comparator; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -24,8 +26,6 @@ import rx.util.functions.Func1; import rx.util.functions.Functions; -import java.util.Comparator; - /** * Returns an Observable that emits all sequentially distinct items emitted by the source. */ @@ -33,6 +33,7 @@ public final class OperationDistinctUntilChanged { /** * Returns an Observable that emits all sequentially distinct items emitted by the source. + * * @param source * The source Observable to emit the sequentially distinct items for. * @param equalityComparator @@ -40,11 +41,12 @@ public final class OperationDistinctUntilChanged { * @return A subscription function for creating the target Observable. */ public static OnSubscribeFunc distinctUntilChanged(Observable source, Comparator equalityComparator) { - return new DistinctUntilChanged(source, Functions.identity(), equalityComparator); + return new DistinctUntilChanged(source, Functions. identity(), equalityComparator); } - + /** * Returns an Observable that emits all sequentially distinct items emitted by the source. + * * @param source * The source Observable to emit the sequentially distinct items for. * @param keySelector @@ -56,9 +58,10 @@ public static OnSubscribeFunc distinctUntilChanged(Observable OnSubscribeFunc distinctUntilChanged(Observable source, Func1 keySelector, Comparator equalityComparator) { return new DistinctUntilChanged(source, keySelector, equalityComparator); } - + /** * Returns an Observable that emits all sequentially distinct items emitted by the source. + * * @param source * The source Observable to emit the sequentially distinct items for. * @param keySelector @@ -68,15 +71,16 @@ public static OnSubscribeFunc distinctUntilChanged(Observable OnSubscribeFunc distinctUntilChanged(Observable source, Func1 keySelector) { return new DistinctUntilChanged(source, keySelector, new DefaultEqualityComparator()); } - + /** * Returns an Observable that emits all sequentially distinct items emitted by the source. + * * @param source * The source Observable to emit the sequentially distinct items for. * @return A subscription function for creating the target Observable. */ public static OnSubscribeFunc distinctUntilChanged(Observable source) { - return new DistinctUntilChanged(source, Functions.identity(), new DefaultEqualityComparator()); + return new DistinctUntilChanged(source, Functions. identity(), new DefaultEqualityComparator()); } // does not define a useful ordering; it's only used for equality tests here @@ -90,12 +94,12 @@ public int compare(T t1, T t2) { } } } - + private static class DistinctUntilChanged implements OnSubscribeFunc { private final Observable source; private final Func1 keySelector; private final Comparator equalityComparator; - + private DistinctUntilChanged(Observable source, Func1 keySelector, Comparator equalityComparator) { this.source = source; this.keySelector = keySelector; @@ -107,7 +111,7 @@ public Subscription onSubscribe(final Observer observer) { final Subscription sourceSub = source.subscribe(new Observer() { private U lastEmittedKey; private boolean hasEmitted; - + @Override public void onCompleted() { observer.onCompleted(); @@ -131,7 +135,7 @@ public void onNext(T next) { } } }); - + return Subscriptions.create(new Action0() { @Override public void call() { diff --git a/rxjava-core/src/main/java/rx/operators/OperationElementAt.java b/rxjava-core/src/main/java/rx/operators/OperationElementAt.java index c1310bc013..3001043c80 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationElementAt.java +++ b/rxjava-core/src/main/java/rx/operators/OperationElementAt.java @@ -15,13 +15,13 @@ */ package rx.operators; +import java.util.concurrent.atomic.AtomicInteger; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import java.util.concurrent.atomic.AtomicInteger; - /** * Returns the element at a specified index in a sequence. */ diff --git a/rxjava-core/src/main/java/rx/operators/OperationFinally.java b/rxjava-core/src/main/java/rx/operators/OperationFinally.java index 6e4d58768a..f8237cbb20 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationFinally.java +++ b/rxjava-core/src/main/java/rx/operators/OperationFinally.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -33,14 +33,16 @@ public final class OperationFinally { /** * Call a given action when a sequence completes (with or without an - * exception). The returned observable is exactly as threadsafe as the + * exception). The returned observable is exactly as threadsafe as the * source observable. *

    * Note that "finally" is a Java reserved word and cannot be an identifier, * so we use "finallyDo". - * - * @param sequence An observable sequence of elements - * @param action An action to be taken when the sequence is complete or throws an exception + * + * @param sequence + * An observable sequence of elements + * @param action + * An action to be taken when the sequence is complete or throws an exception * @return An observable sequence with the same elements as the input. * After the last element is consumed (and {@link Observer#onCompleted} has been called), * or after an exception is thrown (and {@link Observer#onError} has been called), diff --git a/rxjava-core/src/main/java/rx/operators/OperationFirstOrDefault.java b/rxjava-core/src/main/java/rx/operators/OperationFirstOrDefault.java index 66dcb6e90d..1a37d68d73 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationFirstOrDefault.java +++ b/rxjava-core/src/main/java/rx/operators/OperationFirstOrDefault.java @@ -15,6 +15,10 @@ */ package rx.operators; +import static rx.util.functions.Functions.*; + +import java.util.concurrent.atomic.AtomicBoolean; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -23,10 +27,6 @@ import rx.util.functions.Action0; import rx.util.functions.Func1; -import java.util.concurrent.atomic.AtomicBoolean; - -import static rx.util.functions.Functions.alwaysTrue; - /** * Returns an Observable that emits the first item emitted by the source * Observable, or a default value if the source emits nothing. @@ -35,7 +35,7 @@ public final class OperationFirstOrDefault { /** * Returns an Observable that emits the first item emitted by the source - * Observable that satisfies the given condition, + * Observable that satisfies the given condition, * or a default value if the source emits no items that satisfy the given condition. * * @param source @@ -49,7 +49,7 @@ public final class OperationFirstOrDefault { public static OnSubscribeFunc firstOrDefault(Observable source, Func1 predicate, T defaultValue) { return new FirstOrElse(source, predicate, defaultValue); } - + /** * Returns an Observable that emits the first item emitted by the source * Observable, or a default value if the source emits nothing. @@ -79,7 +79,7 @@ private FirstOrElse(Observable source, Func1 pr public Subscription onSubscribe(final Observer observer) { final Subscription sourceSub = source.subscribe(new Observer() { private final AtomicBoolean hasEmitted = new AtomicBoolean(false); - + @Override public void onCompleted() { if (!hasEmitted.get()) { @@ -107,7 +107,7 @@ public void onNext(T next) { } } }); - + return Subscriptions.create(new Action0() { @Override public void call() { diff --git a/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java b/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java index e4311c5421..a30b97523f 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java +++ b/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java @@ -15,6 +15,11 @@ */ package rx.operators; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -23,11 +28,6 @@ import rx.util.functions.Func1; import rx.util.functions.Functions; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - /** * Groups the items emitted by an Observable according to a specified criterion, and emits these * grouped items as Observables, one Observable per group. @@ -161,7 +161,7 @@ private void unsubscribeKey(K key) { private static class GroupedSubject extends GroupedObservable implements Observer { static GroupedSubject create(final K key, final GroupBy parent) { - final AtomicReference> subscribedObserver = new AtomicReference>(OperationGroupBy.emptyObserver()); + final AtomicReference> subscribedObserver = new AtomicReference>(OperationGroupBy. emptyObserver()); return new GroupedSubject(key, new OnSubscribeFunc() { private final SafeObservableSubscription subscription = new SafeObservableSubscription(); @@ -177,7 +177,7 @@ public Subscription onSubscribe(Observer observer) { @Override public void unsubscribe() { // we remove the Observer so we stop emitting further events (they will be ignored if parent continues to send) - subscribedObserver.set(OperationGroupBy.emptyObserver()); + subscribedObserver.set(OperationGroupBy. emptyObserver()); // now we need to notify the parent that we're unsubscribed parent.unsubscribeKey(key); } @@ -210,18 +210,18 @@ public void onNext(T v) { } - private static Observer emptyObserver() { + private static Observer emptyObserver() { return new Observer() { @Override public void onCompleted() { // do nothing } - + @Override public void onError(Throwable e) { // do nothing } - + @Override public void onNext(T t) { // do nothing diff --git a/rxjava-core/src/main/java/rx/operators/OperationInterval.java b/rxjava-core/src/main/java/rx/operators/OperationInterval.java index 0c767eada7..7e1be94991 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationInterval.java +++ b/rxjava-core/src/main/java/rx/operators/OperationInterval.java @@ -15,6 +15,8 @@ */ package rx.operators; +import java.util.concurrent.TimeUnit; + import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; @@ -23,8 +25,6 @@ import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; -import java.util.concurrent.TimeUnit; - /** * Returns an observable sequence that produces a value after each period. * The value starts at 0 and counts up each period. @@ -55,7 +55,7 @@ private static class Interval implements OnSubscribeFunc { private final long period; private final TimeUnit unit; private final Scheduler scheduler; - + private long currentValue; private Interval(long period, TimeUnit unit, Scheduler scheduler) { @@ -73,7 +73,7 @@ public void call() { currentValue++; } }, period, period, unit); - + return Subscriptions.create(new Action0() { @Override public void call() { diff --git a/rxjava-core/src/main/java/rx/operators/OperationMap.java b/rxjava-core/src/main/java/rx/operators/OperationMap.java index f2543326f2..d78b5dc6ef 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMap.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMap.java @@ -46,11 +46,11 @@ public final class OperationMap { */ public static OnSubscribeFunc map(final Observable sequence, final Func1 func) { return mapWithIndex(sequence, new Func2() { - @Override - public R call(T value, @SuppressWarnings("unused") Integer unused) { - return func.call(value); - } - }); + @Override + public R call(T value, @SuppressWarnings("unused") Integer unused) { + return func.call(value); + } + }); } /** @@ -60,7 +60,7 @@ public R call(T value, @SuppressWarnings("unused") Integer unused) { * @param sequence * the input sequence. * @param func - * a function to apply to each item in the sequence. The function gets the index of the emitted item + * a function to apply to each item in the sequence. The function gets the index of the emitted item * as additional parameter. * @param * the type of the input sequence. diff --git a/rxjava-core/src/main/java/rx/operators/OperationMerge.java b/rxjava-core/src/main/java/rx/operators/OperationMerge.java index 4f98439292..12619e9176 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMerge.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMerge.java @@ -15,16 +15,16 @@ */ package rx.operators; +import java.util.Arrays; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.subscriptions.CompositeSubscription; -import java.util.Arrays; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; - /** * Flattens a list of Observables into one Observable sequence, without any transformation. *

    diff --git a/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java b/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java index 5f1af2ff80..dbca17522d 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java @@ -15,17 +15,17 @@ */ package rx.operators; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.util.CompositeException; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; - /** * This behaves like {@link OperationMerge} except that if any of the merged Observables notify of * an error via onError, mergeDelayError will refrain from propagating that error diff --git a/rxjava-core/src/main/java/rx/operators/OperationMostRecent.java b/rxjava-core/src/main/java/rx/operators/OperationMostRecent.java index 003b54aaad..15406aeece 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMostRecent.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMostRecent.java @@ -15,14 +15,14 @@ */ package rx.operators; -import rx.Observable; -import rx.Observer; -import rx.util.Exceptions; - import java.util.Iterator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import rx.Observable; +import rx.Observer; +import rx.util.Exceptions; + /** * Returns an Iterable that always returns the item most recently emitted by an Observable, or a * seed value if no item has yet been emitted. diff --git a/rxjava-core/src/main/java/rx/operators/OperationMulticast.java b/rxjava-core/src/main/java/rx/operators/OperationMulticast.java index b93f9917f1..b634b2dbac 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMulticast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMulticast.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -67,7 +67,6 @@ public void onNext(T args) { } } - return new Subscription() { @Override public void unsubscribe() { @@ -81,6 +80,5 @@ public void unsubscribe() { }; } - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationNext.java b/rxjava-core/src/main/java/rx/operators/OperationNext.java index a4812881e3..4f5e9dfd31 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationNext.java +++ b/rxjava-core/src/main/java/rx/operators/OperationNext.java @@ -15,17 +15,17 @@ */ package rx.operators; -import rx.Notification; -import rx.Observable; -import rx.Observer; -import rx.util.Exceptions; - import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; +import rx.Notification; +import rx.Observable; +import rx.Observer; +import rx.util.Exceptions; + /** * Returns an Iterable that blocks until the Observable emits another item, then returns that item. *

    @@ -63,17 +63,17 @@ private NextIterator(NextObserver observer) { @Override public boolean hasNext() { - if(error != null) { + if (error != null) { // If any error has already been thrown, throw it again. throw Exceptions.propagate(error); } // Since an iterator should not be used in different thread, // so we do not need any synchronization. - if(hasNext == false) { + if (hasNext == false) { // the iterator has reached the end. return false; } - if(isNextConsumed == false) { + if (isNextConsumed == false) { // next has not been used yet. return true; } @@ -83,7 +83,7 @@ public boolean hasNext() { private boolean moveToNext() { try { Notification nextNotification = observer.takeNext(); - if(nextNotification.isOnNext()) { + if (nextNotification.isOnNext()) { isNextConsumed = false; next = nextNotification.getValue(); return true; @@ -91,10 +91,10 @@ private boolean moveToNext() { // If an observable is completed or fails, // hasNext() always return false. hasNext = false; - if(nextNotification.isOnCompleted()) { + if (nextNotification.isOnCompleted()) { return false; } - if(nextNotification.isOnError()) { + if (nextNotification.isOnError()) { error = nextNotification.getThrowable(); throw Exceptions.propagate(error); } @@ -108,11 +108,11 @@ private boolean moveToNext() { @Override public T next() { - if(error != null) { + if (error != null) { // If any error has already been thrown, throw it again. throw Exceptions.propagate(error); } - if(hasNext()) { + if (hasNext()) { isNextConsumed = true; return next; } diff --git a/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaFunction.java b/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaFunction.java index 3f63463cf9..0122a8c5e1 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaFunction.java +++ b/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaFunction.java @@ -15,6 +15,9 @@ */ package rx.operators; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReference; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -22,9 +25,6 @@ import rx.util.CompositeException; import rx.util.functions.Func1; -import java.util.Arrays; -import java.util.concurrent.atomic.AtomicReference; - /** * Instruct an Observable to pass control to another Observable (the return value of a function) * rather than invoking onError if it encounters an error. diff --git a/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaObservable.java b/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaObservable.java index 8e09ce027a..6c14d3618a 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaObservable.java +++ b/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaObservable.java @@ -15,13 +15,13 @@ */ package rx.operators; +import java.util.concurrent.atomic.AtomicReference; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import java.util.concurrent.atomic.AtomicReference; - /** * Instruct an Observable to pass control to another Observable rather than invoking * onError if it encounters an error. @@ -67,7 +67,7 @@ public Subscription onSubscribe(final Observer observer) { subscription.wrap(originalSequence.subscribe(new Observer() { public void onNext(T value) { // forward the successful calls unless resumed - if (subscriptionRef.get()==subscription) + if (subscriptionRef.get() == subscription) observer.onNext(value); } @@ -92,7 +92,7 @@ public void onError(Throwable ex) { public void onCompleted() { // forward the successful calls unless resumed - if (subscriptionRef.get()==subscription) + if (subscriptionRef.get() == subscription) observer.onCompleted(); } })); diff --git a/rxjava-core/src/main/java/rx/operators/OperationOnErrorReturn.java b/rxjava-core/src/main/java/rx/operators/OperationOnErrorReturn.java index fd04d078b1..d5d6a9fbc7 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationOnErrorReturn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationOnErrorReturn.java @@ -15,6 +15,9 @@ */ package rx.operators; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReference; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -22,9 +25,6 @@ import rx.util.CompositeException; import rx.util.functions.Func1; -import java.util.Arrays; -import java.util.concurrent.atomic.AtomicReference; - /** * Instruct an Observable to emit a particular item to its Observer's onNext method * rather than invoking onError if it encounters an error. diff --git a/rxjava-core/src/main/java/rx/operators/OperationOnExceptionResumeNextViaObservable.java b/rxjava-core/src/main/java/rx/operators/OperationOnExceptionResumeNextViaObservable.java index 20e8fce867..864ba0730e 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationOnExceptionResumeNextViaObservable.java +++ b/rxjava-core/src/main/java/rx/operators/OperationOnExceptionResumeNextViaObservable.java @@ -15,13 +15,13 @@ */ package rx.operators; +import java.util.concurrent.atomic.AtomicReference; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import java.util.concurrent.atomic.AtomicReference; - /** * Instruct an Observable to pass control to another Observable rather than invoking * onError if it encounters an error of type {@link java.lang.Exception}. diff --git a/rxjava-core/src/main/java/rx/operators/OperationParallel.java b/rxjava-core/src/main/java/rx/operators/OperationParallel.java index 25a955e616..5a584ce370 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationParallel.java +++ b/rxjava-core/src/main/java/rx/operators/OperationParallel.java @@ -15,6 +15,8 @@ */ package rx.operators; +import java.util.concurrent.atomic.AtomicInteger; + import rx.Observable; import rx.Scheduler; import rx.concurrency.Schedulers; @@ -22,8 +24,6 @@ import rx.util.functions.Func0; import rx.util.functions.Func1; -import java.util.concurrent.atomic.AtomicInteger; - /** * Identifies unit of work that can be executed in parallel on a given Scheduler. */ diff --git a/rxjava-core/src/main/java/rx/operators/OperationRefCount.java b/rxjava-core/src/main/java/rx/operators/OperationRefCount.java index 4617f3ac40..d19b700afd 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationRefCount.java +++ b/rxjava-core/src/main/java/rx/operators/OperationRefCount.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. diff --git a/rxjava-core/src/main/java/rx/operators/OperationRetry.java b/rxjava-core/src/main/java/rx/operators/OperationRetry.java index 864caf3c4e..430ef53ce4 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationRetry.java +++ b/rxjava-core/src/main/java/rx/operators/OperationRetry.java @@ -16,6 +16,8 @@ * limitations under the License. */ +import java.util.concurrent.atomic.AtomicInteger; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -26,8 +28,6 @@ import rx.subscriptions.MultipleAssignmentSubscription; import rx.util.functions.Func2; -import java.util.concurrent.atomic.AtomicInteger; - public class OperationRetry { private static final int INFINITE_RETRY = -1; diff --git a/rxjava-core/src/main/java/rx/operators/OperationSample.java b/rxjava-core/src/main/java/rx/operators/OperationSample.java index 32c58b1f04..89da00e5fb 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSample.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSample.java @@ -15,6 +15,10 @@ */ package rx.operators; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -24,10 +28,6 @@ import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - /** * Returns an Observable that emits the results of sampling the items emitted by the source * Observable at a specified time interval. @@ -49,16 +49,16 @@ public static OnSubscribeFunc sample(final Observable source public static OnSubscribeFunc sample(final Observable source, long period, TimeUnit unit, Scheduler scheduler) { return new Sample(source, period, unit, scheduler); } - + private static class Sample implements OnSubscribeFunc { private final Observable source; private final long period; private final TimeUnit unit; private final Scheduler scheduler; - + private final AtomicBoolean hasValue = new AtomicBoolean(); private final AtomicReference latestValue = new AtomicReference(); - + private Sample(Observable source, long interval, TimeUnit unit, Scheduler scheduler) { this.source = source; this.period = interval; @@ -71,11 +71,13 @@ public Subscription onSubscribe(final Observer observer) { Observable clock = Observable.create(OperationInterval.interval(period, unit, scheduler)); final Subscription clockSubscription = clock.subscribe(new Observer() { @Override - public void onCompleted() { /* the clock never completes */ } - + public void onCompleted() { /* the clock never completes */ + } + @Override - public void onError(Throwable e) { /* the clock has no errors */ } - + public void onError(Throwable e) { /* the clock has no errors */ + } + @Override public void onNext(Long tick) { if (hasValue.get()) { @@ -83,27 +85,27 @@ public void onNext(Long tick) { } } }); - + final Subscription sourceSubscription = source.subscribe(new Observer() { @Override public void onCompleted() { clockSubscription.unsubscribe(); observer.onCompleted(); } - + @Override public void onError(Throwable e) { clockSubscription.unsubscribe(); observer.onError(e); } - + @Override public void onNext(T value) { latestValue.set(value); hasValue.set(true); } }); - + return Subscriptions.create(new Action0() { @Override public void call() { diff --git a/rxjava-core/src/main/java/rx/operators/OperationScan.java b/rxjava-core/src/main/java/rx/operators/OperationScan.java index 4d0cfc95d0..a5eedfee46 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationScan.java +++ b/rxjava-core/src/main/java/rx/operators/OperationScan.java @@ -46,7 +46,8 @@ public final class OperationScan { * An accumulator function to be invoked on each element from the sequence. * * @return An observable sequence whose elements are the result of accumulating the output from the list of Observables. - * @see Observable.Scan(TSource, TAccumulate) Method (IObservable(TSource), TAccumulate, Func(TAccumulate, TSource, TAccumulate)) + * @see Observable.Scan(TSource, TAccumulate) Method (IObservable(TSource), TAccumulate, Func(TAccumulate, TSource, + * TAccumulate)) */ public static OnSubscribeFunc scan(Observable sequence, R initialValue, Func2 accumulator) { return new Accumulator(sequence, initialValue, accumulator); @@ -70,18 +71,18 @@ public static OnSubscribeFunc scan(Observable sequence, Func private static class AccuWithoutInitialValue implements OnSubscribeFunc { private final Observable sequence; private final Func2 accumulatorFunction; - + private AccumulatingObserver accumulatingObserver; - + private AccuWithoutInitialValue(Observable sequence, Func2 accumulator) { this.sequence = sequence; this.accumulatorFunction = accumulator; } - + @Override public Subscription onSubscribe(final Observer observer) { return sequence.subscribe(new Observer() { - + // has to be synchronized so that the initial value is always sent only once. @Override public synchronized void onNext(T value) { @@ -92,7 +93,7 @@ public synchronized void onNext(T value) { accumulatingObserver.onNext(value); } } - + @Override public void onError(Throwable e) { observer.onError(e); @@ -105,7 +106,7 @@ public void onCompleted() { }); } } - + private static class Accumulator implements OnSubscribeFunc { private final Observable sequence; private final R initialValue; @@ -133,7 +134,7 @@ private static class AccumulatingObserver implements Observer { private AccumulatingObserver(Observer observer, R initialValue, Func2 accumulator) { this.observer = observer; this.accumulatorFunction = accumulator; - + this.acc = initialValue; } @@ -153,12 +154,12 @@ public synchronized void onNext(T value) { observer.onError(ex); } } - + @Override public void onError(Throwable e) { observer.onError(e); } - + @Override public void onCompleted() { observer.onCompleted(); diff --git a/rxjava-core/src/main/java/rx/operators/OperationSkip.java b/rxjava-core/src/main/java/rx/operators/OperationSkip.java index 63b873d826..4dc3359815 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSkip.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSkip.java @@ -15,13 +15,13 @@ */ package rx.operators; +import java.util.concurrent.atomic.AtomicInteger; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import java.util.concurrent.atomic.AtomicInteger; - /** * Returns an Observable that skips the first num items emitted by the source * Observable. diff --git a/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java b/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java index 9bd8253392..f3cb462e55 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java @@ -15,15 +15,15 @@ */ package rx.operators; +import java.util.Deque; +import java.util.LinkedList; +import java.util.concurrent.locks.ReentrantLock; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import java.util.Deque; -import java.util.LinkedList; -import java.util.concurrent.locks.ReentrantLock; - /** * Bypasses a specified number of elements at the end of an observable sequence. */ @@ -37,7 +37,7 @@ public class OperationSkipLast { * count elements. As more elements are received, elements are taken from * the front of the queue and produced on the result sequence. This causes * elements to be delayed. - * + * * @param source * the source sequence. * @param count @@ -45,7 +45,7 @@ public class OperationSkipLast { * sequence. * @return An observable sequence containing the source sequence elements * except for the bypassed ones at the end. - * + * * @throws IndexOutOfBoundsException * count is less than zero. */ diff --git a/rxjava-core/src/main/java/rx/operators/OperationSkipWhile.java b/rxjava-core/src/main/java/rx/operators/OperationSkipWhile.java index bf1528d8a2..6bc747e9bf 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSkipWhile.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSkipWhile.java @@ -15,6 +15,9 @@ */ package rx.operators; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -22,9 +25,6 @@ import rx.util.functions.Func1; import rx.util.functions.Func2; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - /** * Skips any emitted source items as long as the specified condition holds true. Emits all further source items * as soon as the condition becomes false. @@ -33,7 +33,7 @@ public final class OperationSkipWhile { public static OnSubscribeFunc skipWhileWithIndex(Observable source, Func2 predicate) { return new SkipWhile(source, predicate); } - + public static OnSubscribeFunc skipWhile(Observable source, final Func1 predicate) { return new SkipWhile(source, new Func2() { @Override @@ -48,7 +48,7 @@ private static class SkipWhile implements OnSubscribeFunc { private final Func2 predicate; private final AtomicBoolean skipping = new AtomicBoolean(true); private final AtomicInteger index = new AtomicInteger(0); - + SkipWhile(Observable source, Func2 pred) { this.source = source; this.predicate = pred; @@ -86,7 +86,7 @@ public void onNext(T next) { observer.onNext(next); } else { } - } catch(Throwable t) { + } catch (Throwable t) { observer.onError(t); } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java b/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java index f1cac4fe9d..fe7aa00216 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. diff --git a/rxjava-core/src/main/java/rx/operators/OperationSum.java b/rxjava-core/src/main/java/rx/operators/OperationSum.java index cc34f79520..fef81a2625 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSum.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSum.java @@ -20,6 +20,7 @@ /** * A few operators for implementing the sum operation. + * * @see MSDN: Observable.Sum */ public final class OperationSum { diff --git a/rxjava-core/src/main/java/rx/operators/OperationSwitch.java b/rxjava-core/src/main/java/rx/operators/OperationSwitch.java index 25d94a714a..9094a2affd 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSwitch.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSwitch.java @@ -33,14 +33,11 @@ public final class OperationSwitch { /** - * This function transforms an {@link Observable} sequence of - * {@link Observable} sequences into a single {@link Observable} sequence - * which produces values from the most recently published {@link Observable} - * . + * This function transforms an {@link Observable} sequence of {@link Observable} sequences into a single {@link Observable} sequence + * which produces values from the most recently published {@link Observable} . * * @param sequences - * The {@link Observable} sequence consisting of - * {@link Observable} sequences. + * The {@link Observable} sequence consisting of {@link Observable} sequences. * @return A {@link Func1} which does this transformation. */ public static OnSubscribeFunc switchDo(final Observable> sequences) { @@ -76,13 +73,13 @@ public Subscription onSubscribe(Observer observer) { private static class SwitchObserver implements Observer> { - private final Object gate; - private final Observer observer; - private final SafeObservableSubscription parent; + private final Object gate; + private final Observer observer; + private final SafeObservableSubscription parent; private final MultipleAssignmentSubscription child; - private long latest; - private boolean stopped; - private boolean hasLatest; + private long latest; + private boolean stopped; + private boolean hasLatest; public SwitchObserver(Observer observer, SafeObservableSubscription parent, MultipleAssignmentSubscription child) { diff --git a/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java b/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java index 4ec205f156..5ba8c0a98e 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSynchronize.java @@ -58,11 +58,11 @@ public static OnSubscribeFunc synchronize(Observable observa /** * Accepts an observable and wraps it in another observable which ensures that the resulting observable is well-behaved. * This is accomplished by acquiring a mutual-exclusion lock for the object provided as the lock parameter. - * + * * A well-behaved observable ensures onNext, onCompleted, or onError calls to its subscribers are * not interleaved, onCompleted and onError are only called once respectively, and no * onNext calls follow onCompleted and onError calls. - * + * * @param observable * @param lock * The lock object to synchronize each observer call on @@ -86,7 +86,7 @@ public Synchronize(Observable innerObservable, Object lock) { public Subscription onSubscribe(Observer observer) { SafeObservableSubscription subscription = new SafeObservableSubscription(); - if(lock == null) { + if (lock == null) { atomicObserver = new SynchronizedObserver(observer, subscription); } else { diff --git a/rxjava-core/src/main/java/rx/operators/OperationTake.java b/rxjava-core/src/main/java/rx/operators/OperationTake.java index 6448a628db..877ba4d5f3 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTake.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTake.java @@ -15,14 +15,14 @@ */ package rx.operators; +import java.util.concurrent.atomic.AtomicInteger; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.subscriptions.Subscriptions; -import java.util.concurrent.atomic.AtomicInteger; - /** * Returns an Observable that emits the first num items emitted by the source * Observable. diff --git a/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java b/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java index d608552315..d324f7ce42 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java @@ -15,15 +15,15 @@ */ package rx.operators; +import java.util.Deque; +import java.util.LinkedList; +import java.util.concurrent.locks.ReentrantLock; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import java.util.Deque; -import java.util.LinkedList; -import java.util.concurrent.locks.ReentrantLock; - /** * Returns an Observable that emits the last count items emitted by the source * Observable. diff --git a/rxjava-core/src/main/java/rx/operators/OperationTakeWhile.java b/rxjava-core/src/main/java/rx/operators/OperationTakeWhile.java index b56029bcae..479badf6a6 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTakeWhile.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTakeWhile.java @@ -15,6 +15,8 @@ */ package rx.operators; +import java.util.concurrent.atomic.AtomicInteger; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -22,8 +24,6 @@ import rx.util.functions.Func1; import rx.util.functions.Func2; -import java.util.concurrent.atomic.AtomicInteger; - /** * Returns an Observable that emits items emitted by the source Observable as long as a specified * condition is true. diff --git a/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java b/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java index ebc6bf55d9..e21617a82c 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java +++ b/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java @@ -15,6 +15,9 @@ */ package rx.operators; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -23,9 +26,6 @@ import rx.concurrency.Schedulers; import rx.util.functions.Func1; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - /** * Throttle by windowing a stream and returning the first value in each window. */ diff --git a/rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java b/rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java index 874cb9dd39..2cd1860711 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTimeInterval.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. diff --git a/rxjava-core/src/main/java/rx/operators/OperationTimeout.java b/rxjava-core/src/main/java/rx/operators/OperationTimeout.java index 03f5e515ac..3df6cd2929 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTimeout.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTimeout.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -15,6 +15,11 @@ */ package rx.operators; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + import rx.Observable; import rx.Observer; import rx.Scheduler; @@ -24,11 +29,6 @@ import rx.util.functions.Action0; import rx.util.functions.Func0; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; - public final class OperationTimeout { public static Observable.OnSubscribeFunc timeout(Observable source, long timeout, TimeUnit timeUnit, Scheduler scheduler) { return new Timeout(source, timeout, timeUnit, scheduler); diff --git a/rxjava-core/src/main/java/rx/operators/OperationToIterator.java b/rxjava-core/src/main/java/rx/operators/OperationToIterator.java index e7dcc308eb..59debafcb9 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToIterator.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToIterator.java @@ -36,6 +36,7 @@ *

    * *

    + * * @see Issue #50 */ public class OperationToIterator { diff --git a/rxjava-core/src/main/java/rx/operators/OperationToObservableFuture.java b/rxjava-core/src/main/java/rx/operators/OperationToObservableFuture.java index af2f0b1a3f..875a0dc0d8 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableFuture.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToObservableFuture.java @@ -15,14 +15,14 @@ */ package rx.operators; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; import rx.subscriptions.Subscriptions; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - /** * Converts a Future into an Observable. *

    @@ -35,7 +35,7 @@ * Observable.subscribe(Observer) does nothing. */ public class OperationToObservableFuture { - static class ToObservableFuture implements OnSubscribeFunc { + /* package accessible for unit tests */static class ToObservableFuture implements OnSubscribeFunc { private final Future that; private final Long time; private final TimeUnit unit; diff --git a/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java b/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java index c798cf8cc1..b87a49fdd1 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java @@ -15,15 +15,15 @@ */ package rx.operators; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; - /** * Returns an Observable that emits a single item, a list composed of all the items emitted by the * source Observable. @@ -56,6 +56,7 @@ public Subscription onSubscribe(final Observer> observer) { return that.subscribe(new Observer() { final ConcurrentLinkedQueue list = new ConcurrentLinkedQueue(); + public void onNext(T value) { // onNext can be concurrently executed so list must be thread-safe list.add(value); diff --git a/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java b/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java index cf3ffac636..cfc7ba35fa 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java @@ -15,18 +15,18 @@ */ package rx.operators; -import rx.Observable; -import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscription; -import rx.util.functions.Func2; - import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Func2; + /** * Return an Observable that emits the items emitted by the source Observable, in a sorted order * (each item emitted by the Observable must implement Comparable with respect to all other items diff --git a/rxjava-core/src/main/java/rx/operators/OperationWindow.java b/rxjava-core/src/main/java/rx/operators/OperationWindow.java index 61d1839f32..d7ff20e061 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationWindow.java +++ b/rxjava-core/src/main/java/rx/operators/OperationWindow.java @@ -15,6 +15,8 @@ */ package rx.operators; +import java.util.concurrent.TimeUnit; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; @@ -26,8 +28,6 @@ import rx.util.functions.Func0; import rx.util.functions.Func1; -import java.util.concurrent.TimeUnit; - public final class OperationWindow extends ChunkedOperation { public static Func0> windowMaker() { @@ -37,7 +37,7 @@ public Window call() { return new Window(); } }; - } + } /** *

    This method creates a {@link rx.util.functions.Func1} object which represents the window operation. This operation takes @@ -63,7 +63,7 @@ public static OnSubscribeFunc> window(final Observable>() { @Override public Subscription onSubscribe(final Observer> observer) { - NonOverlappingChunks> windows = new NonOverlappingChunks>(observer, OperationWindow.windowMaker()); + NonOverlappingChunks> windows = new NonOverlappingChunks>(observer, OperationWindow. windowMaker()); ChunkCreator creator = new ObservableBasedSingleChunkCreator>(windows, windowClosingSelector); return source.subscribe(new ChunkObserver>(windows, observer, creator)); } @@ -100,7 +100,7 @@ public static OnSubscribeFunc> window(final Observable>() { @Override public Subscription onSubscribe(final Observer> observer) { - OverlappingChunks> windows = new OverlappingChunks>(observer, OperationWindow.windowMaker()); + OverlappingChunks> windows = new OverlappingChunks>(observer, OperationWindow. windowMaker()); ChunkCreator creator = new ObservableBasedMultiChunkCreator>(windows, windowOpenings, windowClosingSelector); return source.subscribe(new ChunkObserver>(windows, observer, creator)); } @@ -155,7 +155,7 @@ public static OnSubscribeFunc> window(final Observable>() { @Override public Subscription onSubscribe(final Observer> observer) { - Chunks> chunks = new SizeBasedChunks>(observer, OperationWindow.windowMaker(), count); + Chunks> chunks = new SizeBasedChunks>(observer, OperationWindow. windowMaker(), count); ChunkCreator creator = new SkippingChunkCreator>(chunks, skip); return source.subscribe(new ChunkObserver>(chunks, observer, creator)); } @@ -210,7 +210,7 @@ public static OnSubscribeFunc> window(final Observable>() { @Override public Subscription onSubscribe(final Observer> observer) { - NonOverlappingChunks> windows = new NonOverlappingChunks>(observer, OperationWindow.windowMaker()); + NonOverlappingChunks> windows = new NonOverlappingChunks>(observer, OperationWindow. windowMaker()); ChunkCreator creator = new TimeBasedChunkCreator>(windows, timespan, unit, scheduler); return source.subscribe(new ChunkObserver>(windows, observer, creator)); } @@ -271,7 +271,7 @@ public static OnSubscribeFunc> window(final Observable>() { @Override public Subscription onSubscribe(final Observer> observer) { - Chunks> chunks = new TimeAndSizeBasedChunks>(observer, OperationWindow.windowMaker(), count, timespan, unit, scheduler); + Chunks> chunks = new TimeAndSizeBasedChunks>(observer, OperationWindow. windowMaker(), count, timespan, unit, scheduler); ChunkCreator creator = new SingleChunkCreator>(chunks); return source.subscribe(new ChunkObserver>(chunks, observer, creator)); } @@ -332,7 +332,7 @@ public static OnSubscribeFunc> window(final Observable>() { @Override public Subscription onSubscribe(final Observer> observer) { - OverlappingChunks> windows = new TimeBasedChunks>(observer, OperationWindow.windowMaker(), timespan, unit, scheduler); + OverlappingChunks> windows = new TimeBasedChunks>(observer, OperationWindow. windowMaker(), timespan, unit, scheduler); ChunkCreator creator = new TimeBasedChunkCreator>(windows, timeshift, unit, scheduler); return source.subscribe(new ChunkObserver>(windows, observer, creator)); } diff --git a/rxjava-core/src/main/java/rx/operators/OperationZip.java b/rxjava-core/src/main/java/rx/operators/OperationZip.java index eb07f87738..1837f6e58b 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationZip.java +++ b/rxjava-core/src/main/java/rx/operators/OperationZip.java @@ -15,15 +15,24 @@ */ package rx.operators; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; + import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.util.functions.*; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; +import rx.util.functions.Func2; +import rx.util.functions.Func3; +import rx.util.functions.Func4; +import rx.util.functions.Func5; +import rx.util.functions.Func6; +import rx.util.functions.Func7; +import rx.util.functions.Func8; +import rx.util.functions.Func9; +import rx.util.functions.FuncN; +import rx.util.functions.Functions; /** * Returns an Observable that emits the results of a function applied to sets of items emitted, in @@ -141,7 +150,7 @@ public static OnSubscribeFunc zip(Iterable> ws, F /* * ThreadSafe */ - static class ZipObserver implements Observer { + /* package accessible for unit tests */static class ZipObserver implements Observer { final Observable w; final Aggregator a; private final SafeObservableSubscription subscription = new SafeObservableSubscription(); @@ -186,7 +195,7 @@ public void onNext(T args) { * * @param */ - static class Aggregator implements OnSubscribeFunc { + /* package accessible for unit tests */static class Aggregator implements OnSubscribeFunc { private volatile SynchronizedObserver observer; private final FuncN zipFunction; diff --git a/rxjava-core/src/main/java/rx/operators/SafeObservableSubscription.java b/rxjava-core/src/main/java/rx/operators/SafeObservableSubscription.java index 69e22def56..ef33ebd3d3 100644 --- a/rxjava-core/src/main/java/rx/operators/SafeObservableSubscription.java +++ b/rxjava-core/src/main/java/rx/operators/SafeObservableSubscription.java @@ -15,10 +15,10 @@ */ package rx.operators; -import rx.Subscription; - import java.util.concurrent.atomic.AtomicReference; +import rx.Subscription; + /** * Thread-safe wrapper around Observable Subscription that ensures unsubscribe can be called only once. *

    @@ -50,7 +50,8 @@ public SafeObservableSubscription(Subscription actualSubscription) { /** * Wraps the actual subscription once it exists (if it wasn't available when constructed) * - * @param actualSubscription the wrapped subscription + * @param actualSubscription + * the wrapped subscription * @throws IllegalStateException * if trying to set more than once (or use this method after setting via constructor) */ diff --git a/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java b/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java index 20416f2235..0bfef9c2c9 100644 --- a/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java +++ b/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java @@ -36,8 +36,7 @@ private final AtomicBoolean started = new AtomicBoolean(); private final ConcurrentLinkedQueue> queue = new ConcurrentLinkedQueue>(); - - + public ScheduledObserver(CompositeSubscription s, Observer underlying, Scheduler scheduler) { this.parentSubscription = s; this.underlying = underlying; diff --git a/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java b/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java index c8b4fac812..2daef3e280 100644 --- a/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java +++ b/rxjava-core/src/main/java/rx/operators/SynchronizedObserver.java @@ -62,7 +62,7 @@ public SynchronizedObserver(Observer Observer, SafeObservableSubscrip this.subscription = subscription; this.lock = lock; } - + /** * Used when synchronizing an Observer without access to the subscription. * diff --git a/rxjava-core/src/main/java/rx/package-info.java b/rxjava-core/src/main/java/rx/package-info.java index 8af9fb51c8..fdd5084e60 100644 --- a/rxjava-core/src/main/java/rx/package-info.java +++ b/rxjava-core/src/main/java/rx/package-info.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -15,7 +15,7 @@ */ /** *

    Rx Observables

    - * + * *

    A library that enables subscribing to and composing asynchronous events and * callbacks.

    *

    The Observable/Observer interfaces and associated operators (in @@ -25,8 +25,8 @@ * More information can be found at http://msdn.microsoft.com/en-us/data/gg577609. *

    - * - * + * + * *

    Compared with the Microsoft implementation: *

      *
    • Observable == IObservable
    • @@ -36,9 +36,9 @@ *
    *

    *

    Services which intend on exposing data asynchronously and wish - * to allow reactive processing and composition can implement the - * {@link rx.Observable} interface which then allows Observers to subscribe to them + * to allow reactive processing and composition can implement the {@link rx.Observable} interface which then allows Observers to subscribe to them * and receive events.

    *

    Usage examples can be found on the {@link rx.Observable} and {@link rx.Observer} classes.

    */ package rx; + diff --git a/rxjava-core/src/main/java/rx/plugins/RxJavaPlugins.java b/rxjava-core/src/main/java/rx/plugins/RxJavaPlugins.java index 1e049732ff..2eb806dd6b 100644 --- a/rxjava-core/src/main/java/rx/plugins/RxJavaPlugins.java +++ b/rxjava-core/src/main/java/rx/plugins/RxJavaPlugins.java @@ -32,7 +32,7 @@ public class RxJavaPlugins { private final AtomicReference errorHandler = new AtomicReference(); private final AtomicReference observableExecutionHook = new AtomicReference(); - RxJavaPlugins() { + /* package accessible for unit tests */RxJavaPlugins() { } diff --git a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java index 7e4357c696..fc20aaf274 100644 --- a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -15,13 +15,13 @@ */ package rx.subjects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; + import rx.Observer; import rx.Subscription; import rx.operators.SafeObservableSubscription; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; - /** * Subject that publishes only the last event to each {@link Observer} that has subscribed when the * sequence completes. @@ -32,7 +32,7 @@ *

    *

     {@code
     
    -  // observer will receive no onNext events because the subject.onCompleted() isn't called.
    + * / observer will receive no onNext events because the subject.onCompleted() isn't called.
       AsyncSubject subject = AsyncSubject.create();
       subject.subscribe(observer);
       subject.onNext("one");
    @@ -48,15 +48,14 @@
       subject.onCompleted();
     
       } 
    - *
    + * 
      * @param 
      */
     public class AsyncSubject extends Subject {
     
    -
         /**
          * Create a new AsyncSubject
    -     *
    +     * 
          * @return a new AsyncSubject
          */
         public static  AsyncSubject create() {
    diff --git a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java
    index 5ea3c52146..55c91ed122 100644
    --- a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java
    +++ b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java
    @@ -1,12 +1,12 @@
     /**
      * Copyright 2013 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.
    @@ -15,13 +15,13 @@
      */
     package rx.subjects;
     
    +import java.util.concurrent.ConcurrentHashMap;
    +import java.util.concurrent.atomic.AtomicReference;
    +
     import rx.Observer;
     import rx.Subscription;
     import rx.operators.SafeObservableSubscription;
     
    -import java.util.concurrent.ConcurrentHashMap;
    -import java.util.concurrent.atomic.AtomicReference;
    -
     /**
      * Subject that publishes the most recent and all subsequent events to each subscribed {@link Observer}.
      * 

    @@ -31,7 +31,7 @@ *

    *

     {@code
     
    -  // observer will receive all events.
    + * / observer will receive all events.
       BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default");
       subject.subscribe(observer);
       subject.onNext("one");
    @@ -47,18 +47,16 @@
       subject.onNext("three");
     
       } 
    - *
    + * 
      * @param 
      */
     public class BehaviorSubject extends Subject {
     
         /**
    -     * Creates a {@link BehaviorSubject} which publishes the last and all subsequent events to each
    -     * {@link Observer} that subscribes to it.
    -     *
    +     * Creates a {@link BehaviorSubject} which publishes the last and all subsequent events to each {@link Observer} that subscribes to it.
    +     * 
          * @param defaultValue
    -     *            The value which will be published to any {@link Observer} as long as the
    -     *            {@link BehaviorSubject} has not yet received any events.
    +     *            The value which will be published to any {@link Observer} as long as the {@link BehaviorSubject} has not yet received any events.
          * @return the constructed {@link BehaviorSubject}.
          */
         public static  BehaviorSubject createWithDefaultValue(T defaultValue) {
    diff --git a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java
    index ca248ea231..fd32bd6e42 100644
    --- a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java
    +++ b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java
    @@ -15,14 +15,14 @@
      */
     package rx.subjects;
     
    -import rx.Observer;
    -import rx.Subscription;
    -import rx.operators.SafeObservableSubscription;
    -
     import java.util.ArrayList;
     import java.util.Collection;
     import java.util.concurrent.ConcurrentHashMap;
     
    +import rx.Observer;
    +import rx.Subscription;
    +import rx.operators.SafeObservableSubscription;
    +
     /**
      * Subject that, once and {@link Observer} has subscribed, publishes all subsequent events to the subscriber.
      * 

    diff --git a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java index e103e5fe3b..13d0cc3b57 100644 --- a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java +++ b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -15,13 +15,17 @@ */ package rx.subjects; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import rx.Observer; import rx.Subscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Func1; -import java.util.*; - /** * Subject that retains all events and will replay them to an {@link Observer} that subscribes. *

    @@ -31,7 +35,7 @@ *

    *

     {@code
     
    -  ReplaySubject subject = ReplaySubject.create();
    + * eplaySubject subject = ReplaySubject.create();
       subject.onNext("one");
       subject.onNext("two");
       subject.onNext("three");
    @@ -42,7 +46,7 @@
       subject.subscribe(observer2);
     
       } 
    - *
    + * 
      * @param 
      */
     public final class ReplaySubject extends Subject
    diff --git a/rxjava-core/src/main/java/rx/subjects/UnsubscribeTester.java b/rxjava-core/src/main/java/rx/subjects/UnsubscribeTester.java
    index 6ff50d39ee..0aaf498bc7 100644
    --- a/rxjava-core/src/main/java/rx/subjects/UnsubscribeTester.java
    +++ b/rxjava-core/src/main/java/rx/subjects/UnsubscribeTester.java
    @@ -1,12 +1,12 @@
     /**
      * Copyright 2013 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.
    diff --git a/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java b/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java
    index 480da47178..326de38471 100644
    --- a/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java
    +++ b/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java
    @@ -15,15 +15,15 @@
      */
     package rx.subscriptions;
     
    -import rx.Subscription;
    -import rx.util.CompositeException;
    -
     import java.util.ArrayList;
     import java.util.Collection;
     import java.util.List;
     import java.util.concurrent.ConcurrentHashMap;
     import java.util.concurrent.atomic.AtomicBoolean;
     
    +import rx.Subscription;
    +import rx.util.CompositeException;
    +
     /**
      * Subscription that represents a group of Subscriptions that are unsubscribed together.
      * 
    diff --git a/rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java b/rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java
    index c1235afda6..ebe084f11c 100644
    --- a/rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java
    +++ b/rxjava-core/src/main/java/rx/subscriptions/SerialSubscription.java
    @@ -1,12 +1,12 @@
     /**
      * Copyright 2013 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.
    @@ -20,7 +20,7 @@
     /**
      * Represents a subscription whose underlying subscription can be swapped for another subscription
      * which causes the previous underlying subscription to be unsubscribed.
    - *
    + * 
      * @see Rx.Net equivalent SerialDisposable
      */
     public class SerialSubscription implements Subscription {
    diff --git a/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java b/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java
    index bb85ed7a15..61fd6b3f6c 100644
    --- a/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java
    +++ b/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java
    @@ -15,12 +15,12 @@
      */
     package rx.subscriptions;
     
    +import java.util.concurrent.Future;
    +
     import rx.Subscription;
     import rx.operators.SafeObservableSubscription;
     import rx.util.functions.Action0;
     
    -import java.util.concurrent.Future;
    -
     /**
      * Helper methods and utilities for creating and working with {@link Subscription} objects
      */
    @@ -102,7 +102,7 @@ public void unsubscribe() {
         public static CompositeSubscription from(Subscription... subscriptions) {
             return new CompositeSubscription(subscriptions);
         }
    -    
    +
         /**
          * A {@link Subscription} that groups multiple Subscriptions together and unsubscribes from all of them together.
          * 
    diff --git a/rxjava-core/src/main/java/rx/util/Closings.java b/rxjava-core/src/main/java/rx/util/Closings.java
    index cc6c589283..0de43b97e9 100644
    --- a/rxjava-core/src/main/java/rx/util/Closings.java
    +++ b/rxjava-core/src/main/java/rx/util/Closings.java
    @@ -18,7 +18,8 @@
     public class Closings {
     
         public static Closing create() {
    -        return new Closing() {};
    +        return new Closing() {
    +        };
         }
     
         private Closings() {
    diff --git a/rxjava-core/src/main/java/rx/util/Openings.java b/rxjava-core/src/main/java/rx/util/Openings.java
    index 3f962ec163..30e11f72ca 100644
    --- a/rxjava-core/src/main/java/rx/util/Openings.java
    +++ b/rxjava-core/src/main/java/rx/util/Openings.java
    @@ -18,7 +18,8 @@
     public class Openings {
     
         public static Opening create() {
    -        return new Opening() {};
    +        return new Opening() {
    +        };
         }
     
         private Openings() {
    diff --git a/rxjava-core/src/main/java/rx/util/TimeInterval.java b/rxjava-core/src/main/java/rx/util/TimeInterval.java
    index 7bf1383688..d585baf1ce 100644
    --- a/rxjava-core/src/main/java/rx/util/TimeInterval.java
    +++ b/rxjava-core/src/main/java/rx/util/TimeInterval.java
    @@ -1,12 +1,12 @@
     /**
      * Copyright 2013 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.
    @@ -26,7 +26,7 @@ public TimeInterval(long intervalInMilliseconds, T value) {
     
         /**
          * Returns the interval in milliseconds.
    -     *
    +     * 
          * @return interval in milliseconds
          */
         public long getIntervalInMilliseconds() {
    @@ -35,7 +35,7 @@ public long getIntervalInMilliseconds() {
     
         /**
          * Returns the value.
    -     *
    +     * 
          * @return the value
          */
         public T getValue() {
    diff --git a/rxjava-core/src/main/java/rx/util/Timestamped.java b/rxjava-core/src/main/java/rx/util/Timestamped.java
    index 729d27ea99..d47bd2b57f 100644
    --- a/rxjava-core/src/main/java/rx/util/Timestamped.java
    +++ b/rxjava-core/src/main/java/rx/util/Timestamped.java
    @@ -1,12 +1,12 @@
     /**
      * Copyright 2013 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.
    @@ -29,7 +29,7 @@ public Timestamped(long timestampMillis, T value) {
     
         /**
          * Returns time timestamp, in milliseconds.
    -     *
    +     * 
          * @return timestamp in milliseconds
          */
         public long getTimestampMillis() {
    @@ -38,7 +38,7 @@ public long getTimestampMillis() {
     
         /**
          * Returns the value.
    -     *
    +     * 
          * @return the value
          */
         public T getValue() {
    diff --git a/rxjava-core/src/main/java/rx/util/functions/Functions.java b/rxjava-core/src/main/java/rx/util/functions/Functions.java
    index 356ce41173..91449d95bd 100644
    --- a/rxjava-core/src/main/java/rx/util/functions/Functions.java
    +++ b/rxjava-core/src/main/java/rx/util/functions/Functions.java
    @@ -317,7 +317,8 @@ public Void call(Object... args) {
          * Constructs a predicate that returns true for each input that the source
          * predicate returns false for and vice versa.
          * 
    -     * @param predicate The source predicate to negate.
    +     * @param predicate
    +     *            The source predicate to negate.
          */
         public static  Func1 not(Func1 predicate) {
             return new Not(predicate);
    @@ -348,7 +349,7 @@ public Boolean call(Object o) {
                 return true;
             }
         }
    -    
    +
         private enum AlwaysFalse implements Func1 {
             INSTANCE;
     
    diff --git a/rxjava-core/src/main/java/rx/util/functions/Not.java b/rxjava-core/src/main/java/rx/util/functions/Not.java
    index d3928f7888..b63e1dcea4 100644
    --- a/rxjava-core/src/main/java/rx/util/functions/Not.java
    +++ b/rxjava-core/src/main/java/rx/util/functions/Not.java
    @@ -16,23 +16,25 @@
     package rx.util.functions;
     
     /**
    - * Implements the negation of a predicate. 
    + * Implements the negation of a predicate.
      * 
    - * @param  The type of the single input parameter.
    + * @param 
    + *            The type of the single input parameter.
      */
     public class Not implements Func1 {
         private final Func1 predicate;
    - 
    +
         /**
          * Constructs a predicate that returns true for each input that the source
          * predicate returns false for and vice versa.
          * 
    -     * @param predicate The source predicate to negate.
    +     * @param predicate
    +     *            The source predicate to negate.
          */
         public Not(Func1 predicate) {
             this.predicate = predicate;
         }
    -    
    +
         @Override
         public Boolean call(T param) {
             return !predicate.call(param);
    diff --git a/rxjava-core/src/test/java/README.md b/rxjava-core/src/test/java/README.md
    deleted file mode 100644
    index c6a9aea6af..0000000000
    --- a/rxjava-core/src/test/java/README.md
    +++ /dev/null
    @@ -1,6 +0,0 @@
    -Not all unit tests are here, many are also embedded as inner classes of the main code (such as here: [rxjava-core/src/main/java/rx/operators](https://github.com/Netflix/RxJava/tree/master/rxjava-core/src/main/java/rx/operators)).
    -
    -* For an explanation of this design choice see 
    -Ben J. Christensen's [JUnit Tests as Inner Classes](http://benjchristensen.com/2011/10/23/junit-tests-as-inner-classes/).
    -
    -Also, each of the language adaptors has a /src/test/ folder which further testing (see Groovy for an example: [language-adaptors/rxjava-groovy/src/test](https://github.com/Netflix/RxJava/tree/master/language-adaptors/rxjava-groovy/src/test)).
    diff --git a/rxjava-core/src/test/java/rx/ConcatTests.java b/rxjava-core/src/test/java/rx/ConcatTests.java
    index 29a51d8dfe..a83e9f5a9b 100644
    --- a/rxjava-core/src/test/java/rx/ConcatTests.java
    +++ b/rxjava-core/src/test/java/rx/ConcatTests.java
    @@ -60,7 +60,7 @@ public void testConcatWithIterableOfObservable() {
             assertEquals("three", values.get(2));
             assertEquals("four", values.get(3));
         }
    -    
    +
         @Test
         public void testConcatCovariance() {
             Observable o1 = Observable. from(new HorrorMovie(), new Movie());
    @@ -80,14 +80,14 @@ public void testConcatCovariance2() {
     
             List values = Observable.concat(os).toList().toBlockingObservable().single();
         }
    -    
    +
         @Test
         public void testConcatCovariance3() {
             Observable o1 = Observable.from(new HorrorMovie(), new Movie());
             Observable o2 = Observable.from(new Media(), new HorrorMovie());
     
             List values = Observable.concat(o1, o2).toList().toBlockingObservable().single();
    -        
    +
             assertTrue(values.get(0) instanceof HorrorMovie);
             assertTrue(values.get(1) instanceof Movie);
             assertTrue(values.get(2) instanceof Media);
    @@ -112,7 +112,7 @@ public Subscription onSubscribe(Observer o) {
             Observable o2 = Observable.from(new Media(), new HorrorMovie());
     
             List values = Observable.concat(o1, o2).toList().toBlockingObservable().single();
    -        
    +
             assertTrue(values.get(0) instanceof HorrorMovie);
             assertTrue(values.get(1) instanceof Movie);
             assertTrue(values.get(2) instanceof Media);
    diff --git a/rxjava-core/src/test/java/rx/IntervalDemo.java b/rxjava-core/src/test/java/rx/IntervalDemo.java
    index ef2af97549..2d16901674 100644
    --- a/rxjava-core/src/test/java/rx/IntervalDemo.java
    +++ b/rxjava-core/src/test/java/rx/IntervalDemo.java
    @@ -10,33 +10,37 @@
     import rx.util.functions.Action0;
     import rx.util.functions.Action1;
     
    -@Ignore // since this doesn't do any automatic testing
    +@Ignore
    +// since this doesn't do any automatic testing
     public class IntervalDemo {
    -	
    -	@Test public void demoInterval() throws Exception {
    -		testLongObservable(Observable.interval(500, TimeUnit.MILLISECONDS).take(4), "demoInterval");
    -	}	
    -	
    -	public void testLongObservable(Observable o, final String testname) throws Exception {
    -		final List l = new ArrayList();
    -		Action1 onNext = new Action1() {
    -			public void call(Long i) { 
    -				l.add(i);
    -				System.out.println(testname + " got " + i);
    -			}
    -		};
    -		Action1 onError = new Action1() {
    -			public void call(Throwable t) { t.printStackTrace(); }
    -		};
    -		Action0 onComplete = new Action0() {
    -			public void call() {
    -				System.out.println(testname + " complete"); 
    -			}
    -		};
    -		o.subscribe(onNext, onError, onComplete);
    -		
    -		// need to wait, otherwise JUnit kills the thread of interval()
    -		Thread.sleep(2500);
    -	}
    -	
    +
    +    @Test
    +    public void demoInterval() throws Exception {
    +        testLongObservable(Observable.interval(500, TimeUnit.MILLISECONDS).take(4), "demoInterval");
    +    }
    +
    +    public void testLongObservable(Observable o, final String testname) throws Exception {
    +        final List l = new ArrayList();
    +        Action1 onNext = new Action1() {
    +            public void call(Long i) {
    +                l.add(i);
    +                System.out.println(testname + " got " + i);
    +            }
    +        };
    +        Action1 onError = new Action1() {
    +            public void call(Throwable t) {
    +                t.printStackTrace();
    +            }
    +        };
    +        Action0 onComplete = new Action0() {
    +            public void call() {
    +                System.out.println(testname + " complete");
    +            }
    +        };
    +        o.subscribe(onNext, onError, onComplete);
    +
    +        // need to wait, otherwise JUnit kills the thread of interval()
    +        Thread.sleep(2500);
    +    }
    +
     }
    diff --git a/rxjava-core/src/test/java/rx/MergeTests.java b/rxjava-core/src/test/java/rx/MergeTests.java
    index 11f30c7908..83a71044ef 100644
    --- a/rxjava-core/src/test/java/rx/MergeTests.java
    +++ b/rxjava-core/src/test/java/rx/MergeTests.java
    @@ -43,14 +43,14 @@ public void testMergeCovariance2() {
     
             List values = Observable.merge(os).toList().toBlockingObservable().single();
         }
    -    
    +
         @Test
         public void testMergeCovariance3() {
             Observable o1 = Observable.from(new HorrorMovie(), new Movie());
             Observable o2 = Observable.from(new Media(), new HorrorMovie());
     
             List values = Observable.merge(o1, o2).toList().toBlockingObservable().single();
    -        
    +
             assertTrue(values.get(0) instanceof HorrorMovie);
             assertTrue(values.get(1) instanceof Movie);
             assertTrue(values.get(2) instanceof Media);
    @@ -75,12 +75,11 @@ public Subscription onSubscribe(Observer o) {
             Observable o2 = Observable.from(new Media(), new HorrorMovie());
     
             List values = Observable.merge(o1, o2).toList().toBlockingObservable().single();
    -        
    +
             assertTrue(values.get(0) instanceof HorrorMovie);
             assertTrue(values.get(1) instanceof Movie);
             assertTrue(values.get(2) instanceof Media);
             assertTrue(values.get(3) instanceof HorrorMovie);
         }
    -    
    -    
    +
     }
    diff --git a/rxjava-core/src/test/java/rx/ObservableTests.java b/rxjava-core/src/test/java/rx/ObservableTests.java
    index de0c7d6e81..496f520a11 100644
    --- a/rxjava-core/src/test/java/rx/ObservableTests.java
    +++ b/rxjava-core/src/test/java/rx/ObservableTests.java
    @@ -522,7 +522,8 @@ public void call(String value) {
             // subscribe twice
             connectable.subscribe(new Action1() {
                 @Override
    -            public void call(String _) {}
    +            public void call(String _) {
    +            }
             });
     
             Subscription subscription = connectable.connect();
    @@ -745,7 +746,7 @@ public void onNext(String v) {
                 fail("It should be a NumberFormatException");
             }
         }
    -    
    +
         @Test
         public void testOfType() {
             Observable observable = Observable.from(1, "abc", false, 2L).ofType(String.class);
    @@ -770,7 +771,7 @@ public void testOfTypeWithPolymorphism() {
             l2.add(2);
     
             @SuppressWarnings("rawtypes")
    -        Observable observable = Observable.from(l1, l2, "123").ofType(List.class);
    +        Observable observable = Observable. from(l1, l2, "123").ofType(List.class);
     
             @SuppressWarnings("unchecked")
             Observer aObserver = mock(Observer.class);
    @@ -827,7 +828,7 @@ public void testContainsWithNull() {
     
         @Test
         public void testContainsWithEmptyObservable() {
    -        Observable observable = Observable.empty().contains("a");
    +        Observable observable = Observable. empty().contains("a");
     
             @SuppressWarnings("unchecked")
             Observer aObserver = mock(Observer.class);
    diff --git a/rxjava-core/src/test/java/rx/RefCountTests.java b/rxjava-core/src/test/java/rx/RefCountTests.java
    index ff99e60e1c..94b4ed50d4 100644
    --- a/rxjava-core/src/test/java/rx/RefCountTests.java
    +++ b/rxjava-core/src/test/java/rx/RefCountTests.java
    @@ -1,5 +1,13 @@
     package rx;
     
    +import static org.junit.Assert.*;
    +import static org.mockito.Mockito.*;
    +
    +import java.util.ArrayList;
    +import java.util.List;
    +import java.util.concurrent.TimeUnit;
    +import java.util.concurrent.atomic.AtomicInteger;
    +
     import org.junit.Before;
     import org.junit.Test;
     import org.mockito.MockitoAnnotations;
    @@ -9,14 +17,6 @@
     import rx.util.functions.Action0;
     import rx.util.functions.Action1;
     
    -import java.util.ArrayList;
    -import java.util.List;
    -import java.util.concurrent.TimeUnit;
    -import java.util.concurrent.atomic.AtomicInteger;
    -
    -import static org.junit.Assert.assertEquals;
    -import static org.mockito.Mockito.mock;
    -
     public class RefCountTests {
     
         @Before
    @@ -51,7 +51,7 @@ public void call() {
             second.unsubscribe();
             assertEquals(1, unsubscriptionCount.get());
         }
    -    
    +
         @Test
         public void testRefCount() {
             TestScheduler s = new TestScheduler();
    diff --git a/rxjava-core/src/test/java/rx/SchedulersTest.java b/rxjava-core/src/test/java/rx/SchedulersTest.java
    index 4150686d74..c9d97054d0 100644
    --- a/rxjava-core/src/test/java/rx/SchedulersTest.java
    +++ b/rxjava-core/src/test/java/rx/SchedulersTest.java
    @@ -1,12 +1,12 @@
     /**
      * Copyright 2013 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.
    @@ -15,9 +15,21 @@
      */
     package rx;
     
    +import static org.junit.Assert.*;
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +
    +import java.util.Date;
    +import java.util.concurrent.CountDownLatch;
    +import java.util.concurrent.TimeUnit;
    +import java.util.concurrent.atomic.AtomicBoolean;
    +import java.util.concurrent.atomic.AtomicInteger;
    +import java.util.concurrent.atomic.AtomicReference;
    +
     import org.junit.Test;
     import org.mockito.InOrder;
     import org.mockito.Mockito;
    +
     import rx.Observable.OnSubscribeFunc;
     import rx.concurrency.Schedulers;
     import rx.concurrency.TestScheduler;
    @@ -28,555 +40,543 @@
     import rx.util.functions.Func1;
     import rx.util.functions.Func2;
     
    -import java.util.Date;
    -import java.util.concurrent.CountDownLatch;
    -import java.util.concurrent.TimeUnit;
    -import java.util.concurrent.atomic.AtomicBoolean;
    -import java.util.concurrent.atomic.AtomicInteger;
    -import java.util.concurrent.atomic.AtomicReference;
    -
    -import static org.junit.Assert.*;
    -import static org.mockito.Matchers.anyLong;
    -import static org.mockito.Mockito.*;
    -
     public class SchedulersTest {
     
    -  @SuppressWarnings("unchecked")
    -  // mocking is unchecked, unfortunately
    -  @Test
    -  public void testPeriodicScheduling() {
    -    final Func1 calledOp = mock(Func1.class);
    +    @SuppressWarnings("unchecked")
    +    // mocking is unchecked, unfortunately
    +    @Test
    +    public void testPeriodicScheduling() {
    +        final Func1 calledOp = mock(Func1.class);
     
    -    final TestScheduler scheduler = new TestScheduler();
    -    Subscription subscription = scheduler.schedulePeriodically(new Action0() {
    -      @Override
    -      public void call() {
    -        System.out.println(scheduler.now());
    -        calledOp.call(scheduler.now());
    -      }
    -    }, 1, 2, TimeUnit.SECONDS);
    -
    -    verify(calledOp, never()).call(anyLong());
    +        final TestScheduler scheduler = new TestScheduler();
    +        Subscription subscription = scheduler.schedulePeriodically(new Action0() {
    +            @Override
    +            public void call() {
    +                System.out.println(scheduler.now());
    +                calledOp.call(scheduler.now());
    +            }
    +        }, 1, 2, TimeUnit.SECONDS);
     
    -    InOrder inOrder = Mockito.inOrder(calledOp);
    -
    -    scheduler.advanceTimeBy(999L, TimeUnit.MILLISECONDS);
    -    inOrder.verify(calledOp, never()).call(anyLong());
    +        verify(calledOp, never()).call(anyLong());
     
    -    scheduler.advanceTimeBy(1L, TimeUnit.MILLISECONDS);
    -    inOrder.verify(calledOp, times(1)).call(1000L);
    -
    -    scheduler.advanceTimeBy(1999L, TimeUnit.MILLISECONDS);
    -    inOrder.verify(calledOp, never()).call(3000L);
    -
    -    scheduler.advanceTimeBy(1L, TimeUnit.MILLISECONDS);
    -    inOrder.verify(calledOp, times(1)).call(3000L);
    -
    -    scheduler.advanceTimeBy(5L, TimeUnit.SECONDS);
    -    inOrder.verify(calledOp, times(1)).call(5000L);
    -    inOrder.verify(calledOp, times(1)).call(7000L);
    -
    -    subscription.unsubscribe();
    -    scheduler.advanceTimeBy(11L, TimeUnit.SECONDS);
    -    inOrder.verify(calledOp, never()).call(anyLong());
    -  }
    -
    -  @Test
    -  public void testComputationThreadPool1() {
    -
    -    Observable o1 = Observable.from(1, 2, 3, 4, 5);
    -    Observable o2 = Observable.from(6, 7, 8, 9, 10);
    -    Observable o = Observable.merge(o1, o2).map(new Func1() {
    -
    -      @Override
    -      public String call(Integer t) {
    -        assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool"));
    -        return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    -      }
    -    });
    +        InOrder inOrder = Mockito.inOrder(calledOp);
     
    -    o.subscribeOn(Schedulers.threadPoolForComputation()).toBlockingObservable().forEach(new Action1() {
    -
    -      @Override
    -      public void call(String t) {
    -        System.out.println("t: " + t);
    -      }
    -    });
    -  }
    -
    -  @Test
    -  public void testIOThreadPool1() {
    +        scheduler.advanceTimeBy(999L, TimeUnit.MILLISECONDS);
    +        inOrder.verify(calledOp, never()).call(anyLong());
     
    -    Observable o1 = Observable.from(1, 2, 3, 4, 5);
    -    Observable o2 = Observable.from(6, 7, 8, 9, 10);
    -    Observable o = Observable.merge(o1, o2).map(new Func1() {
    +        scheduler.advanceTimeBy(1L, TimeUnit.MILLISECONDS);
    +        inOrder.verify(calledOp, times(1)).call(1000L);
     
    -      @Override
    -      public String call(Integer t) {
    -        assertTrue(Thread.currentThread().getName().startsWith("RxIOThreadPool"));
    -        return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    -      }
    -    });
    -
    -    o.subscribeOn(Schedulers.threadPoolForIO()).toBlockingObservable().forEach(new Action1() {
    -
    -      @Override
    -      public void call(String t) {
    -        System.out.println("t: " + t);
    -      }
    -    });
    -  }
    -
    -  @Test
    -  public void testMergeWithoutScheduler1() {
    -
    -    final String currentThreadName = Thread.currentThread().getName();
    -
    -    Observable o1 = Observable.from(1, 2, 3, 4, 5);
    -    Observable o2 = Observable.from(6, 7, 8, 9, 10);
    -    Observable o = Observable.merge(o1, o2).map(new Func1() {
    -
    -      @Override
    -      public String call(Integer t) {
    -        assertTrue(Thread.currentThread().getName().equals(currentThreadName));
    -        return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    -      }
    -    });
    -
    -    o.toBlockingObservable().forEach(new Action1() {
    -
    -      @Override
    -      public void call(String t) {
    -        System.out.println("t: " + t);
    -      }
    -    });
    -  }
    +        scheduler.advanceTimeBy(1999L, TimeUnit.MILLISECONDS);
    +        inOrder.verify(calledOp, never()).call(3000L);
     
    -  @Test
    -  public void testMergeWithImmediateScheduler1() {
    +        scheduler.advanceTimeBy(1L, TimeUnit.MILLISECONDS);
    +        inOrder.verify(calledOp, times(1)).call(3000L);
     
    -    final String currentThreadName = Thread.currentThread().getName();
    +        scheduler.advanceTimeBy(5L, TimeUnit.SECONDS);
    +        inOrder.verify(calledOp, times(1)).call(5000L);
    +        inOrder.verify(calledOp, times(1)).call(7000L);
     
    -    Observable o1 = Observable.from(1, 2, 3, 4, 5);
    -    Observable o2 = Observable.from(6, 7, 8, 9, 10);
    -    Observable o = Observable.merge(o1, o2).subscribeOn(Schedulers.immediate()).map(new Func1() {
    +        subscription.unsubscribe();
    +        scheduler.advanceTimeBy(11L, TimeUnit.SECONDS);
    +        inOrder.verify(calledOp, never()).call(anyLong());
    +    }
     
    -      @Override
    -      public String call(Integer t) {
    -        assertTrue(Thread.currentThread().getName().equals(currentThreadName));
    -        return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    -      }
    -    });
    -
    -    o.toBlockingObservable().forEach(new Action1() {
    -
    -      @Override
    -      public void call(String t) {
    -        System.out.println("t: " + t);
    -      }
    -    });
    -  }
    -
    -  @Test
    -  public void testMergeWithCurrentThreadScheduler1() {
    -
    -    final String currentThreadName = Thread.currentThread().getName();
    -
    -    Observable o1 = Observable.from(1, 2, 3, 4, 5);
    -    Observable o2 = Observable.from(6, 7, 8, 9, 10);
    -    Observable o = Observable.merge(o1, o2).subscribeOn(Schedulers.currentThread()).map(new Func1() {
    -
    -      @Override
    -      public String call(Integer t) {
    -        assertTrue(Thread.currentThread().getName().equals(currentThreadName));
    -        return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    -      }
    -    });
    +    @Test
    +    public void testComputationThreadPool1() {
     
    -    o.toBlockingObservable().forEach(new Action1() {
    +        Observable o1 = Observable. from(1, 2, 3, 4, 5);
    +        Observable o2 = Observable. from(6, 7, 8, 9, 10);
    +        Observable o = Observable. merge(o1, o2).map(new Func1() {
     
    -      @Override
    -      public void call(String t) {
    -        System.out.println("t: " + t);
    -      }
    -    });
    -  }
    +            @Override
    +            public String call(Integer t) {
    +                assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool"));
    +                return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    +            }
    +        });
     
    -  @Test
    -  public void testMergeWithScheduler1() {
    +        o.subscribeOn(Schedulers.threadPoolForComputation()).toBlockingObservable().forEach(new Action1() {
     
    -    final String currentThreadName = Thread.currentThread().getName();
    +            @Override
    +            public void call(String t) {
    +                System.out.println("t: " + t);
    +            }
    +        });
    +    }
     
    -    Observable o1 = Observable.from(1, 2, 3, 4, 5);
    -    Observable o2 = Observable.from(6, 7, 8, 9, 10);
    -    Observable o = Observable.merge(o1, o2).subscribeOn(Schedulers.threadPoolForComputation()).map(new Func1() {
    +    @Test
    +    public void testIOThreadPool1() {
     
    -      @Override
    -      public String call(Integer t) {
    -        assertFalse(Thread.currentThread().getName().equals(currentThreadName));
    -        assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool"));
    -        return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    -      }
    -    });
    +        Observable o1 = Observable. from(1, 2, 3, 4, 5);
    +        Observable o2 = Observable. from(6, 7, 8, 9, 10);
    +        Observable o = Observable. merge(o1, o2).map(new Func1() {
    +
    +            @Override
    +            public String call(Integer t) {
    +                assertTrue(Thread.currentThread().getName().startsWith("RxIOThreadPool"));
    +                return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    +            }
    +        });
     
    -    o.toBlockingObservable().forEach(new Action1() {
    +        o.subscribeOn(Schedulers.threadPoolForIO()).toBlockingObservable().forEach(new Action1() {
     
    -      @Override
    -      public void call(String t) {
    -        System.out.println("t: " + t);
    -      }
    -    });
    -  }
    +            @Override
    +            public void call(String t) {
    +                System.out.println("t: " + t);
    +            }
    +        });
    +    }
     
    -  @Test
    -  public void testSubscribeWithScheduler1() throws InterruptedException {
    +    @Test
    +    public void testMergeWithoutScheduler1() {
     
    -    final AtomicInteger count = new AtomicInteger();
    +        final String currentThreadName = Thread.currentThread().getName();
     
    -    Observable o1 = Observable.from(1, 2, 3, 4, 5);
    +        Observable o1 = Observable. from(1, 2, 3, 4, 5);
    +        Observable o2 = Observable. from(6, 7, 8, 9, 10);
    +        Observable o = Observable. merge(o1, o2).map(new Func1() {
     
    -    o1.subscribe(new Action1() {
    +            @Override
    +            public String call(Integer t) {
    +                assertTrue(Thread.currentThread().getName().equals(currentThreadName));
    +                return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    +            }
    +        });
     
    -      @Override
    -      public void call(Integer t) {
    -        System.out.println("Thread: " + Thread.currentThread().getName());
    -        System.out.println("t: " + t);
    -        count.incrementAndGet();
    -      }
    -    });
    +        o.toBlockingObservable().forEach(new Action1() {
     
    -    // the above should be blocking so we should see a count of 5
    -    assertEquals(5, count.get());
    +            @Override
    +            public void call(String t) {
    +                System.out.println("t: " + t);
    +            }
    +        });
    +    }
     
    -    count.set(0);
    +    @Test
    +    public void testMergeWithImmediateScheduler1() {
     
    -    // now we'll subscribe with a scheduler and it should be async
    +        final String currentThreadName = Thread.currentThread().getName();
     
    -    final String currentThreadName = Thread.currentThread().getName();
    +        Observable o1 = Observable. from(1, 2, 3, 4, 5);
    +        Observable o2 = Observable. from(6, 7, 8, 9, 10);
    +        Observable o = Observable. merge(o1, o2).subscribeOn(Schedulers.immediate()).map(new Func1() {
     
    -    // latches for deterministically controlling the test below across threads
    -    final CountDownLatch latch = new CountDownLatch(5);
    -    final CountDownLatch first = new CountDownLatch(1);
    +            @Override
    +            public String call(Integer t) {
    +                assertTrue(Thread.currentThread().getName().equals(currentThreadName));
    +                return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    +            }
    +        });
     
    -    o1.subscribe(new Action1() {
    +        o.toBlockingObservable().forEach(new Action1() {
     
    -      @Override
    -      public void call(Integer t) {
    -        try {
    -          // we block the first one so we can assert this executes asynchronously with a count
    -          first.await(1000, TimeUnit.SECONDS);
    -        } catch (InterruptedException e) {
    -          throw new RuntimeException("The latch should have released if we are async.", e);
    -        }
    -        assertFalse(Thread.currentThread().getName().equals(currentThreadName));
    -        assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool"));
    -        System.out.println("Thread: " + Thread.currentThread().getName());
    -        System.out.println("t: " + t);
    -        count.incrementAndGet();
    -        latch.countDown();
    -      }
    -    }, Schedulers.threadPoolForComputation());
    -
    -    // assert we are async
    -    assertEquals(0, count.get());
    -    // release the latch so it can go forward
    -    first.countDown();
    -
    -    // wait for all 5 responses
    -    latch.await();
    -    assertEquals(5, count.get());
    -  }
    -
    -  @Test
    -  public void testRecursiveScheduler1() {
    -    Observable obs = Observable.create(new OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(final Observer observer) {
    -        return Schedulers.currentThread().schedule(0, new Func2() {
    -          @Override
    -          public Subscription call(Scheduler scheduler, Integer i) {
    -            if (i > 42) {
    -              observer.onCompleted();
    -              return Subscriptions.empty();
    +            @Override
    +            public void call(String t) {
    +                System.out.println("t: " + t);
                 }
    +        });
    +    }
     
    -            observer.onNext(i);
    +    @Test
    +    public void testMergeWithCurrentThreadScheduler1() {
     
    -            return scheduler.schedule(i + 1, this);
    -          }
    +        final String currentThreadName = Thread.currentThread().getName();
    +
    +        Observable o1 = Observable. from(1, 2, 3, 4, 5);
    +        Observable o2 = Observable. from(6, 7, 8, 9, 10);
    +        Observable o = Observable. merge(o1, o2).subscribeOn(Schedulers.currentThread()).map(new Func1() {
    +
    +            @Override
    +            public String call(Integer t) {
    +                assertTrue(Thread.currentThread().getName().equals(currentThreadName));
    +                return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    +            }
             });
    -      }
    -    });
    -
    -    final AtomicInteger lastValue = new AtomicInteger();
    -    obs.toBlockingObservable().forEach(new Action1() {
    -
    -      @Override
    -      public void call(Integer v) {
    -        System.out.println("Value: " + v);
    -        lastValue.set(v);
    -      }
    -    });
    -
    -    assertEquals(42, lastValue.get());
    -  }
    -
    -  @Test
    -  public void testRecursiveScheduler2() throws InterruptedException {
    -    // use latches instead of Thread.sleep
    -    final CountDownLatch latch = new CountDownLatch(10);
    -    final CountDownLatch completionLatch = new CountDownLatch(1);
    -
    -    Observable obs = Observable.create(new OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(final Observer observer) {
    -
    -        return Schedulers.threadPoolForComputation().schedule(new BooleanSubscription(), new Func2() {
    -          @Override
    -          public Subscription call(Scheduler scheduler, BooleanSubscription cancel) {
    -            if (cancel.isUnsubscribed()) {
    -              observer.onCompleted();
    -              completionLatch.countDown();
    -              return Subscriptions.empty();
    +
    +        o.toBlockingObservable().forEach(new Action1() {
    +
    +            @Override
    +            public void call(String t) {
    +                System.out.println("t: " + t);
                 }
    +        });
    +    }
     
    -            observer.onNext(42);
    -            latch.countDown();
    +    @Test
    +    public void testMergeWithScheduler1() {
     
    -            // this will recursively schedule this task for execution again
    -            scheduler.schedule(cancel, this);
    +        final String currentThreadName = Thread.currentThread().getName();
     
    -            return cancel;
    -          }
    +        Observable o1 = Observable. from(1, 2, 3, 4, 5);
    +        Observable o2 = Observable. from(6, 7, 8, 9, 10);
    +        Observable o = Observable. merge(o1, o2).subscribeOn(Schedulers.threadPoolForComputation()).map(new Func1() {
    +
    +            @Override
    +            public String call(Integer t) {
    +                assertFalse(Thread.currentThread().getName().equals(currentThreadName));
    +                assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool"));
    +                return "Value_" + t + "_Thread_" + Thread.currentThread().getName();
    +            }
             });
    -      }
    -    });
    -
    -    final AtomicInteger count = new AtomicInteger();
    -    final AtomicBoolean completed = new AtomicBoolean(false);
    -    Subscription subscribe = obs.subscribe(new Observer() {
    -      @Override
    -      public void onCompleted() {
    -        System.out.println("Completed");
    -        completed.set(true);
    -      }
    -
    -      @Override
    -      public void onError(Throwable e) {
    -        System.out.println("Error");
    -      }
    -
    -      @Override
    -      public void onNext(Integer args) {
    -        count.incrementAndGet();
    -        System.out.println(args);
    -      }
    -    });
    -
    -    if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
    -      fail("Timed out waiting on onNext latch");
    -    }
     
    -    // now unsubscribe and ensure it stops the recursive loop
    -    subscribe.unsubscribe();
    -    System.out.println("unsubscribe");
    +        o.toBlockingObservable().forEach(new Action1() {
     
    -    if (!completionLatch.await(5000, TimeUnit.MILLISECONDS)) {
    -      fail("Timed out waiting on completion latch");
    +            @Override
    +            public void call(String t) {
    +                System.out.println("t: " + t);
    +            }
    +        });
         }
     
    -    // the count can be 10 or higher due to thread scheduling of the unsubscribe vs the scheduler looping to emit the count
    -    assertTrue(count.get() >= 10);
    -    assertTrue(completed.get());
    -  }
    +    @Test
    +    public void testSubscribeWithScheduler1() throws InterruptedException {
    +
    +        final AtomicInteger count = new AtomicInteger();
     
    -  @Test
    -  public void testSchedulingWithDueTime() throws InterruptedException {
    +        Observable o1 = Observable. from(1, 2, 3, 4, 5);
     
    -    final CountDownLatch latch = new CountDownLatch(5);
    -    final AtomicInteger counter = new AtomicInteger();
    +        o1.subscribe(new Action1() {
     
    -    long start = System.currentTimeMillis();
    +            @Override
    +            public void call(Integer t) {
    +                System.out.println("Thread: " + Thread.currentThread().getName());
    +                System.out.println("t: " + t);
    +                count.incrementAndGet();
    +            }
    +        });
     
    -    Schedulers.threadPoolForComputation().schedule(null, new Func2() {
    +        // the above should be blocking so we should see a count of 5
    +        assertEquals(5, count.get());
     
    -      @Override
    -      public Subscription call(Scheduler scheduler, String state) {
    -        System.out.println("doing work");
    -        counter.incrementAndGet();
    -        latch.countDown();
    -        if (latch.getCount() == 0) {
    -          return Subscriptions.empty();
    -        } else {
    -          return scheduler.schedule(state, this, new Date(System.currentTimeMillis() + 50));
    -        }
    -      }
    -    }, new Date(System.currentTimeMillis() + 100));
    +        count.set(0);
    +
    +        // now we'll subscribe with a scheduler and it should be async
    +
    +        final String currentThreadName = Thread.currentThread().getName();
    +
    +        // latches for deterministically controlling the test below across threads
    +        final CountDownLatch latch = new CountDownLatch(5);
    +        final CountDownLatch first = new CountDownLatch(1);
    +
    +        o1.subscribe(new Action1() {
    +
    +            @Override
    +            public void call(Integer t) {
    +                try {
    +                    // we block the first one so we can assert this executes asynchronously with a count
    +                    first.await(1000, TimeUnit.SECONDS);
    +                } catch (InterruptedException e) {
    +                    throw new RuntimeException("The latch should have released if we are async.", e);
    +                }
    +                assertFalse(Thread.currentThread().getName().equals(currentThreadName));
    +                assertTrue(Thread.currentThread().getName().startsWith("RxComputationThreadPool"));
    +                System.out.println("Thread: " + Thread.currentThread().getName());
    +                System.out.println("t: " + t);
    +                count.incrementAndGet();
    +                latch.countDown();
    +            }
    +        }, Schedulers.threadPoolForComputation());
    +
    +        // assert we are async
    +        assertEquals(0, count.get());
    +        // release the latch so it can go forward
    +        first.countDown();
     
    -    if (!latch.await(3000, TimeUnit.MILLISECONDS)) {
    -      fail("didn't execute ... timed out");
    +        // wait for all 5 responses
    +        latch.await();
    +        assertEquals(5, count.get());
         }
     
    -    long end = System.currentTimeMillis();
    +    @Test
    +    public void testRecursiveScheduler1() {
    +        Observable obs = Observable.create(new OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(final Observer observer) {
    +                return Schedulers.currentThread().schedule(0, new Func2() {
    +                    @Override
    +                    public Subscription call(Scheduler scheduler, Integer i) {
    +                        if (i > 42) {
    +                            observer.onCompleted();
    +                            return Subscriptions.empty();
    +                        }
    +
    +                        observer.onNext(i);
    +
    +                        return scheduler.schedule(i + 1, this);
    +                    }
    +                });
    +            }
    +        });
    +
    +        final AtomicInteger lastValue = new AtomicInteger();
    +        obs.toBlockingObservable().forEach(new Action1() {
     
    -    assertEquals(5, counter.get());
    -    if ((end - start) < 250) {
    -      fail("it should have taken over 250ms since each step was scheduled 50ms in the future");
    +            @Override
    +            public void call(Integer v) {
    +                System.out.println("Value: " + v);
    +                lastValue.set(v);
    +            }
    +        });
    +
    +        assertEquals(42, lastValue.get());
         }
    -  }
     
    -  @Test
    -  public void testConcurrentOnNextFailsValidation() throws InterruptedException {
    +    @Test
    +    public void testRecursiveScheduler2() throws InterruptedException {
    +        // use latches instead of Thread.sleep
    +        final CountDownLatch latch = new CountDownLatch(10);
    +        final CountDownLatch completionLatch = new CountDownLatch(1);
     
    -    final int count = 10;
    -    final CountDownLatch latch = new CountDownLatch(count);
    -    Observable o = Observable.create(new OnSubscribeFunc() {
    +        Observable obs = Observable.create(new OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(final Observer observer) {
    +
    +                return Schedulers.threadPoolForComputation().schedule(new BooleanSubscription(), new Func2() {
    +                    @Override
    +                    public Subscription call(Scheduler scheduler, BooleanSubscription cancel) {
    +                        if (cancel.isUnsubscribed()) {
    +                            observer.onCompleted();
    +                            completionLatch.countDown();
    +                            return Subscriptions.empty();
    +                        }
    +
    +                        observer.onNext(42);
    +                        latch.countDown();
    +
    +                        // this will recursively schedule this task for execution again
    +                        scheduler.schedule(cancel, this);
    +
    +                        return cancel;
    +                    }
    +                });
    +            }
    +        });
     
    -      @Override
    -      public Subscription onSubscribe(final Observer observer) {
    -        for (int i = 0; i < count; i++) {
    -          final int v = i;
    -          new Thread(new Runnable() {
    +        final AtomicInteger count = new AtomicInteger();
    +        final AtomicBoolean completed = new AtomicBoolean(false);
    +        Subscription subscribe = obs.subscribe(new Observer() {
    +            @Override
    +            public void onCompleted() {
    +                System.out.println("Completed");
    +                completed.set(true);
    +            }
     
                 @Override
    -            public void run() {
    -              observer.onNext("v: " + v);
    +            public void onError(Throwable e) {
    +                System.out.println("Error");
    +            }
     
    -              latch.countDown();
    +            @Override
    +            public void onNext(Integer args) {
    +                count.incrementAndGet();
    +                System.out.println(args);
                 }
    -          }).start();
    +        });
    +
    +        if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
    +            fail("Timed out waiting on onNext latch");
             }
    -        return Subscriptions.empty();
    -      }
    -    });
     
    -    ConcurrentObserverValidator observer = new ConcurrentObserverValidator();
    -    // this should call onNext concurrently
    -    o.subscribe(observer);
    +        // now unsubscribe and ensure it stops the recursive loop
    +        subscribe.unsubscribe();
    +        System.out.println("unsubscribe");
     
    -    if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) {
    -      fail("timed out");
    -    }
    +        if (!completionLatch.await(5000, TimeUnit.MILLISECONDS)) {
    +            fail("Timed out waiting on completion latch");
    +        }
     
    -    if (observer.error.get() == null) {
    -      fail("We expected error messages due to concurrency");
    +        // the count can be 10 or higher due to thread scheduling of the unsubscribe vs the scheduler looping to emit the count
    +        assertTrue(count.get() >= 10);
    +        assertTrue(completed.get());
         }
    -  }
     
    -  @Test
    -  public void testObserveOn() throws InterruptedException {
    +    @Test
    +    public void testSchedulingWithDueTime() throws InterruptedException {
     
    -    Observable o = Observable.from("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten");
    +        final CountDownLatch latch = new CountDownLatch(5);
    +        final AtomicInteger counter = new AtomicInteger();
     
    -    ConcurrentObserverValidator observer = new ConcurrentObserverValidator();
    +        long start = System.currentTimeMillis();
     
    -    o.observeOn(Schedulers.threadPoolForComputation()).subscribe(observer);
    +        Schedulers.threadPoolForComputation().schedule(null, new Func2() {
     
    -    if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) {
    -      fail("timed out");
    -    }
    +            @Override
    +            public Subscription call(Scheduler scheduler, String state) {
    +                System.out.println("doing work");
    +                counter.incrementAndGet();
    +                latch.countDown();
    +                if (latch.getCount() == 0) {
    +                    return Subscriptions.empty();
    +                } else {
    +                    return scheduler.schedule(state, this, new Date(System.currentTimeMillis() + 50));
    +                }
    +            }
    +        }, new Date(System.currentTimeMillis() + 100));
     
    -    if (observer.error.get() != null) {
    -      observer.error.get().printStackTrace();
    -      fail("Error: " + observer.error.get().getMessage());
    -    }
    -  }
    +        if (!latch.await(3000, TimeUnit.MILLISECONDS)) {
    +            fail("didn't execute ... timed out");
    +        }
     
    -  @Test
    -  public void testSubscribeOnNestedConcurrency() throws InterruptedException {
    +        long end = System.currentTimeMillis();
     
    -    Observable o = Observable.from("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten")
    -        .mapMany(new Func1>() {
    +        assertEquals(5, counter.get());
    +        if ((end - start) < 250) {
    +            fail("it should have taken over 250ms since each step was scheduled 50ms in the future");
    +        }
    +    }
     
    -          @Override
    -          public Observable call(final String v) {
    -            return Observable.create(new OnSubscribeFunc() {
    +    @Test
    +    public void testConcurrentOnNextFailsValidation() throws InterruptedException {
     
    -              @Override
    -              public Subscription onSubscribe(final Observer observer) {
    -                observer.onNext("value_after_map-" + v);
    -                observer.onCompleted();
    +        final int count = 10;
    +        final CountDownLatch latch = new CountDownLatch(count);
    +        Observable o = Observable.create(new OnSubscribeFunc() {
    +
    +            @Override
    +            public Subscription onSubscribe(final Observer observer) {
    +                for (int i = 0; i < count; i++) {
    +                    final int v = i;
    +                    new Thread(new Runnable() {
    +
    +                        @Override
    +                        public void run() {
    +                            observer.onNext("v: " + v);
    +
    +                            latch.countDown();
    +                        }
    +                    }).start();
    +                }
                     return Subscriptions.empty();
    -              }
    -            }).subscribeOn(Schedulers.newThread()); // subscribe on a new thread
    -          }
    +            }
             });
     
    -    ConcurrentObserverValidator observer = new ConcurrentObserverValidator();
    +        ConcurrentObserverValidator observer = new ConcurrentObserverValidator();
    +        // this should call onNext concurrently
    +        o.subscribe(observer);
     
    -    o.subscribe(observer);
    +        if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) {
    +            fail("timed out");
    +        }
     
    -    if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) {
    -      fail("timed out");
    +        if (observer.error.get() == null) {
    +            fail("We expected error messages due to concurrency");
    +        }
         }
     
    -    if (observer.error.get() != null) {
    -      observer.error.get().printStackTrace();
    -      fail("Error: " + observer.error.get().getMessage());
    -    }
    -  }
    +    @Test
    +    public void testObserveOn() throws InterruptedException {
     
    -  @Test
    -  public void testRecursion() {
    -    TestScheduler s = new TestScheduler();
    +        Observable o = Observable.from("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten");
     
    -    final AtomicInteger counter = new AtomicInteger(0);
    +        ConcurrentObserverValidator observer = new ConcurrentObserverValidator();
     
    -    Subscription subscription = s.schedule(new Action1() {
    +        o.observeOn(Schedulers.threadPoolForComputation()).subscribe(observer);
     
    -      @Override
    -      public void call(Action0 self) {
    -        counter.incrementAndGet();
    -        System.out.println("counter: " + counter.get());
    -        self.call();
    -      }
    +        if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) {
    +            fail("timed out");
    +        }
    +
    +        if (observer.error.get() != null) {
    +            observer.error.get().printStackTrace();
    +            fail("Error: " + observer.error.get().getMessage());
    +        }
    +    }
    +
    +    @Test
    +    public void testSubscribeOnNestedConcurrency() throws InterruptedException {
     
    -    });
    -    subscription.unsubscribe();
    -    assertEquals(0, counter.get());
    -  }
    +        Observable o = Observable.from("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten")
    +                .mapMany(new Func1>() {
     
    +                    @Override
    +                    public Observable call(final String v) {
    +                        return Observable.create(new OnSubscribeFunc() {
     
    -  /**
    -   * Used to determine if onNext is being invoked concurrently.
    -   *
    -   * @param 
    -   */
    -  private static class ConcurrentObserverValidator implements Observer {
    +                            @Override
    +                            public Subscription onSubscribe(final Observer observer) {
    +                                observer.onNext("value_after_map-" + v);
    +                                observer.onCompleted();
    +                                return Subscriptions.empty();
    +                            }
    +                        }).subscribeOn(Schedulers.newThread()); // subscribe on a new thread
    +                    }
    +                });
     
    -    final AtomicInteger concurrentCounter = new AtomicInteger();
    -    final AtomicReference error = new AtomicReference();
    -    final CountDownLatch completed = new CountDownLatch(1);
    +        ConcurrentObserverValidator observer = new ConcurrentObserverValidator();
     
    -    @Override
    -    public void onCompleted() {
    -      completed.countDown();
    +        o.subscribe(observer);
    +
    +        if (!observer.completed.await(3000, TimeUnit.MILLISECONDS)) {
    +            fail("timed out");
    +        }
    +
    +        if (observer.error.get() != null) {
    +            observer.error.get().printStackTrace();
    +            fail("Error: " + observer.error.get().getMessage());
    +        }
         }
     
    -    @Override
    -    public void onError(Throwable e) {
    -      completed.countDown();
    -      error.set(e);
    +    @Test
    +    public void testRecursion() {
    +        TestScheduler s = new TestScheduler();
    +
    +        final AtomicInteger counter = new AtomicInteger(0);
    +
    +        Subscription subscription = s.schedule(new Action1() {
    +
    +            @Override
    +            public void call(Action0 self) {
    +                counter.incrementAndGet();
    +                System.out.println("counter: " + counter.get());
    +                self.call();
    +            }
    +
    +        });
    +        subscription.unsubscribe();
    +        assertEquals(0, counter.get());
         }
     
    -    @Override
    -    public void onNext(T args) {
    -      int count = concurrentCounter.incrementAndGet();
    -      System.out.println("ConcurrentObserverValidator.onNext: " + args);
    -      if (count > 1) {
    -        onError(new RuntimeException("we should not have concurrent execution of onNext"));
    -      }
    -      try {
    -        try {
    -          // take some time so other onNext calls could pile up (I haven't yet thought of a way to do this without sleeping)
    -          Thread.sleep(50);
    -        } catch (InterruptedException e) {
    -          // ignore
    +    /**
    +     * Used to determine if onNext is being invoked concurrently.
    +     * 
    +     * @param 
    +     */
    +    private static class ConcurrentObserverValidator implements Observer {
    +
    +        final AtomicInteger concurrentCounter = new AtomicInteger();
    +        final AtomicReference error = new AtomicReference();
    +        final CountDownLatch completed = new CountDownLatch(1);
    +
    +        @Override
    +        public void onCompleted() {
    +            completed.countDown();
    +        }
    +
    +        @Override
    +        public void onError(Throwable e) {
    +            completed.countDown();
    +            error.set(e);
    +        }
    +
    +        @Override
    +        public void onNext(T args) {
    +            int count = concurrentCounter.incrementAndGet();
    +            System.out.println("ConcurrentObserverValidator.onNext: " + args);
    +            if (count > 1) {
    +                onError(new RuntimeException("we should not have concurrent execution of onNext"));
    +            }
    +            try {
    +                try {
    +                    // take some time so other onNext calls could pile up (I haven't yet thought of a way to do this without sleeping)
    +                    Thread.sleep(50);
    +                } catch (InterruptedException e) {
    +                    // ignore
    +                }
    +            } finally {
    +                concurrentCounter.decrementAndGet();
    +            }
             }
    -      } finally {
    -        concurrentCounter.decrementAndGet();
    -      }
    -    }
     
    -  }
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/StartWithTests.java b/rxjava-core/src/test/java/rx/StartWithTests.java
    index de7b03e6ef..2204b6f081 100644
    --- a/rxjava-core/src/test/java/rx/StartWithTests.java
    +++ b/rxjava-core/src/test/java/rx/StartWithTests.java
    @@ -16,7 +16,7 @@ public void startWith1() {
             assertEquals("zero", values.get(0));
             assertEquals("two", values.get(2));
         }
    -    
    +
         @Test
         public void startWithIterable() {
             List li = new ArrayList();
    diff --git a/rxjava-core/src/test/java/rx/TimeoutTests.java b/rxjava-core/src/test/java/rx/TimeoutTests.java
    index 532ae42eba..26da4a3713 100644
    --- a/rxjava-core/src/test/java/rx/TimeoutTests.java
    +++ b/rxjava-core/src/test/java/rx/TimeoutTests.java
    @@ -1,12 +1,12 @@
     /**
      * Copyright 2013 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.
    @@ -15,20 +15,19 @@
      */
     package rx;
     
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +
    +import java.util.concurrent.TimeUnit;
    +import java.util.concurrent.TimeoutException;
    +
     import org.junit.Before;
     import org.junit.Test;
     import org.mockito.MockitoAnnotations;
    +
     import rx.concurrency.TestScheduler;
     import rx.subjects.PublishSubject;
     
    -import java.util.concurrent.TimeUnit;
    -import java.util.concurrent.TimeoutException;
    -
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Mockito.mock;
    -import static org.mockito.Mockito.never;
    -import static org.mockito.Mockito.verify;
    -
     public class TimeoutTests {
         private PublishSubject underlyingSubject;
         private TestScheduler testScheduler;
    diff --git a/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java b/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java
    index de34142a93..e9a1a1061a 100644
    --- a/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java
    +++ b/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java
    @@ -1,129 +1,128 @@
     package rx.concurrency;
     
    -import org.junit.Test;
    -import org.mockito.InOrder;
    -import rx.util.functions.Action0;
    +import static org.mockito.Mockito.*;
     
     import java.util.concurrent.TimeUnit;
     
    -import static org.mockito.Mockito.*;
    +import org.junit.Test;
    +import org.mockito.InOrder;
    +
    +import rx.util.functions.Action0;
     
     public class CurrentThreadSchedulerTest {
     
    -  @Test
    -  public void testNestedActions() {
    -    final CurrentThreadScheduler scheduler = new CurrentThreadScheduler();
    -
    -    final Action0 firstStepStart = mock(Action0.class);
    -    final Action0 firstStepEnd = mock(Action0.class);
    -
    -    final Action0 secondStepStart = mock(Action0.class);
    -    final Action0 secondStepEnd = mock(Action0.class);
    -
    -    final Action0 thirdStepStart = mock(Action0.class);
    -    final Action0 thirdStepEnd = mock(Action0.class);
    -
    -    final Action0 firstAction = new Action0() {
    -      @Override
    -      public void call() {
    -        firstStepStart.call();
    -        firstStepEnd.call();
    -      }
    -    };
    -    final Action0 secondAction = new Action0() {
    -      @Override
    -      public void call() {
    -        secondStepStart.call();
    -        scheduler.schedule(firstAction);
    -        secondStepEnd.call();
    -
    -      }
    -    };
    -    final Action0 thirdAction = new Action0() {
    -      @Override
    -      public void call() {
    -        thirdStepStart.call();
    -        scheduler.schedule(secondAction);
    -        thirdStepEnd.call();
    -      }
    -    };
    -
    -    InOrder inOrder = inOrder(firstStepStart, firstStepEnd, secondStepStart, secondStepEnd, thirdStepStart, thirdStepEnd);
    -
    -    scheduler.schedule(thirdAction);
    -
    -    inOrder.verify(thirdStepStart, times(1)).call();
    -    inOrder.verify(thirdStepEnd, times(1)).call();
    -    inOrder.verify(secondStepStart, times(1)).call();
    -    inOrder.verify(secondStepEnd, times(1)).call();
    -    inOrder.verify(firstStepStart, times(1)).call();
    -    inOrder.verify(firstStepEnd, times(1)).call();
    -  }
    -
    -  @Test
    -  public void testSequenceOfActions() {
    -    final CurrentThreadScheduler scheduler = new CurrentThreadScheduler();
    -
    -    final Action0 first = mock(Action0.class);
    -    final Action0 second = mock(Action0.class);
    -
    -    scheduler.schedule(first);
    -    scheduler.schedule(second);
    -
    -    verify(first, times(1)).call();
    -    verify(second, times(1)).call();
    -
    -  }
    -
    -  @Test
    -  public void testSequenceOfDelayedActions() {
    -    final CurrentThreadScheduler scheduler = new CurrentThreadScheduler();
    -
    -    final Action0 first = mock(Action0.class);
    -    final Action0 second = mock(Action0.class);
    -
    -    scheduler.schedule(new Action0() {
    -      @Override
    -      public void call() {
    -        scheduler.schedule(first, 30, TimeUnit.MILLISECONDS);
    -        scheduler.schedule(second, 10, TimeUnit.MILLISECONDS);
    -      }
    -    });
    -
    -    InOrder inOrder = inOrder(first, second);
    -
    -    inOrder.verify(second, times(1)).call();
    -    inOrder.verify(first, times(1)).call();
    -
    -
    -  }
    -
    -  @Test
    -  public void testMixOfDelayedAndNonDelayedActions() {
    -    final CurrentThreadScheduler scheduler = new CurrentThreadScheduler();
    -
    -    final Action0 first = mock(Action0.class);
    -    final Action0 second = mock(Action0.class);
    -    final Action0 third = mock(Action0.class);
    -    final Action0 fourth = mock(Action0.class);
    -
    -    scheduler.schedule(new Action0() {
    -      @Override
    -      public void call() {
    +    @Test
    +    public void testNestedActions() {
    +        final CurrentThreadScheduler scheduler = new CurrentThreadScheduler();
    +
    +        final Action0 firstStepStart = mock(Action0.class);
    +        final Action0 firstStepEnd = mock(Action0.class);
    +
    +        final Action0 secondStepStart = mock(Action0.class);
    +        final Action0 secondStepEnd = mock(Action0.class);
    +
    +        final Action0 thirdStepStart = mock(Action0.class);
    +        final Action0 thirdStepEnd = mock(Action0.class);
    +
    +        final Action0 firstAction = new Action0() {
    +            @Override
    +            public void call() {
    +                firstStepStart.call();
    +                firstStepEnd.call();
    +            }
    +        };
    +        final Action0 secondAction = new Action0() {
    +            @Override
    +            public void call() {
    +                secondStepStart.call();
    +                scheduler.schedule(firstAction);
    +                secondStepEnd.call();
    +
    +            }
    +        };
    +        final Action0 thirdAction = new Action0() {
    +            @Override
    +            public void call() {
    +                thirdStepStart.call();
    +                scheduler.schedule(secondAction);
    +                thirdStepEnd.call();
    +            }
    +        };
    +
    +        InOrder inOrder = inOrder(firstStepStart, firstStepEnd, secondStepStart, secondStepEnd, thirdStepStart, thirdStepEnd);
    +
    +        scheduler.schedule(thirdAction);
    +
    +        inOrder.verify(thirdStepStart, times(1)).call();
    +        inOrder.verify(thirdStepEnd, times(1)).call();
    +        inOrder.verify(secondStepStart, times(1)).call();
    +        inOrder.verify(secondStepEnd, times(1)).call();
    +        inOrder.verify(firstStepStart, times(1)).call();
    +        inOrder.verify(firstStepEnd, times(1)).call();
    +    }
    +
    +    @Test
    +    public void testSequenceOfActions() {
    +        final CurrentThreadScheduler scheduler = new CurrentThreadScheduler();
    +
    +        final Action0 first = mock(Action0.class);
    +        final Action0 second = mock(Action0.class);
    +
             scheduler.schedule(first);
    -        scheduler.schedule(second, 300, TimeUnit.MILLISECONDS);
    -        scheduler.schedule(third, 100, TimeUnit.MILLISECONDS);
    -        scheduler.schedule(fourth);
    -      }
    -    });
    +        scheduler.schedule(second);
    +
    +        verify(first, times(1)).call();
    +        verify(second, times(1)).call();
    +
    +    }
    +
    +    @Test
    +    public void testSequenceOfDelayedActions() {
    +        final CurrentThreadScheduler scheduler = new CurrentThreadScheduler();
    +
    +        final Action0 first = mock(Action0.class);
    +        final Action0 second = mock(Action0.class);
    +
    +        scheduler.schedule(new Action0() {
    +            @Override
    +            public void call() {
    +                scheduler.schedule(first, 30, TimeUnit.MILLISECONDS);
    +                scheduler.schedule(second, 10, TimeUnit.MILLISECONDS);
    +            }
    +        });
    +
    +        InOrder inOrder = inOrder(first, second);
    +
    +        inOrder.verify(second, times(1)).call();
    +        inOrder.verify(first, times(1)).call();
    +
    +    }
    +
    +    @Test
    +    public void testMixOfDelayedAndNonDelayedActions() {
    +        final CurrentThreadScheduler scheduler = new CurrentThreadScheduler();
    +
    +        final Action0 first = mock(Action0.class);
    +        final Action0 second = mock(Action0.class);
    +        final Action0 third = mock(Action0.class);
    +        final Action0 fourth = mock(Action0.class);
     
    -    InOrder inOrder = inOrder(first, second, third, fourth);
    +        scheduler.schedule(new Action0() {
    +            @Override
    +            public void call() {
    +                scheduler.schedule(first);
    +                scheduler.schedule(second, 300, TimeUnit.MILLISECONDS);
    +                scheduler.schedule(third, 100, TimeUnit.MILLISECONDS);
    +                scheduler.schedule(fourth);
    +            }
    +        });
     
    -    inOrder.verify(first, times(1)).call();
    -    inOrder.verify(fourth, times(1)).call();
    -    inOrder.verify(third, times(1)).call();
    -    inOrder.verify(second, times(1)).call();
    +        InOrder inOrder = inOrder(first, second, third, fourth);
     
    +        inOrder.verify(first, times(1)).call();
    +        inOrder.verify(fourth, times(1)).call();
    +        inOrder.verify(third, times(1)).call();
    +        inOrder.verify(second, times(1)).call();
     
    -  }
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java b/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java
    index df08b8aa31..fc2da52963 100644
    --- a/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java
    +++ b/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java
    @@ -1,59 +1,60 @@
     package rx.concurrency;
     
    +import static org.mockito.Mockito.*;
    +
     import org.junit.Test;
     import org.mockito.InOrder;
    -import rx.util.functions.Action0;
     
    -import static org.mockito.Mockito.*;
    +import rx.util.functions.Action0;
     
     public class ImmediateSchedulerTest {
    -  @Test
    -  public void testNestedActions() {
    -    final ImmediateScheduler scheduler = new ImmediateScheduler();
    -
    -    final Action0 firstStepStart = mock(Action0.class);
    -    final Action0 firstStepEnd = mock(Action0.class);
    -
    -    final Action0 secondStepStart = mock(Action0.class);
    -    final Action0 secondStepEnd = mock(Action0.class);
    -
    -    final Action0 thirdStepStart = mock(Action0.class);
    -    final Action0 thirdStepEnd = mock(Action0.class);
    -
    -    final Action0 firstAction = new Action0() {
    -      @Override
    -      public void call() {
    -        firstStepStart.call();
    -        firstStepEnd.call();
    -      }
    -    };
    -    final Action0 secondAction = new Action0() {
    -      @Override
    -      public void call() {
    -        secondStepStart.call();
    -        scheduler.schedule(firstAction);
    -        secondStepEnd.call();
    -
    -      }
    -    };
    -    final Action0 thirdAction = new Action0() {
    -      @Override
    -      public void call() {
    -        thirdStepStart.call();
    -        scheduler.schedule(secondAction);
    -        thirdStepEnd.call();
    -      }
    -    };
    -
    -    InOrder inOrder = inOrder(firstStepStart, firstStepEnd, secondStepStart, secondStepEnd, thirdStepStart, thirdStepEnd);
    -
    -    scheduler.schedule(thirdAction);
    -
    -    inOrder.verify(thirdStepStart, times(1)).call();
    -    inOrder.verify(secondStepStart, times(1)).call();
    -    inOrder.verify(firstStepStart, times(1)).call();
    -    inOrder.verify(firstStepEnd, times(1)).call();
    -    inOrder.verify(secondStepEnd, times(1)).call();
    -    inOrder.verify(thirdStepEnd, times(1)).call();
    -  }
    +    @Test
    +    public void testNestedActions() {
    +        final ImmediateScheduler scheduler = new ImmediateScheduler();
    +
    +        final Action0 firstStepStart = mock(Action0.class);
    +        final Action0 firstStepEnd = mock(Action0.class);
    +
    +        final Action0 secondStepStart = mock(Action0.class);
    +        final Action0 secondStepEnd = mock(Action0.class);
    +
    +        final Action0 thirdStepStart = mock(Action0.class);
    +        final Action0 thirdStepEnd = mock(Action0.class);
    +
    +        final Action0 firstAction = new Action0() {
    +            @Override
    +            public void call() {
    +                firstStepStart.call();
    +                firstStepEnd.call();
    +            }
    +        };
    +        final Action0 secondAction = new Action0() {
    +            @Override
    +            public void call() {
    +                secondStepStart.call();
    +                scheduler.schedule(firstAction);
    +                secondStepEnd.call();
    +
    +            }
    +        };
    +        final Action0 thirdAction = new Action0() {
    +            @Override
    +            public void call() {
    +                thirdStepStart.call();
    +                scheduler.schedule(secondAction);
    +                thirdStepEnd.call();
    +            }
    +        };
    +
    +        InOrder inOrder = inOrder(firstStepStart, firstStepEnd, secondStepStart, secondStepEnd, thirdStepStart, thirdStepEnd);
    +
    +        scheduler.schedule(thirdAction);
    +
    +        inOrder.verify(thirdStepStart, times(1)).call();
    +        inOrder.verify(secondStepStart, times(1)).call();
    +        inOrder.verify(firstStepStart, times(1)).call();
    +        inOrder.verify(firstStepEnd, times(1)).call();
    +        inOrder.verify(secondStepEnd, times(1)).call();
    +        inOrder.verify(thirdStepEnd, times(1)).call();
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java b/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java
    index 0c6f885de9..e97765c6ab 100644
    --- a/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java
    +++ b/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java
    @@ -1,9 +1,14 @@
     package rx.observables;
     
    +import static org.junit.Assert.*;
    +
    +import java.util.Iterator;
    +
     import org.junit.Before;
     import org.junit.Test;
     import org.mockito.Mock;
     import org.mockito.MockitoAnnotations;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.Subscription;
    @@ -12,237 +17,233 @@
     import rx.util.functions.Action1;
     import rx.util.functions.Func1;
     
    -import java.util.Iterator;
    +public class BlockingObservableTest {
     
    -import static org.junit.Assert.*;
    +    @Mock
    +    Observer w;
     
    -public class BlockingObservableTest {
    +    @Before
    +    public void before() {
    +        MockitoAnnotations.initMocks(this);
    +    }
     
    -  @Mock
    -  Observer w;
    -
    -  @Before
    -  public void before() {
    -    MockitoAnnotations.initMocks(this);
    -  }
    -
    -  @Test
    -  public void testLast() {
    -    BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three"));
    -
    -    assertEquals("three", obs.last());
    -  }
    -
    -  @Test
    -  public void testLastEmptyObservable() {
    -    BlockingObservable obs = BlockingObservable.from(Observable.empty());
    -
    -    assertNull(obs.last());
    -  }
    -
    -  @Test
    -  public void testLastOrDefault() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.from(1, 0, -1));
    -    int last = observable.lastOrDefault(-100, new Func1() {
    -      @Override
    -      public Boolean call(Integer args) {
    -        return args >= 0;
    -      }
    -    });
    -    assertEquals(0, last);
    -  }
    -
    -  @Test
    -  public void testLastOrDefault1() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "three"));
    -    assertEquals("three", observable.lastOrDefault("default"));
    -  }
    -
    -  @Test
    -  public void testLastOrDefault2() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.empty());
    -    assertEquals("default", observable.lastOrDefault("default"));
    -  }
    -
    -  @Test
    -  public void testLastOrDefaultWithPredicate() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.from(1, 0, -1));
    -    int last = observable.lastOrDefault(0, new Func1() {
    -      @Override
    -      public Boolean call(Integer args) {
    -        return args < 0;
    -      }
    -    });
    -
    -    assertEquals(-1, last);
    -  }
    -
    -  @Test
    -  public void testLastOrDefaultWrongPredicate() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.from(-1, -2, -3));
    -    int last = observable.lastOrDefault(0, new Func1() {
    -      @Override
    -      public Boolean call(Integer args) {
    -        return args >= 0;
    -      }
    -    });
    -    assertEquals(0, last);
    -  }
    -
    -  @Test
    -  public void testLastWithPredicate() {
    -    BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three"));
    -
    -    assertEquals("two", obs.last(new Func1() {
    -      @Override
    -      public Boolean call(String s) {
    -        return s.length() == 3;
    -      }
    -    }));
    -  }
    -
    -  public void testSingle() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.from("one"));
    -    assertEquals("one", observable.single());
    -  }
    -
    -  @Test
    -  public void testSingleDefault() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.empty());
    -    assertEquals("default", observable.singleOrDefault("default"));
    -  }
    -
    -  @Test(expected = IllegalStateException.class)
    -  public void testSingleDefaultPredicateMatchesMoreThanOne() {
    -    BlockingObservable.from(Observable.from("one", "two")).singleOrDefault("default", new Func1() {
    -      @Override
    -      public Boolean call(String args) {
    -        return args.length() == 3;
    -      }
    -    });
    -  }
    -
    -  @Test
    -  public void testSingleDefaultPredicateMatchesNothing() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two"));
    -    String result = observable.singleOrDefault("default", new Func1() {
    -      @Override
    -      public Boolean call(String args) {
    -        return args.length() == 4;
    -      }
    -    });
    -    assertEquals("default", result);
    -  }
    -
    -  @Test(expected = IllegalStateException.class)
    -  public void testSingleDefaultWithMoreThanOne() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "three"));
    -    observable.singleOrDefault("default");
    -  }
    -
    -  @Test
    -  public void testSingleWithPredicateDefault() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "four"));
    -    assertEquals("four", observable.single(new Func1() {
    -      @Override
    -      public Boolean call(String s) {
    -        return s.length() == 4;
    -      }
    -    }));
    -  }
    -
    -  @Test(expected = IllegalStateException.class)
    -  public void testSingleWrong() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.from(1, 2));
    -    observable.single();
    -  }
    -
    -  @Test(expected = IllegalStateException.class)
    -  public void testSingleWrongPredicate() {
    -    BlockingObservable observable = BlockingObservable.from(Observable.from(-1));
    -    observable.single(new Func1() {
    -      @Override
    -      public Boolean call(Integer args) {
    -        return args > 0;
    -      }
    -    });
    -  }
    -
    -  @Test
    -  public void testToIterable() {
    -    BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three"));
    -
    -    Iterator it = obs.toIterable().iterator();
    -
    -    assertEquals(true, it.hasNext());
    -    assertEquals("one", it.next());
    -
    -    assertEquals(true, it.hasNext());
    -    assertEquals("two", it.next());
    -
    -    assertEquals(true, it.hasNext());
    -    assertEquals("three", it.next());
    -
    -    assertEquals(false, it.hasNext());
    -
    -  }
    -
    -  @Test(expected = TestException.class)
    -  public void testToIterableWithException() {
    -    BlockingObservable obs = BlockingObservable.from(Observable.create(new Observable.OnSubscribeFunc() {
    -
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        observer.onNext("one");
    -        observer.onError(new TestException());
    -        return Subscriptions.empty();
    -      }
    -    }));
    -
    -    Iterator it = obs.toIterable().iterator();
    -
    -    assertEquals(true, it.hasNext());
    -    assertEquals("one", it.next());
    -
    -    assertEquals(true, it.hasNext());
    -    it.next();
    -
    -  }
    -
    -  @Test
    -  public void testForEachWithError() {
    -    try {
    -      BlockingObservable.from(Observable.create(new Observable.OnSubscribeFunc() {
    -
    -        @Override
    -        public Subscription onSubscribe(final Observer observer) {
    -          final BooleanSubscription subscription = new BooleanSubscription();
    -          new Thread(new Runnable() {
    +    @Test
    +    public void testLast() {
    +        BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three"));
     
    +        assertEquals("three", obs.last());
    +    }
    +
    +    @Test
    +    public void testLastEmptyObservable() {
    +        BlockingObservable obs = BlockingObservable.from(Observable.empty());
    +
    +        assertNull(obs.last());
    +    }
    +
    +    @Test
    +    public void testLastOrDefault() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.from(1, 0, -1));
    +        int last = observable.lastOrDefault(-100, new Func1() {
                 @Override
    -            public void run() {
    -              observer.onNext("one");
    -              observer.onNext("two");
    -              observer.onNext("three");
    -              observer.onCompleted();
    +            public Boolean call(Integer args) {
    +                return args >= 0;
                 }
    -          }).start();
    -          return subscription;
    -        }
    -      })).forEach(new Action1() {
    +        });
    +        assertEquals(0, last);
    +    }
    +
    +    @Test
    +    public void testLastOrDefault1() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "three"));
    +        assertEquals("three", observable.lastOrDefault("default"));
    +    }
    +
    +    @Test
    +    public void testLastOrDefault2() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.empty());
    +        assertEquals("default", observable.lastOrDefault("default"));
    +    }
    +
    +    @Test
    +    public void testLastOrDefaultWithPredicate() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.from(1, 0, -1));
    +        int last = observable.lastOrDefault(0, new Func1() {
    +            @Override
    +            public Boolean call(Integer args) {
    +                return args < 0;
    +            }
    +        });
    +
    +        assertEquals(-1, last);
    +    }
    +
    +    @Test
    +    public void testLastOrDefaultWrongPredicate() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.from(-1, -2, -3));
    +        int last = observable.lastOrDefault(0, new Func1() {
    +            @Override
    +            public Boolean call(Integer args) {
    +                return args >= 0;
    +            }
    +        });
    +        assertEquals(0, last);
    +    }
    +
    +    @Test
    +    public void testLastWithPredicate() {
    +        BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three"));
    +
    +        assertEquals("two", obs.last(new Func1() {
    +            @Override
    +            public Boolean call(String s) {
    +                return s.length() == 3;
    +            }
    +        }));
    +    }
    +
    +    public void testSingle() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.from("one"));
    +        assertEquals("one", observable.single());
    +    }
    +
    +    @Test
    +    public void testSingleDefault() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.empty());
    +        assertEquals("default", observable.singleOrDefault("default"));
    +    }
     
    -        @Override
    -        public void call(String t1) {
    -          throw new RuntimeException("fail");
    +    @Test(expected = IllegalStateException.class)
    +    public void testSingleDefaultPredicateMatchesMoreThanOne() {
    +        BlockingObservable.from(Observable.from("one", "two")).singleOrDefault("default", new Func1() {
    +            @Override
    +            public Boolean call(String args) {
    +                return args.length() == 3;
    +            }
    +        });
    +    }
    +
    +    @Test
    +    public void testSingleDefaultPredicateMatchesNothing() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two"));
    +        String result = observable.singleOrDefault("default", new Func1() {
    +            @Override
    +            public Boolean call(String args) {
    +                return args.length() == 4;
    +            }
    +        });
    +        assertEquals("default", result);
    +    }
    +
    +    @Test(expected = IllegalStateException.class)
    +    public void testSingleDefaultWithMoreThanOne() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "three"));
    +        observable.singleOrDefault("default");
    +    }
    +
    +    @Test
    +    public void testSingleWithPredicateDefault() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.from("one", "two", "four"));
    +        assertEquals("four", observable.single(new Func1() {
    +            @Override
    +            public Boolean call(String s) {
    +                return s.length() == 4;
    +            }
    +        }));
    +    }
    +
    +    @Test(expected = IllegalStateException.class)
    +    public void testSingleWrong() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.from(1, 2));
    +        observable.single();
    +    }
    +
    +    @Test(expected = IllegalStateException.class)
    +    public void testSingleWrongPredicate() {
    +        BlockingObservable observable = BlockingObservable.from(Observable.from(-1));
    +        observable.single(new Func1() {
    +            @Override
    +            public Boolean call(Integer args) {
    +                return args > 0;
    +            }
    +        });
    +    }
    +
    +    @Test
    +    public void testToIterable() {
    +        BlockingObservable obs = BlockingObservable.from(Observable.from("one", "two", "three"));
    +
    +        Iterator it = obs.toIterable().iterator();
    +
    +        assertEquals(true, it.hasNext());
    +        assertEquals("one", it.next());
    +
    +        assertEquals(true, it.hasNext());
    +        assertEquals("two", it.next());
    +
    +        assertEquals(true, it.hasNext());
    +        assertEquals("three", it.next());
    +
    +        assertEquals(false, it.hasNext());
    +
    +    }
    +
    +    @Test(expected = TestException.class)
    +    public void testToIterableWithException() {
    +        BlockingObservable obs = BlockingObservable.from(Observable.create(new Observable.OnSubscribeFunc() {
    +
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                observer.onNext("one");
    +                observer.onError(new TestException());
    +                return Subscriptions.empty();
    +            }
    +        }));
    +
    +        Iterator it = obs.toIterable().iterator();
    +
    +        assertEquals(true, it.hasNext());
    +        assertEquals("one", it.next());
    +
    +        assertEquals(true, it.hasNext());
    +        it.next();
    +
    +    }
    +
    +    @Test
    +    public void testForEachWithError() {
    +        try {
    +            BlockingObservable.from(Observable.create(new Observable.OnSubscribeFunc() {
    +
    +                @Override
    +                public Subscription onSubscribe(final Observer observer) {
    +                    final BooleanSubscription subscription = new BooleanSubscription();
    +                    new Thread(new Runnable() {
    +
    +                        @Override
    +                        public void run() {
    +                            observer.onNext("one");
    +                            observer.onNext("two");
    +                            observer.onNext("three");
    +                            observer.onCompleted();
    +                        }
    +                    }).start();
    +                    return subscription;
    +                }
    +            })).forEach(new Action1() {
    +
    +                @Override
    +                public void call(String t1) {
    +                    throw new RuntimeException("fail");
    +                }
    +            });
    +            fail("we expect an exception to be thrown");
    +        } catch (Throwable e) {
    +            // do nothing as we expect this
             }
    -      });
    -      fail("we expect an exception to be thrown");
    -    } catch (Throwable e) {
    -      // do nothing as we expect this
         }
    -  }
     
    -  private static class TestException extends RuntimeException {
    -    private static final long serialVersionUID = 1L;
    -  }
    +    private static class TestException extends RuntimeException {
    +        private static final long serialVersionUID = 1L;
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationAllTest.java b/rxjava-core/src/test/java/rx/operators/OperationAllTest.java
    index 23bde39e59..8418e821d6 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationAllTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationAllTest.java
    @@ -1,84 +1,85 @@
     package rx.operators;
     
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationAll.*;
    +
     import org.junit.Test;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.util.functions.Func1;
     
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationAll.all;
    -
     public class OperationAllTest {
     
    -  @Test
    -  @SuppressWarnings("unchecked")
    -  public void testAll() {
    -    Observable obs = Observable.from("one", "two", "six");
    -
    -    Observer observer = mock(Observer.class);
    -    Observable.create(all(obs, new Func1() {
    -      @Override
    -      public Boolean call(String s) {
    -        return s.length() == 3;
    -      }
    -    })).subscribe(observer);
    -
    -    verify(observer).onNext(true);
    -    verify(observer).onCompleted();
    -    verifyNoMoreInteractions(observer);
    -  }
    -
    -  @Test
    -  @SuppressWarnings("unchecked")
    -  public void testNotAll() {
    -    Observable obs = Observable.from("one", "two", "three", "six");
    -
    -    Observer observer = mock(Observer.class);
    -    Observable.create(all(obs, new Func1() {
    -      @Override
    -      public Boolean call(String s) {
    -        return s.length() == 3;
    -      }
    -    })).subscribe(observer);
    -
    -    verify(observer).onNext(false);
    -    verify(observer).onCompleted();
    -    verifyNoMoreInteractions(observer);
    -  }
    -
    -  @Test
    -  @SuppressWarnings("unchecked")
    -  public void testEmpty() {
    -    Observable obs = Observable.empty();
    -
    -    Observer observer = mock(Observer.class);
    -    Observable.create(all(obs, new Func1() {
    -      @Override
    -      public Boolean call(String s) {
    -        return s.length() == 3;
    -      }
    -    })).subscribe(observer);
    -
    -    verify(observer).onNext(true);
    -    verify(observer).onCompleted();
    -    verifyNoMoreInteractions(observer);
    -  }
    -
    -  @Test
    -  @SuppressWarnings("unchecked")
    -  public void testError() {
    -    Throwable error = new Throwable();
    -    Observable obs = Observable.error(error);
    -
    -    Observer observer = mock(Observer.class);
    -    Observable.create(all(obs, new Func1() {
    -      @Override
    -      public Boolean call(String s) {
    -        return s.length() == 3;
    -      }
    -    })).subscribe(observer);
    -
    -    verify(observer).onError(error);
    -    verifyNoMoreInteractions(observer);
    -  }
    +    @Test
    +    @SuppressWarnings("unchecked")
    +    public void testAll() {
    +        Observable obs = Observable.from("one", "two", "six");
    +
    +        Observer observer = mock(Observer.class);
    +        Observable.create(all(obs, new Func1() {
    +            @Override
    +            public Boolean call(String s) {
    +                return s.length() == 3;
    +            }
    +        })).subscribe(observer);
    +
    +        verify(observer).onNext(true);
    +        verify(observer).onCompleted();
    +        verifyNoMoreInteractions(observer);
    +    }
    +
    +    @Test
    +    @SuppressWarnings("unchecked")
    +    public void testNotAll() {
    +        Observable obs = Observable.from("one", "two", "three", "six");
    +
    +        Observer observer = mock(Observer.class);
    +        Observable.create(all(obs, new Func1() {
    +            @Override
    +            public Boolean call(String s) {
    +                return s.length() == 3;
    +            }
    +        })).subscribe(observer);
    +
    +        verify(observer).onNext(false);
    +        verify(observer).onCompleted();
    +        verifyNoMoreInteractions(observer);
    +    }
    +
    +    @Test
    +    @SuppressWarnings("unchecked")
    +    public void testEmpty() {
    +        Observable obs = Observable.empty();
    +
    +        Observer observer = mock(Observer.class);
    +        Observable.create(all(obs, new Func1() {
    +            @Override
    +            public Boolean call(String s) {
    +                return s.length() == 3;
    +            }
    +        })).subscribe(observer);
    +
    +        verify(observer).onNext(true);
    +        verify(observer).onCompleted();
    +        verifyNoMoreInteractions(observer);
    +    }
    +
    +    @Test
    +    @SuppressWarnings("unchecked")
    +    public void testError() {
    +        Throwable error = new Throwable();
    +        Observable obs = Observable.error(error);
    +
    +        Observer observer = mock(Observer.class);
    +        Observable.create(all(obs, new Func1() {
    +            @Override
    +            public Boolean call(String s) {
    +                return s.length() == 3;
    +            }
    +        })).subscribe(observer);
    +
    +        verify(observer).onError(error);
    +        verifyNoMoreInteractions(observer);
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java b/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java
    index 801a35d0c3..f9e64791d8 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java
    @@ -1,182 +1,182 @@
     package rx.operators;
     
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationAny.*;
    +
     import org.junit.Test;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.util.functions.Func1;
     
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationAny.any;
    -import static rx.operators.OperationAny.*;
    -
     public class OperationAnyTest {
     
    -  @Test
    -  public void testAnyWithTwoItems() {
    -    Observable w = Observable.from(1, 2);
    -    Observable observable = Observable.create(any(w));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, never()).onNext(false);
    -    verify(aObserver, times(1)).onNext(true);
    -    verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testIsEmptyWithTwoItems() {
    -    Observable w = Observable.from(1, 2);
    -    Observable observable = Observable.create(isEmpty(w));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, never()).onNext(true);
    -    verify(aObserver, times(1)).onNext(false);
    -    verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testAnyWithOneItem() {
    -    Observable w = Observable.from(1);
    -    Observable observable = Observable.create(any(w));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, never()).onNext(false);
    -    verify(aObserver, times(1)).onNext(true);
    -    verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testIsEmptyWithOneItem() {
    -    Observable w = Observable.from(1);
    -    Observable observable = Observable.create(isEmpty(w));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, never()).onNext(true);
    -    verify(aObserver, times(1)).onNext(false);
    -    verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testAnyWithEmpty() {
    -    Observable w = Observable.empty();
    -    Observable observable = Observable.create(any(w));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, times(1)).onNext(false);
    -    verify(aObserver, never()).onNext(true);
    -    verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testIsEmptyWithEmpty() {
    -    Observable w = Observable.empty();
    -    Observable observable = Observable.create(isEmpty(w));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, times(1)).onNext(true);
    -    verify(aObserver, never()).onNext(false);
    -    verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testAnyWithPredicate1() {
    -    Observable w = Observable.from(1, 2, 3);
    -    Observable observable = Observable.create(any(w,
    -        new Func1() {
    -
    -          @Override
    -          public Boolean call(Integer t1) {
    -            return t1 < 2;
    -          }
    -        }));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, never()).onNext(false);
    -    verify(aObserver, times(1)).onNext(true);
    -    verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testExists1() {
    -    Observable w = Observable.from(1, 2, 3);
    -    Observable observable = Observable.create(exists(w,
    -        new Func1() {
    -
    -          @Override
    -          public Boolean call(Integer t1) {
    -            return t1 < 2;
    -          }
    -        }));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, never()).onNext(false);
    -    verify(aObserver, times(1)).onNext(true);
    -    verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testAnyWithPredicate2() {
    -    Observable w = Observable.from(1, 2, 3);
    -    Observable observable = Observable.create(any(w,
    -        new Func1() {
    -
    -          @Override
    -          public Boolean call(Integer t1) {
    -            return t1 < 1;
    -          }
    -        }));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, times(1)).onNext(false);
    -    verify(aObserver, never()).onNext(true);
    -    verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testAnyWithEmptyAndPredicate() {
    -    // If the source is empty, always output false.
    -    Observable w = Observable.empty();
    -    Observable observable = Observable.create(any(w,
    -        new Func1() {
    -
    -          @Override
    -          public Boolean call(Integer t1) {
    -            return true;
    -          }
    -        }));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, times(1)).onNext(false);
    -    verify(aObserver, never()).onNext(true);
    -    verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    +    @Test
    +    public void testAnyWithTwoItems() {
    +        Observable w = Observable.from(1, 2);
    +        Observable observable = Observable.create(any(w));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, never()).onNext(false);
    +        verify(aObserver, times(1)).onNext(true);
    +        verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testIsEmptyWithTwoItems() {
    +        Observable w = Observable.from(1, 2);
    +        Observable observable = Observable.create(isEmpty(w));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, never()).onNext(true);
    +        verify(aObserver, times(1)).onNext(false);
    +        verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testAnyWithOneItem() {
    +        Observable w = Observable.from(1);
    +        Observable observable = Observable.create(any(w));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, never()).onNext(false);
    +        verify(aObserver, times(1)).onNext(true);
    +        verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testIsEmptyWithOneItem() {
    +        Observable w = Observable.from(1);
    +        Observable observable = Observable.create(isEmpty(w));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, never()).onNext(true);
    +        verify(aObserver, times(1)).onNext(false);
    +        verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testAnyWithEmpty() {
    +        Observable w = Observable.empty();
    +        Observable observable = Observable.create(any(w));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, times(1)).onNext(false);
    +        verify(aObserver, never()).onNext(true);
    +        verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testIsEmptyWithEmpty() {
    +        Observable w = Observable.empty();
    +        Observable observable = Observable.create(isEmpty(w));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, times(1)).onNext(true);
    +        verify(aObserver, never()).onNext(false);
    +        verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testAnyWithPredicate1() {
    +        Observable w = Observable.from(1, 2, 3);
    +        Observable observable = Observable.create(any(w,
    +                new Func1() {
    +
    +                    @Override
    +                    public Boolean call(Integer t1) {
    +                        return t1 < 2;
    +                    }
    +                }));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, never()).onNext(false);
    +        verify(aObserver, times(1)).onNext(true);
    +        verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testExists1() {
    +        Observable w = Observable.from(1, 2, 3);
    +        Observable observable = Observable.create(exists(w,
    +                new Func1() {
    +
    +                    @Override
    +                    public Boolean call(Integer t1) {
    +                        return t1 < 2;
    +                    }
    +                }));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, never()).onNext(false);
    +        verify(aObserver, times(1)).onNext(true);
    +        verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testAnyWithPredicate2() {
    +        Observable w = Observable.from(1, 2, 3);
    +        Observable observable = Observable.create(any(w,
    +                new Func1() {
    +
    +                    @Override
    +                    public Boolean call(Integer t1) {
    +                        return t1 < 1;
    +                    }
    +                }));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, times(1)).onNext(false);
    +        verify(aObserver, never()).onNext(true);
    +        verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testAnyWithEmptyAndPredicate() {
    +        // If the source is empty, always output false.
    +        Observable w = Observable.empty();
    +        Observable observable = Observable.create(any(w,
    +                new Func1() {
    +
    +                    @Override
    +                    public Boolean call(Integer t1) {
    +                        return true;
    +                    }
    +                }));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, times(1)).onNext(false);
    +        verify(aObserver, never()).onNext(true);
    +        verify(aObserver, never()).onError(org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java b/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java
    index 45e23bf2b9..5985959669 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java
    @@ -1,111 +1,106 @@
     package rx.operators;
     
    -import org.junit.Test;
    -import rx.Observable;
    -import rx.Observer;
    -
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Matchers.anyDouble;
    -import static org.mockito.Matchers.anyFloat;
    -import static org.mockito.Matchers.anyInt;
    -import static org.mockito.Matchers.anyLong;
    +import static org.mockito.Matchers.*;
     import static org.mockito.Mockito.*;
     import static rx.operators.OperationAverage.*;
     
    +import org.junit.Test;
    +
    +import rx.Observable;
    +import rx.Observer;
     
     public class OperationAverageTest {
     
    -  @SuppressWarnings("unchecked")
    -  Observer w = mock(Observer.class);
    -  @SuppressWarnings("unchecked")
    -  Observer wl = mock(Observer.class);
    -  @SuppressWarnings("unchecked")
    -  Observer wf = mock(Observer.class);
    -  @SuppressWarnings("unchecked")
    -  Observer wd = mock(Observer.class);
    -
    -  @Test
    -  public void testAverageOfAFewInts() throws Throwable {
    -    Observable src = Observable.from(1, 2, 3, 4, 6);
    -    average(src).subscribe(w);
    -
    -    verify(w, times(1)).onNext(anyInt());
    -    verify(w).onNext(3);
    -    verify(w, never()).onError(any(Throwable.class));
    -    verify(w, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testEmptyAverage() throws Throwable {
    -    Observable src = Observable.empty();
    -    average(src).subscribe(w);
    -
    -    verify(w, never()).onNext(anyInt());
    -    verify(w, times(1)).onError(any(ArithmeticException.class));
    -    verify(w, never()).onCompleted();
    -  }
    -
    -  @Test
    -  public void testAverageOfAFewLongs() throws Throwable {
    -    Observable src = Observable.from(1L, 2L, 3L, 4L, 6L);
    -    averageLongs(src).subscribe(wl);
    -
    -    verify(wl, times(1)).onNext(anyLong());
    -    verify(wl).onNext(3L);
    -    verify(wl, never()).onError(any(Throwable.class));
    -    verify(wl, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testEmptyAverageLongs() throws Throwable {
    -    Observable src = Observable.empty();
    -    averageLongs(src).subscribe(wl);
    -
    -    verify(wl, never()).onNext(anyLong());
    -    verify(wl, times(1)).onError(any(ArithmeticException.class));
    -    verify(wl, never()).onCompleted();
    -  }
    -
    -  @Test
    -  public void testAverageOfAFewFloats() throws Throwable {
    -    Observable src = Observable.from(1.0f, 2.0f);
    -    averageFloats(src).subscribe(wf);
    -
    -    verify(wf, times(1)).onNext(anyFloat());
    -    verify(wf).onNext(1.5f);
    -    verify(wf, never()).onError(any(Throwable.class));
    -    verify(wf, times(1)).onCompleted();
    -  }
    -
    -
    -  @Test
    -  public void testEmptyAverageFloats() throws Throwable {
    -    Observable src = Observable.empty();
    -    averageFloats(src).subscribe(wf);
    -
    -    verify(wf, never()).onNext(anyFloat());
    -    verify(wf, times(1)).onError(any(ArithmeticException.class));
    -    verify(wf, never()).onCompleted();
    -  }
    -
    -  @Test
    -  public void testAverageOfAFewDoubles() throws Throwable {
    -    Observable src = Observable.from(1.0d, 2.0d);
    -    averageDoubles(src).subscribe(wd);
    -
    -    verify(wd, times(1)).onNext(anyDouble());
    -    verify(wd).onNext(1.5d);
    -    verify(wd, never()).onError(any(Throwable.class));
    -    verify(wd, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testEmptyAverageDoubles() throws Throwable {
    -    Observable src = Observable.empty();
    -    averageDoubles(src).subscribe(wd);
    -
    -    verify(wd, never()).onNext(anyDouble());
    -    verify(wd, times(1)).onError(any(ArithmeticException.class));
    -    verify(wd, never()).onCompleted();
    -  }
    +    @SuppressWarnings("unchecked")
    +    Observer w = mock(Observer.class);
    +    @SuppressWarnings("unchecked")
    +    Observer wl = mock(Observer.class);
    +    @SuppressWarnings("unchecked")
    +    Observer wf = mock(Observer.class);
    +    @SuppressWarnings("unchecked")
    +    Observer wd = mock(Observer.class);
    +
    +    @Test
    +    public void testAverageOfAFewInts() throws Throwable {
    +        Observable src = Observable.from(1, 2, 3, 4, 6);
    +        average(src).subscribe(w);
    +
    +        verify(w, times(1)).onNext(anyInt());
    +        verify(w).onNext(3);
    +        verify(w, never()).onError(any(Throwable.class));
    +        verify(w, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testEmptyAverage() throws Throwable {
    +        Observable src = Observable.empty();
    +        average(src).subscribe(w);
    +
    +        verify(w, never()).onNext(anyInt());
    +        verify(w, times(1)).onError(any(ArithmeticException.class));
    +        verify(w, never()).onCompleted();
    +    }
    +
    +    @Test
    +    public void testAverageOfAFewLongs() throws Throwable {
    +        Observable src = Observable.from(1L, 2L, 3L, 4L, 6L);
    +        averageLongs(src).subscribe(wl);
    +
    +        verify(wl, times(1)).onNext(anyLong());
    +        verify(wl).onNext(3L);
    +        verify(wl, never()).onError(any(Throwable.class));
    +        verify(wl, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testEmptyAverageLongs() throws Throwable {
    +        Observable src = Observable.empty();
    +        averageLongs(src).subscribe(wl);
    +
    +        verify(wl, never()).onNext(anyLong());
    +        verify(wl, times(1)).onError(any(ArithmeticException.class));
    +        verify(wl, never()).onCompleted();
    +    }
    +
    +    @Test
    +    public void testAverageOfAFewFloats() throws Throwable {
    +        Observable src = Observable.from(1.0f, 2.0f);
    +        averageFloats(src).subscribe(wf);
    +
    +        verify(wf, times(1)).onNext(anyFloat());
    +        verify(wf).onNext(1.5f);
    +        verify(wf, never()).onError(any(Throwable.class));
    +        verify(wf, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testEmptyAverageFloats() throws Throwable {
    +        Observable src = Observable.empty();
    +        averageFloats(src).subscribe(wf);
    +
    +        verify(wf, never()).onNext(anyFloat());
    +        verify(wf, times(1)).onError(any(ArithmeticException.class));
    +        verify(wf, never()).onCompleted();
    +    }
    +
    +    @Test
    +    public void testAverageOfAFewDoubles() throws Throwable {
    +        Observable src = Observable.from(1.0d, 2.0d);
    +        averageDoubles(src).subscribe(wd);
    +
    +        verify(wd, times(1)).onNext(anyDouble());
    +        verify(wd).onNext(1.5d);
    +        verify(wd, never()).onError(any(Throwable.class));
    +        verify(wd, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testEmptyAverageDoubles() throws Throwable {
    +        Observable src = Observable.empty();
    +        averageDoubles(src).subscribe(wd);
    +
    +        verify(wd, never()).onNext(anyDouble());
    +        verify(wd, times(1)).onError(any(ArithmeticException.class));
    +        verify(wd, never()).onCompleted();
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java b/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java
    index fdc44e4014..c9f0e8b8e7 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java
    @@ -1,9 +1,18 @@
     package rx.operators;
     
    +import static org.junit.Assert.*;
    +import static rx.operators.OperationBuffer.*;
    +
    +import java.util.ArrayList;
    +import java.util.List;
    +import java.util.concurrent.CountDownLatch;
    +import java.util.concurrent.TimeUnit;
    +
     import org.junit.Before;
     import org.junit.Test;
     import org.mockito.InOrder;
     import org.mockito.Mockito;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.Subscription;
    @@ -18,333 +27,325 @@
     import rx.util.functions.Func0;
     import rx.util.functions.Func1;
     
    -import java.util.ArrayList;
    -import java.util.List;
    -import java.util.concurrent.CountDownLatch;
    -import java.util.concurrent.TimeUnit;
    +public class OperationBufferTest {
     
    -import static org.junit.Assert.assertFalse;
    -import static rx.operators.OperationBuffer.buffer;
    +    private Observer> observer;
    +    private TestScheduler scheduler;
     
    -public class OperationBufferTest {
    +    @Before
    +    @SuppressWarnings("unchecked")
    +    public void before() {
    +        observer = Mockito.mock(Observer.class);
    +        scheduler = new TestScheduler();
    +    }
    +
    +    @Test
    +    public void testComplete() {
    +        Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                observer.onCompleted();
    +                return Subscriptions.empty();
    +            }
    +        });
    +
    +        Observable> buffered = Observable.create(buffer(source, 3, 3));
    +        buffered.subscribe(observer);
    +
    +        Mockito.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    +        Mockito.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    +        Mockito.verify(observer, Mockito.times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testSkipAndCountOverlappingBuffers() {
    +        Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                observer.onNext("one");
    +                observer.onNext("two");
    +                observer.onNext("three");
    +                observer.onNext("four");
    +                observer.onNext("five");
    +                return Subscriptions.empty();
    +            }
    +        });
    +
    +        Observable> buffered = Observable.create(buffer(source, 3, 1));
    +        buffered.subscribe(observer);
    +
    +        InOrder inOrder = Mockito.inOrder(observer);
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three"));
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("two", "three", "four"));
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four", "five"));
    +        inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    +        inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    +        inOrder.verify(observer, Mockito.never()).onCompleted();
    +    }
    +
    +    @Test
    +    public void testSkipAndCountGaplessBuffers() {
    +        Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                observer.onNext("one");
    +                observer.onNext("two");
    +                observer.onNext("three");
    +                observer.onNext("four");
    +                observer.onNext("five");
    +                observer.onCompleted();
    +                return Subscriptions.empty();
    +            }
    +        });
    +
    +        Observable> buffered = Observable.create(buffer(source, 3, 3));
    +        buffered.subscribe(observer);
    +
    +        InOrder inOrder = Mockito.inOrder(observer);
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three"));
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five"));
    +        inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    +        inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    +        inOrder.verify(observer, Mockito.times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testSkipAndCountBuffersWithGaps() {
    +        Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                observer.onNext("one");
    +                observer.onNext("two");
    +                observer.onNext("three");
    +                observer.onNext("four");
    +                observer.onNext("five");
    +                observer.onCompleted();
    +                return Subscriptions.empty();
    +            }
    +        });
    +
    +        Observable> buffered = Observable.create(buffer(source, 2, 3));
    +        buffered.subscribe(observer);
     
    -  private Observer> observer;
    -  private TestScheduler scheduler;
    -
    -  @Before
    -  @SuppressWarnings("unchecked")
    -  public void before() {
    -    observer = Mockito.mock(Observer.class);
    -    scheduler = new TestScheduler();
    -  }
    -
    -  @Test
    -  public void testComplete() {
    -    Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        observer.onCompleted();
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Observable> buffered = Observable.create(buffer(source, 3, 3));
    -    buffered.subscribe(observer);
    -
    -    Mockito.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    -    Mockito.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    -    Mockito.verify(observer, Mockito.times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testSkipAndCountOverlappingBuffers() {
    -    Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        observer.onNext("one");
    -        observer.onNext("two");
    -        observer.onNext("three");
    -        observer.onNext("four");
    -        observer.onNext("five");
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Observable> buffered = Observable.create(buffer(source, 3, 1));
    -    buffered.subscribe(observer);
    -
    -    InOrder inOrder = Mockito.inOrder(observer);
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three"));
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("two", "three", "four"));
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four", "five"));
    -    inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    -    inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    -    inOrder.verify(observer, Mockito.never()).onCompleted();
    -  }
    -
    -  @Test
    -  public void testSkipAndCountGaplessBuffers() {
    -    Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        observer.onNext("one");
    -        observer.onNext("two");
    -        observer.onNext("three");
    -        observer.onNext("four");
    -        observer.onNext("five");
    -        observer.onCompleted();
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Observable> buffered = Observable.create(buffer(source, 3, 3));
    -    buffered.subscribe(observer);
    -
    -    InOrder inOrder = Mockito.inOrder(observer);
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three"));
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five"));
    -    inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    -    inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    -    inOrder.verify(observer, Mockito.times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testSkipAndCountBuffersWithGaps() {
    -    Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        observer.onNext("one");
    -        observer.onNext("two");
    -        observer.onNext("three");
    -        observer.onNext("four");
    -        observer.onNext("five");
    -        observer.onCompleted();
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Observable> buffered = Observable.create(buffer(source, 2, 3));
    -    buffered.subscribe(observer);
    -
    -    InOrder inOrder = Mockito.inOrder(observer);
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two"));
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five"));
    -    inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    -    inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    -    inOrder.verify(observer, Mockito.times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testTimedAndCount() {
    -    Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        push(observer, "one", 10);
    -        push(observer, "two", 90);
    -        push(observer, "three", 110);
    -        push(observer, "four", 190);
    -        push(observer, "five", 210);
    -        complete(observer, 250);
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Observable> buffered = Observable.create(buffer(source, 100, TimeUnit.MILLISECONDS, 2, scheduler));
    -    buffered.subscribe(observer);
    -
    -    InOrder inOrder = Mockito.inOrder(observer);
    -    scheduler.advanceTimeTo(100, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two"));
    -
    -    scheduler.advanceTimeTo(200, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four"));
    -
    -    scheduler.advanceTimeTo(300, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("five"));
    -    inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    -    inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    -    inOrder.verify(observer, Mockito.times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testTimed() {
    -    Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        push(observer, "one", 98);
    -        push(observer, "two", 99);
    -        push(observer, "three", 100);
    -        push(observer, "four", 101);
    -        push(observer, "five", 102);
    -        complete(observer, 150);
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Observable> buffered = Observable.create(buffer(source, 100, TimeUnit.MILLISECONDS, scheduler));
    -    buffered.subscribe(observer);
    -
    -    InOrder inOrder = Mockito.inOrder(observer);
    -    scheduler.advanceTimeTo(101, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three"));
    -
    -    scheduler.advanceTimeTo(201, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five"));
    -    inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    -    inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    -    inOrder.verify(observer, Mockito.times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testObservableBasedOpenerAndCloser() {
    -    Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        push(observer, "one", 10);
    -        push(observer, "two", 60);
    -        push(observer, "three", 110);
    -        push(observer, "four", 160);
    -        push(observer, "five", 210);
    -        complete(observer, 500);
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Observable openings = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        push(observer, Openings.create(), 50);
    -        push(observer, Openings.create(), 200);
    -        complete(observer, 250);
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Func1> closer = new Func1>() {
    -      @Override
    -      public Observable call(Opening opening) {
    -        return Observable.create(new Observable.OnSubscribeFunc() {
    -          @Override
    -          public Subscription onSubscribe(Observer observer) {
    -            push(observer, Closings.create(), 100);
    -            complete(observer, 101);
    -            return Subscriptions.empty();
    -          }
    +        InOrder inOrder = Mockito.inOrder(observer);
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two"));
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five"));
    +        inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    +        inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    +        inOrder.verify(observer, Mockito.times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testTimedAndCount() {
    +        Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                push(observer, "one", 10);
    +                push(observer, "two", 90);
    +                push(observer, "three", 110);
    +                push(observer, "four", 190);
    +                push(observer, "five", 210);
    +                complete(observer, 250);
    +                return Subscriptions.empty();
    +            }
    +        });
    +
    +        Observable> buffered = Observable.create(buffer(source, 100, TimeUnit.MILLISECONDS, 2, scheduler));
    +        buffered.subscribe(observer);
    +
    +        InOrder inOrder = Mockito.inOrder(observer);
    +        scheduler.advanceTimeTo(100, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two"));
    +
    +        scheduler.advanceTimeTo(200, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four"));
    +
    +        scheduler.advanceTimeTo(300, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("five"));
    +        inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    +        inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    +        inOrder.verify(observer, Mockito.times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testTimed() {
    +        Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                push(observer, "one", 98);
    +                push(observer, "two", 99);
    +                push(observer, "three", 100);
    +                push(observer, "four", 101);
    +                push(observer, "five", 102);
    +                complete(observer, 150);
    +                return Subscriptions.empty();
    +            }
    +        });
    +
    +        Observable> buffered = Observable.create(buffer(source, 100, TimeUnit.MILLISECONDS, scheduler));
    +        buffered.subscribe(observer);
    +
    +        InOrder inOrder = Mockito.inOrder(observer);
    +        scheduler.advanceTimeTo(101, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two", "three"));
    +
    +        scheduler.advanceTimeTo(201, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("four", "five"));
    +        inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    +        inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    +        inOrder.verify(observer, Mockito.times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testObservableBasedOpenerAndCloser() {
    +        Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                push(observer, "one", 10);
    +                push(observer, "two", 60);
    +                push(observer, "three", 110);
    +                push(observer, "four", 160);
    +                push(observer, "five", 210);
    +                complete(observer, 500);
    +                return Subscriptions.empty();
    +            }
    +        });
    +
    +        Observable openings = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                push(observer, Openings.create(), 50);
    +                push(observer, Openings.create(), 200);
    +                complete(observer, 250);
    +                return Subscriptions.empty();
    +            }
             });
    -      }
    -    };
    -
    -    Observable> buffered = Observable.create(buffer(source, openings, closer));
    -    buffered.subscribe(observer);
    -
    -    InOrder inOrder = Mockito.inOrder(observer);
    -    scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("two", "three"));
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("five"));
    -    inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    -    inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    -    inOrder.verify(observer, Mockito.times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testObservableBasedCloser() {
    -    Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        push(observer, "one", 10);
    -        push(observer, "two", 60);
    -        push(observer, "three", 110);
    -        push(observer, "four", 160);
    -        push(observer, "five", 210);
    -        complete(observer, 250);
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Func0> closer = new Func0>() {
    -      @Override
    -      public Observable call() {
    -        return Observable.create(new Observable.OnSubscribeFunc() {
    -          @Override
    -          public Subscription onSubscribe(Observer observer) {
    -            push(observer, Closings.create(), 100);
    -            complete(observer, 101);
    -            return Subscriptions.empty();
    -          }
    +
    +        Func1> closer = new Func1>() {
    +            @Override
    +            public Observable call(Opening opening) {
    +                return Observable.create(new Observable.OnSubscribeFunc() {
    +                    @Override
    +                    public Subscription onSubscribe(Observer observer) {
    +                        push(observer, Closings.create(), 100);
    +                        complete(observer, 101);
    +                        return Subscriptions.empty();
    +                    }
    +                });
    +            }
    +        };
    +
    +        Observable> buffered = Observable.create(buffer(source, openings, closer));
    +        buffered.subscribe(observer);
    +
    +        InOrder inOrder = Mockito.inOrder(observer);
    +        scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("two", "three"));
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("five"));
    +        inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    +        inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    +        inOrder.verify(observer, Mockito.times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testObservableBasedCloser() {
    +        Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                push(observer, "one", 10);
    +                push(observer, "two", 60);
    +                push(observer, "three", 110);
    +                push(observer, "four", 160);
    +                push(observer, "five", 210);
    +                complete(observer, 250);
    +                return Subscriptions.empty();
    +            }
             });
    -      }
    -    };
    -
    -    Observable> buffered = Observable.create(buffer(source, closer));
    -    buffered.subscribe(observer);
    -
    -    InOrder inOrder = Mockito.inOrder(observer);
    -    scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two"));
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four"));
    -    inOrder.verify(observer, Mockito.times(1)).onNext(list("five"));
    -    inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    -    inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    -    inOrder.verify(observer, Mockito.times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testLongTimeAction() throws InterruptedException {
    -    final CountDownLatch latch = new CountDownLatch(1);
    -    LongTimeAction action = new LongTimeAction(latch);
    -    Observable.from(1).buffer(10, TimeUnit.MILLISECONDS, 10)
    -        .subscribe(action);
    -    latch.await();
    -    assertFalse(action.fail);
    -  }
    -
    -  private static class LongTimeAction implements Action1> {
    -
    -    CountDownLatch latch;
    -    boolean fail = false;
    -
    -    public LongTimeAction(CountDownLatch latch) {
    -      this.latch = latch;
    +
    +        Func0> closer = new Func0>() {
    +            @Override
    +            public Observable call() {
    +                return Observable.create(new Observable.OnSubscribeFunc() {
    +                    @Override
    +                    public Subscription onSubscribe(Observer observer) {
    +                        push(observer, Closings.create(), 100);
    +                        complete(observer, 101);
    +                        return Subscriptions.empty();
    +                    }
    +                });
    +            }
    +        };
    +
    +        Observable> buffered = Observable.create(buffer(source, closer));
    +        buffered.subscribe(observer);
    +
    +        InOrder inOrder = Mockito.inOrder(observer);
    +        scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("one", "two"));
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("three", "four"));
    +        inOrder.verify(observer, Mockito.times(1)).onNext(list("five"));
    +        inOrder.verify(observer, Mockito.never()).onNext(Mockito.anyListOf(String.class));
    +        inOrder.verify(observer, Mockito.never()).onError(Mockito.any(Throwable.class));
    +        inOrder.verify(observer, Mockito.times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testLongTimeAction() throws InterruptedException {
    +        final CountDownLatch latch = new CountDownLatch(1);
    +        LongTimeAction action = new LongTimeAction(latch);
    +        Observable.from(1).buffer(10, TimeUnit.MILLISECONDS, 10)
    +                .subscribe(action);
    +        latch.await();
    +        assertFalse(action.fail);
    +    }
    +
    +    private static class LongTimeAction implements Action1> {
    +
    +        CountDownLatch latch;
    +        boolean fail = false;
    +
    +        public LongTimeAction(CountDownLatch latch) {
    +            this.latch = latch;
    +        }
    +
    +        @Override
    +        public void call(List t1) {
    +            try {
    +                if (fail) {
    +                    return;
    +                }
    +                Thread.sleep(200);
    +            } catch (InterruptedException e) {
    +                fail = true;
    +            } finally {
    +                latch.countDown();
    +            }
    +        }
         }
     
    -    @Override
    -    public void call(List t1) {
    -      try {
    -        if (fail) {
    -          return;
    +    private List list(String... args) {
    +        List list = new ArrayList();
    +        for (String arg : args) {
    +            list.add(arg);
             }
    -        Thread.sleep(200);
    -      } catch (InterruptedException e) {
    -        fail = true;
    -      } finally {
    -        latch.countDown();
    -      }
    +        return list;
    +    }
    +
    +    private  void push(final Observer observer, final T value, int delay) {
    +        scheduler.schedule(new Action0() {
    +            @Override
    +            public void call() {
    +                observer.onNext(value);
    +            }
    +        }, delay, TimeUnit.MILLISECONDS);
         }
    -  }
     
    -  private List list(String... args) {
    -    List list = new ArrayList();
    -    for (String arg : args) {
    -      list.add(arg);
    +    private void complete(final Observer observer, int delay) {
    +        scheduler.schedule(new Action0() {
    +            @Override
    +            public void call() {
    +                observer.onCompleted();
    +            }
    +        }, delay, TimeUnit.MILLISECONDS);
         }
    -    return list;
    -  }
    -
    -  private  void push(final Observer observer, final T value, int delay) {
    -    scheduler.schedule(new Action0() {
    -      @Override
    -      public void call() {
    -        observer.onNext(value);
    -      }
    -    }, delay, TimeUnit.MILLISECONDS);
    -  }
    -
    -  private void complete(final Observer observer, int delay) {
    -    scheduler.schedule(new Action0() {
    -      @Override
    -      public void call() {
    -        observer.onCompleted();
    -      }
    -    }, delay, TimeUnit.MILLISECONDS);
    -  }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java b/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java
    index 9f9ab051a1..60f4b5f4fc 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java
    @@ -1,72 +1,72 @@
     package rx.operators;
     
    +import static org.junit.Assert.*;
    +import static rx.operators.OperationCache.*;
    +
    +import java.util.concurrent.CountDownLatch;
    +import java.util.concurrent.TimeUnit;
    +import java.util.concurrent.atomic.AtomicInteger;
    +
     import org.junit.Test;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.Subscription;
     import rx.subscriptions.BooleanSubscription;
     import rx.util.functions.Action1;
     
    -import java.util.concurrent.CountDownLatch;
    -import java.util.concurrent.TimeUnit;
    -import java.util.concurrent.atomic.AtomicInteger;
    -
    -import static org.junit.Assert.assertEquals;
    -import static org.junit.Assert.fail;
    -import static rx.operators.OperationCache.cache;
    -
     public class OperationCacheTest {
     
    -  @Test
    -  public void testCache() throws InterruptedException {
    -    final AtomicInteger counter = new AtomicInteger();
    -    Observable o = Observable.create(cache(Observable.create(new Observable.OnSubscribeFunc() {
    +    @Test
    +    public void testCache() throws InterruptedException {
    +        final AtomicInteger counter = new AtomicInteger();
    +        Observable o = Observable.create(cache(Observable.create(new Observable.OnSubscribeFunc() {
     
    -      @Override
    -      public Subscription onSubscribe(final Observer observer) {
    -        final BooleanSubscription subscription = new BooleanSubscription();
    -        new Thread(new Runnable() {
    +            @Override
    +            public Subscription onSubscribe(final Observer observer) {
    +                final BooleanSubscription subscription = new BooleanSubscription();
    +                new Thread(new Runnable() {
     
    -          @Override
    -          public void run() {
    -            counter.incrementAndGet();
    -            System.out.println("published observable being executed");
    -            observer.onNext("one");
    -            observer.onCompleted();
    -          }
    -        }).start();
    -        return subscription;
    -      }
    -    })));
    +                    @Override
    +                    public void run() {
    +                        counter.incrementAndGet();
    +                        System.out.println("published observable being executed");
    +                        observer.onNext("one");
    +                        observer.onCompleted();
    +                    }
    +                }).start();
    +                return subscription;
    +            }
    +        })));
     
    -    // we then expect the following 2 subscriptions to get that same value
    -    final CountDownLatch latch = new CountDownLatch(2);
    +        // we then expect the following 2 subscriptions to get that same value
    +        final CountDownLatch latch = new CountDownLatch(2);
     
    -    // subscribe once
    -    o.subscribe(new Action1() {
    +        // subscribe once
    +        o.subscribe(new Action1() {
     
    -      @Override
    -      public void call(String v) {
    -        assertEquals("one", v);
    -        System.out.println("v: " + v);
    -        latch.countDown();
    -      }
    -    });
    +            @Override
    +            public void call(String v) {
    +                assertEquals("one", v);
    +                System.out.println("v: " + v);
    +                latch.countDown();
    +            }
    +        });
     
    -    // subscribe again
    -    o.subscribe(new Action1() {
    +        // subscribe again
    +        o.subscribe(new Action1() {
     
    -      @Override
    -      public void call(String v) {
    -        assertEquals("one", v);
    -        System.out.println("v: " + v);
    -        latch.countDown();
    -      }
    -    });
    +            @Override
    +            public void call(String v) {
    +                assertEquals("one", v);
    +                System.out.println("v: " + v);
    +                latch.countDown();
    +            }
    +        });
     
    -    if (!latch.await(1000, TimeUnit.MILLISECONDS)) {
    -      fail("subscriptions did not receive values");
    +        if (!latch.await(1000, TimeUnit.MILLISECONDS)) {
    +            fail("subscriptions did not receive values");
    +        }
    +        assertEquals(1, counter.get());
         }
    -    assertEquals(1, counter.get());
    -  }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationCastTest.java b/rxjava-core/src/test/java/rx/operators/OperationCastTest.java
    index 1a87c915fc..f5c0b6b515 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationCastTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationCastTest.java
    @@ -1,40 +1,41 @@
     package rx.operators;
     
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationCast.*;
    +
     import org.junit.Test;
    +
     import rx.Observable;
     import rx.Observer;
     
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationCast.cast;
    -
     public class OperationCastTest {
     
    -  @Test
    -  public void testCast() {
    -    Observable source = Observable.from(1, 2);
    -    Observable observable = Observable.create(cast(source,
    -        Integer.class));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, times(1)).onNext(1);
    -    verify(aObserver, times(1)).onNext(1);
    -    verify(aObserver, never()).onError(
    -        org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testCastWithWrongType() {
    -    Observable source = Observable.from(1, 2);
    -    Observable observable = Observable.create(cast(source,
    -        Boolean.class));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, times(1)).onError(
    -        org.mockito.Matchers.any(ClassCastException.class));
    -  }
    +    @Test
    +    public void testCast() {
    +        Observable source = Observable.from(1, 2);
    +        Observable observable = Observable.create(cast(source,
    +                Integer.class));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, times(1)).onNext(1);
    +        verify(aObserver, times(1)).onNext(1);
    +        verify(aObserver, never()).onError(
    +                org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testCastWithWrongType() {
    +        Observable source = Observable.from(1, 2);
    +        Observable observable = Observable.create(cast(source,
    +                Boolean.class));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, times(1)).onError(
    +                org.mockito.Matchers.any(ClassCastException.class));
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java b/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java
    index b7ea62a8fd..b5bb863b2e 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java
    @@ -1,619 +1,621 @@
     package rx.operators;
     
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationCombineLatest.*;
    +
    +import java.util.Arrays;
    +
     import org.junit.Test;
     import org.mockito.InOrder;
     import org.mockito.Matchers;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.Subscription;
    +import rx.operators.OperationCombineLatest.Aggregator;
    +import rx.operators.OperationCombineLatest.CombineObserver;
     import rx.subscriptions.Subscriptions;
     import rx.util.functions.Func2;
     import rx.util.functions.Func3;
     import rx.util.functions.FuncN;
     
    -import java.util.Arrays;
    +public class OperationCombineLatestTest {
     
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Matchers.anyString;
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationCombineLatest.Aggregator;
    -import static rx.operators.OperationCombineLatest.CombineObserver;
    -import static rx.operators.OperationCombineLatest.combineLatest;
    +    @Test
    +    public void testCombineLatestWithFunctionThatThrowsAnException() {
    +        @SuppressWarnings("unchecked")
    +        // mock calls don't do generics
    +        Observer w = mock(Observer.class);
     
    -public class OperationCombineLatestTest {
    +        TestObservable w1 = new TestObservable();
    +        TestObservable w2 = new TestObservable();
    +
    +        Observable combined = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), new Func2() {
    +            @Override
    +            public String call(String v1, String v2) {
    +                throw new RuntimeException("I don't work.");
    +            }
    +        }));
    +        combined.subscribe(w);
     
    -  @Test
    -  public void testCombineLatestWithFunctionThatThrowsAnException() {
    -    @SuppressWarnings("unchecked") // mock calls don't do generics
    +        w1.observer.onNext("first value of w1");
    +        w2.observer.onNext("first value of w2");
    +
    +        verify(w, never()).onNext(anyString());
    +        verify(w, never()).onCompleted();
    +        verify(w, times(1)).onError(Matchers. any());
    +    }
    +
    +    @Test
    +    public void testCombineLatestDifferentLengthObservableSequences1() {
    +        @SuppressWarnings("unchecked")
    +        // mock calls don't do generics
             Observer w = mock(Observer.class);
     
    -    TestObservable w1 = new TestObservable();
    -    TestObservable w2 = new TestObservable();
    +        TestObservable w1 = new TestObservable();
    +        TestObservable w2 = new TestObservable();
    +        TestObservable w3 = new TestObservable();
    +
    +        Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction()));
    +        combineLatestW.subscribe(w);
    +
    +        /* simulate sending data */
    +        // once for w1
    +        w1.observer.onNext("1a");
    +        w2.observer.onNext("2a");
    +        w3.observer.onNext("3a");
    +        w1.observer.onCompleted();
    +        // twice for w2
    +        w2.observer.onNext("2b");
    +        w2.observer.onCompleted();
    +        // 4 times for w3
    +        w3.observer.onNext("3b");
    +        w3.observer.onNext("3c");
    +        w3.observer.onNext("3d");
    +        w3.observer.onCompleted();
    +
    +        /* we should have been called 4 times on the Observer */
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w).onNext("1a2a3a");
    +        inOrder.verify(w).onNext("1a2b3a");
    +        inOrder.verify(w).onNext("1a2b3b");
    +        inOrder.verify(w).onNext("1a2b3c");
    +        inOrder.verify(w).onNext("1a2b3d");
    +        inOrder.verify(w, never()).onNext(anyString());
    +        inOrder.verify(w, times(1)).onCompleted();
    +    }
     
    -    Observable combined = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), new Func2() {
    -      @Override
    -      public String call(String v1, String v2) {
    -        throw new RuntimeException("I don't work.");
    -      }
    -    }));
    -    combined.subscribe(w);
    +    @Test
    +    public void testCombineLatestDifferentLengthObservableSequences2() {
    +        @SuppressWarnings("unchecked")
    +        Observer w = mock(Observer.class);
     
    -    w1.observer.onNext("first value of w1");
    -    w2.observer.onNext("first value of w2");
    +        TestObservable w1 = new TestObservable();
    +        TestObservable w2 = new TestObservable();
    +        TestObservable w3 = new TestObservable();
    +
    +        Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction()));
    +        combineLatestW.subscribe(w);
    +
    +        /* simulate sending data */
    +        // 4 times for w1
    +        w1.observer.onNext("1a");
    +        w1.observer.onNext("1b");
    +        w1.observer.onNext("1c");
    +        w1.observer.onNext("1d");
    +        w1.observer.onCompleted();
    +        // twice for w2
    +        w2.observer.onNext("2a");
    +        w2.observer.onNext("2b");
    +        w2.observer.onCompleted();
    +        // 1 times for w3
    +        w3.observer.onNext("3a");
    +        w3.observer.onCompleted();
    +
    +        /* we should have been called 1 time only on the Observer since we only combine the "latest" we don't go back and loop through others once completed */
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("1d2b3a");
    +        inOrder.verify(w, never()).onNext(anyString());
    +
    +        inOrder.verify(w, times(1)).onCompleted();
     
    -    verify(w, never()).onNext(anyString());
    -    verify(w, never()).onCompleted();
    -    verify(w, times(1)).onError(Matchers.any());
    -  }
    +    }
     
    -  @Test
    -  public void testCombineLatestDifferentLengthObservableSequences1() {
    -    @SuppressWarnings("unchecked") // mock calls don't do generics
    +    @Test
    +    public void testCombineLatestWithInterleavingSequences() {
    +        @SuppressWarnings("unchecked")
             Observer w = mock(Observer.class);
     
    -    TestObservable w1 = new TestObservable();
    -    TestObservable w2 = new TestObservable();
    -    TestObservable w3 = new TestObservable();
    -
    -    Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction()));
    -    combineLatestW.subscribe(w);
    -
    -            /* simulate sending data */
    -    // once for w1
    -    w1.observer.onNext("1a");
    -    w2.observer.onNext("2a");
    -    w3.observer.onNext("3a");
    -    w1.observer.onCompleted();
    -    // twice for w2
    -    w2.observer.onNext("2b");
    -    w2.observer.onCompleted();
    -    // 4 times for w3
    -    w3.observer.onNext("3b");
    -    w3.observer.onNext("3c");
    -    w3.observer.onNext("3d");
    -    w3.observer.onCompleted();
    -
    -            /* we should have been called 4 times on the Observer */
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w).onNext("1a2a3a");
    -    inOrder.verify(w).onNext("1a2b3a");
    -    inOrder.verify(w).onNext("1a2b3b");
    -    inOrder.verify(w).onNext("1a2b3c");
    -    inOrder.verify(w).onNext("1a2b3d");
    -    inOrder.verify(w, never()).onNext(anyString());
    -    inOrder.verify(w, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testCombineLatestDifferentLengthObservableSequences2() {
    +        TestObservable w1 = new TestObservable();
    +        TestObservable w2 = new TestObservable();
    +        TestObservable w3 = new TestObservable();
    +
    +        Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction()));
    +        combineLatestW.subscribe(w);
    +
    +        /* simulate sending data */
    +        w1.observer.onNext("1a");
    +        w2.observer.onNext("2a");
    +        w2.observer.onNext("2b");
    +        w3.observer.onNext("3a");
    +
    +        w1.observer.onNext("1b");
    +        w2.observer.onNext("2c");
    +        w2.observer.onNext("2d");
    +        w3.observer.onNext("3b");
    +
    +        w1.observer.onCompleted();
    +        w2.observer.onCompleted();
    +        w3.observer.onCompleted();
    +
    +        /* we should have been called 5 times on the Observer */
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w).onNext("1a2b3a");
    +        inOrder.verify(w).onNext("1b2b3a");
    +        inOrder.verify(w).onNext("1b2c3a");
    +        inOrder.verify(w).onNext("1b2d3a");
    +        inOrder.verify(w).onNext("1b2d3b");
    +
    +        inOrder.verify(w, never()).onNext(anyString());
    +        inOrder.verify(w, times(1)).onCompleted();
    +    }
    +
    +    /**
    +     * Testing internal private logic due to the complexity so I want to use TDD to test as a I build it rather than relying purely on the overall functionality expected by the public methods.
    +     */
    +    @SuppressWarnings("unchecked")
    +    /* mock calls don't do generics */
    +    @Test
    +    public void testAggregatorSimple() {
    +        FuncN combineLatestFunction = getConcatCombineLatestFunction();
    +        /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    +        Aggregator a = new Aggregator(combineLatestFunction);
    +
    +        /* define a Observer to receive aggregated events */
    +        Observer aObserver = mock(Observer.class);
    +        Observable.create(a).subscribe(aObserver);
    +
    +        /* mock the Observable Observers that are 'pushing' data for us */
    +        CombineObserver r1 = mock(CombineObserver.class);
    +        CombineObserver r2 = mock(CombineObserver.class);
    +
    +        /* pretend we're starting up */
    +        a.addObserver(r1);
    +        a.addObserver(r2);
    +
    +        /* simulate the Observables pushing data into the aggregator */
    +        a.next(r1, "hello");
    +        a.next(r2, "world");
    +
    +        InOrder inOrder = inOrder(aObserver);
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, never()).onCompleted();
    +        inOrder.verify(aObserver, times(1)).onNext("helloworld");
    +
    +        a.next(r1, "hello ");
    +        a.next(r2, "again");
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, never()).onCompleted();
    +        inOrder.verify(aObserver, times(1)).onNext("hello again");
    +
    +        a.complete(r1);
    +        a.complete(r2);
    +
    +        inOrder.verify(aObserver, never()).onNext(anyString());
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
         @SuppressWarnings("unchecked")
    -    Observer w = mock(Observer.class);
    -
    -    TestObservable w1 = new TestObservable();
    -    TestObservable w2 = new TestObservable();
    -    TestObservable w3 = new TestObservable();
    -
    -    Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction()));
    -    combineLatestW.subscribe(w);
    -
    -            /* simulate sending data */
    -    // 4 times for w1
    -    w1.observer.onNext("1a");
    -    w1.observer.onNext("1b");
    -    w1.observer.onNext("1c");
    -    w1.observer.onNext("1d");
    -    w1.observer.onCompleted();
    -    // twice for w2
    -    w2.observer.onNext("2a");
    -    w2.observer.onNext("2b");
    -    w2.observer.onCompleted();
    -    // 1 times for w3
    -    w3.observer.onNext("3a");
    -    w3.observer.onCompleted();
    -
    -            /* we should have been called 1 time only on the Observer since we only combine the "latest" we don't go back and loop through others once completed */
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("1d2b3a");
    -    inOrder.verify(w, never()).onNext(anyString());
    -
    -    inOrder.verify(w, times(1)).onCompleted();
    -
    -  }
    -
    -  @Test
    -  public void testCombineLatestWithInterleavingSequences() {
    +    /* mock calls don't do generics */
    +    @Test
    +    public void testAggregatorDifferentSizedResultsWithOnComplete() {
    +        FuncN combineLatestFunction = getConcatCombineLatestFunction();
    +        /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    +        Aggregator a = new Aggregator(combineLatestFunction);
    +
    +        /* define a Observer to receive aggregated events */
    +        Observer aObserver = mock(Observer.class);
    +        Observable.create(a).subscribe(aObserver);
    +
    +        /* mock the Observable Observers that are 'pushing' data for us */
    +        CombineObserver r1 = mock(CombineObserver.class);
    +        CombineObserver r2 = mock(CombineObserver.class);
    +
    +        /* pretend we're starting up */
    +        a.addObserver(r1);
    +        a.addObserver(r2);
    +
    +        /* simulate the Observables pushing data into the aggregator */
    +        a.next(r1, "hello");
    +        a.next(r2, "world");
    +        a.complete(r2);
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, never()).onCompleted();
    +        verify(aObserver, times(1)).onNext("helloworld");
    +
    +        a.next(r1, "hi");
    +        a.complete(r1);
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +        verify(aObserver, times(1)).onNext("hiworld");
    +    }
    +
         @SuppressWarnings("unchecked")
    -    Observer w = mock(Observer.class);
    -
    -    TestObservable w1 = new TestObservable();
    -    TestObservable w2 = new TestObservable();
    -    TestObservable w3 = new TestObservable();
    -
    -    Observable combineLatestW = Observable.create(combineLatest(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsCombineLatestFunction()));
    -    combineLatestW.subscribe(w);
    -
    -            /* simulate sending data */
    -    w1.observer.onNext("1a");
    -    w2.observer.onNext("2a");
    -    w2.observer.onNext("2b");
    -    w3.observer.onNext("3a");
    -
    -    w1.observer.onNext("1b");
    -    w2.observer.onNext("2c");
    -    w2.observer.onNext("2d");
    -    w3.observer.onNext("3b");
    -
    -    w1.observer.onCompleted();
    -    w2.observer.onCompleted();
    -    w3.observer.onCompleted();
    -
    -            /* we should have been called 5 times on the Observer */
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w).onNext("1a2b3a");
    -    inOrder.verify(w).onNext("1b2b3a");
    -    inOrder.verify(w).onNext("1b2c3a");
    -    inOrder.verify(w).onNext("1b2d3a");
    -    inOrder.verify(w).onNext("1b2d3b");
    -
    -    inOrder.verify(w, never()).onNext(anyString());
    -    inOrder.verify(w, times(1)).onCompleted();
    -  }
    -
    -  /**
    -   * Testing internal private logic due to the complexity so I want to use TDD to test as a I build it rather than relying purely on the overall functionality expected by the public methods.
    -   */
    -  @SuppressWarnings("unchecked")
    -        /* mock calls don't do generics */
    -  @Test
    -  public void testAggregatorSimple() {
    -    FuncN combineLatestFunction = getConcatCombineLatestFunction();
    -            /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    -    Aggregator a = new Aggregator(combineLatestFunction);
    -
    -            /* define a Observer to receive aggregated events */
    -    Observer aObserver = mock(Observer.class);
    -    Observable.create(a).subscribe(aObserver);
    -
    -            /* mock the Observable Observers that are 'pushing' data for us */
    -    CombineObserver r1 = mock(CombineObserver.class);
    -    CombineObserver r2 = mock(CombineObserver.class);
    -
    -            /* pretend we're starting up */
    -    a.addObserver(r1);
    -    a.addObserver(r2);
    -
    -            /* simulate the Observables pushing data into the aggregator */
    -    a.next(r1, "hello");
    -    a.next(r2, "world");
    -
    -    InOrder inOrder = inOrder(aObserver);
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, never()).onCompleted();
    -    inOrder.verify(aObserver, times(1)).onNext("helloworld");
    -
    -    a.next(r1, "hello ");
    -    a.next(r2, "again");
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, never()).onCompleted();
    -    inOrder.verify(aObserver, times(1)).onNext("hello again");
    -
    -    a.complete(r1);
    -    a.complete(r2);
    -
    -    inOrder.verify(aObserver, never()).onNext(anyString());
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @SuppressWarnings("unchecked")
    -        /* mock calls don't do generics */
    -  @Test
    -  public void testAggregatorDifferentSizedResultsWithOnComplete() {
    -    FuncN combineLatestFunction = getConcatCombineLatestFunction();
    -            /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    -    Aggregator a = new Aggregator(combineLatestFunction);
    -
    -            /* define a Observer to receive aggregated events */
    -    Observer aObserver = mock(Observer.class);
    -    Observable.create(a).subscribe(aObserver);
    -
    -            /* mock the Observable Observers that are 'pushing' data for us */
    -    CombineObserver r1 = mock(CombineObserver.class);
    -    CombineObserver r2 = mock(CombineObserver.class);
    -
    -            /* pretend we're starting up */
    -    a.addObserver(r1);
    -    a.addObserver(r2);
    -
    -            /* simulate the Observables pushing data into the aggregator */
    -    a.next(r1, "hello");
    -    a.next(r2, "world");
    -    a.complete(r2);
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, never()).onCompleted();
    -    verify(aObserver, times(1)).onNext("helloworld");
    -
    -    a.next(r1, "hi");
    -    a.complete(r1);
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -    verify(aObserver, times(1)).onNext("hiworld");
    -  }
    -
    -  @SuppressWarnings("unchecked")
    -        /* mock calls don't do generics */
    -  @Test
    -  public void testAggregateMultipleTypes() {
    -    FuncN combineLatestFunction = getConcatCombineLatestFunction();
    -            /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    -    Aggregator a = new Aggregator(combineLatestFunction);
    -
    -            /* define a Observer to receive aggregated events */
    -    Observer aObserver = mock(Observer.class);
    -    Observable.create(a).subscribe(aObserver);
    -
    -            /* mock the Observable Observers that are 'pushing' data for us */
    -    CombineObserver r1 = mock(CombineObserver.class);
    -    CombineObserver r2 = mock(CombineObserver.class);
    -
    -            /* pretend we're starting up */
    -    a.addObserver(r1);
    -    a.addObserver(r2);
    -
    -            /* simulate the Observables pushing data into the aggregator */
    -    a.next(r1, "hello");
    -    a.next(r2, "world");
    -    a.complete(r2);
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, never()).onCompleted();
    -    verify(aObserver, times(1)).onNext("helloworld");
    -
    -    a.next(r1, "hi");
    -    a.complete(r1);
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -    verify(aObserver, times(1)).onNext("hiworld");
    -  }
    -
    -  @SuppressWarnings("unchecked")
    -        /* mock calls don't do generics */
    -  @Test
    -  public void testAggregate3Types() {
    -    FuncN combineLatestFunction = getConcatCombineLatestFunction();
    -            /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    -    Aggregator a = new Aggregator(combineLatestFunction);
    -
    -            /* define a Observer to receive aggregated events */
    -    Observer aObserver = mock(Observer.class);
    -    Observable.create(a).subscribe(aObserver);
    -
    -            /* mock the Observable Observers that are 'pushing' data for us */
    -    CombineObserver r1 = mock(CombineObserver.class);
    -    CombineObserver r2 = mock(CombineObserver.class);
    -    CombineObserver r3 = mock(CombineObserver.class);
    -
    -            /* pretend we're starting up */
    -    a.addObserver(r1);
    -    a.addObserver(r2);
    -    a.addObserver(r3);
    -
    -            /* simulate the Observables pushing data into the aggregator */
    -    a.next(r1, "hello");
    -    a.next(r2, 2);
    -    a.next(r3, new int[]{5, 6, 7});
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, never()).onCompleted();
    -    verify(aObserver, times(1)).onNext("hello2[5, 6, 7]");
    -  }
    -
    -  @SuppressWarnings("unchecked")
    -        /* mock calls don't do generics */
    -  @Test
    -  public void testAggregatorsWithDifferentSizesAndTiming() {
    -    FuncN combineLatestFunction = getConcatCombineLatestFunction();
    -            /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    -    Aggregator a = new Aggregator(combineLatestFunction);
    -
    -            /* define a Observer to receive aggregated events */
    -    Observer aObserver = mock(Observer.class);
    -    Observable.create(a).subscribe(aObserver);
    -
    -            /* mock the Observable Observers that are 'pushing' data for us */
    -    CombineObserver r1 = mock(CombineObserver.class);
    -    CombineObserver r2 = mock(CombineObserver.class);
    -
    -            /* pretend we're starting up */
    -    a.addObserver(r1);
    -    a.addObserver(r2);
    -
    -            /* simulate the Observables pushing data into the aggregator */
    -    a.next(r1, "one");
    -    a.next(r1, "two");
    -    a.next(r1, "three");
    -    a.next(r2, "A");
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, never()).onCompleted();
    -    verify(aObserver, times(1)).onNext("threeA");
    -
    -    a.next(r1, "four");
    -    a.complete(r1);
    -    a.next(r2, "B");
    -    verify(aObserver, times(1)).onNext("fourB");
    -    a.next(r2, "C");
    -    verify(aObserver, times(1)).onNext("fourC");
    -    a.next(r2, "D");
    -    verify(aObserver, times(1)).onNext("fourD");
    -    a.next(r2, "E");
    -    verify(aObserver, times(1)).onNext("fourE");
    -    a.complete(r2);
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @SuppressWarnings("unchecked")
    -        /* mock calls don't do generics */
    -  @Test
    -  public void testAggregatorError() {
    -    FuncN combineLatestFunction = getConcatCombineLatestFunction();
    -            /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    -    Aggregator a = new Aggregator(combineLatestFunction);
    -
    -            /* define a Observer to receive aggregated events */
    -    Observer aObserver = mock(Observer.class);
    -    Observable.create(a).subscribe(aObserver);
    -
    -            /* mock the Observable Observers that are 'pushing' data for us */
    -    CombineObserver r1 = mock(CombineObserver.class);
    -    CombineObserver r2 = mock(CombineObserver.class);
    -
    -            /* pretend we're starting up */
    -    a.addObserver(r1);
    -    a.addObserver(r2);
    -
    -            /* simulate the Observables pushing data into the aggregator */
    -    a.next(r1, "hello");
    -    a.next(r2, "world");
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, never()).onCompleted();
    -    verify(aObserver, times(1)).onNext("helloworld");
    -
    -    a.error(new RuntimeException(""));
    -    a.next(r1, "hello");
    -    a.next(r2, "again");
    -
    -    verify(aObserver, times(1)).onError(any(Throwable.class));
    -    verify(aObserver, never()).onCompleted();
    -    // we don't want to be called again after an error
    -    verify(aObserver, times(0)).onNext("helloagain");
    -  }
    -
    -  @SuppressWarnings("unchecked")
    -        /* mock calls don't do generics */
    -  @Test
    -  public void testAggregatorUnsubscribe() {
    -    FuncN combineLatestFunction = getConcatCombineLatestFunction();
    -            /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    -    Aggregator a = new Aggregator(combineLatestFunction);
    -
    -            /* define a Observer to receive aggregated events */
    -    Observer aObserver = mock(Observer.class);
    -    Subscription subscription = Observable.create(a).subscribe(aObserver);
    -
    -            /* mock the Observable Observers that are 'pushing' data for us */
    -    CombineObserver r1 = mock(CombineObserver.class);
    -    CombineObserver r2 = mock(CombineObserver.class);
    -
    -            /* pretend we're starting up */
    -    a.addObserver(r1);
    -    a.addObserver(r2);
    -
    -            /* simulate the Observables pushing data into the aggregator */
    -    a.next(r1, "hello");
    -    a.next(r2, "world");
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, never()).onCompleted();
    -    verify(aObserver, times(1)).onNext("helloworld");
    -
    -    subscription.unsubscribe();
    -    a.next(r1, "hello");
    -    a.next(r2, "again");
    -
    -    verify(aObserver, times(0)).onError(any(Throwable.class));
    -    verify(aObserver, never()).onCompleted();
    -    // we don't want to be called again after an error
    -    verify(aObserver, times(0)).onNext("helloagain");
    -  }
    -
    -  @SuppressWarnings("unchecked")
    -        /* mock calls don't do generics */
    -  @Test
    -  public void testAggregatorEarlyCompletion() {
    -    FuncN combineLatestFunction = getConcatCombineLatestFunction();
    -            /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    -    Aggregator a = new Aggregator(combineLatestFunction);
    -
    -            /* define a Observer to receive aggregated events */
    -    Observer aObserver = mock(Observer.class);
    -    Observable.create(a).subscribe(aObserver);
    -
    -            /* mock the Observable Observers that are 'pushing' data for us */
    -    CombineObserver r1 = mock(CombineObserver.class);
    -    CombineObserver r2 = mock(CombineObserver.class);
    -
    -            /* pretend we're starting up */
    -    a.addObserver(r1);
    -    a.addObserver(r2);
    -
    -            /* simulate the Observables pushing data into the aggregator */
    -    a.next(r1, "one");
    -    a.next(r1, "two");
    -    a.complete(r1);
    -    a.next(r2, "A");
    -
    -    InOrder inOrder = inOrder(aObserver);
    -
    -    inOrder.verify(aObserver, never()).onError(any(Throwable.class));
    -    inOrder.verify(aObserver, never()).onCompleted();
    -    inOrder.verify(aObserver, times(1)).onNext("twoA");
    -
    -    a.complete(r2);
    -
    -    inOrder.verify(aObserver, never()).onError(any(Throwable.class));
    -    inOrder.verify(aObserver, times(1)).onCompleted();
    -    // we shouldn't get this since completed is called before any other onNext calls could trigger this
    -    inOrder.verify(aObserver, never()).onNext(anyString());
    -  }
    -
    -  @SuppressWarnings("unchecked")
    -        /* mock calls don't do generics */
    -  @Test
    -  public void testCombineLatest2Types() {
    -    Func2 combineLatestFunction = getConcatStringIntegerCombineLatestFunction();
    -
    -            /* define a Observer to receive aggregated events */
    -    Observer aObserver = mock(Observer.class);
    -
    -    Observable w = Observable.create(combineLatest(Observable.from("one", "two"), Observable.from(2, 3, 4), combineLatestFunction));
    -    w.subscribe(aObserver);
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -    verify(aObserver, times(1)).onNext("two2");
    -    verify(aObserver, times(1)).onNext("two3");
    -    verify(aObserver, times(1)).onNext("two4");
    -  }
    -
    -  @SuppressWarnings("unchecked")
    -        /* mock calls don't do generics */
    -  @Test
    -  public void testCombineLatest3TypesA() {
    -    Func3 combineLatestFunction = getConcatStringIntegerIntArrayCombineLatestFunction();
    -
    -            /* define a Observer to receive aggregated events */
    -    Observer aObserver = mock(Observer.class);
    -
    -    Observable w = Observable.create(combineLatest(Observable.from("one", "two"), Observable.from(2), Observable.from(new int[]{4, 5, 6}), combineLatestFunction));
    -    w.subscribe(aObserver);
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -    verify(aObserver, times(1)).onNext("two2[4, 5, 6]");
    -  }
    -
    -  @SuppressWarnings("unchecked")
    -        /* mock calls don't do generics */
    -  @Test
    -  public void testCombineLatest3TypesB() {
    -    Func3 combineLatestFunction = getConcatStringIntegerIntArrayCombineLatestFunction();
    -
    -            /* define a Observer to receive aggregated events */
    -    Observer aObserver = mock(Observer.class);
    -
    -    Observable w = Observable.create(combineLatest(Observable.from("one"), Observable.from(2), Observable.from(new int[]{4, 5, 6}, new int[]{7, 8}), combineLatestFunction));
    -    w.subscribe(aObserver);
    -
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -    verify(aObserver, times(1)).onNext("one2[4, 5, 6]");
    -    verify(aObserver, times(1)).onNext("one2[7, 8]");
    -  }
    -
    -  private Func3 getConcat3StringsCombineLatestFunction() {
    -    Func3 combineLatestFunction = new Func3() {
    -
    -      @Override
    -      public String call(String a1, String a2, String a3) {
    -        if (a1 == null) {
    -          a1 = "";
    -        }
    -        if (a2 == null) {
    -          a2 = "";
    -        }
    -        if (a3 == null) {
    -          a3 = "";
    -        }
    -        return a1 + a2 + a3;
    -      }
    -
    -    };
    -    return combineLatestFunction;
    -  }
    -
    -  private FuncN getConcatCombineLatestFunction() {
    -    FuncN combineLatestFunction = new FuncN() {
    -
    -      @Override
    -      public String call(Object... args) {
    -        String returnValue = "";
    -        for (Object o : args) {
    -          if (o != null) {
    -            returnValue += getStringValue(o);
    -          }
    -        }
    -        System.out.println("returning: " + returnValue);
    -        return returnValue;
    -      }
    -
    -    };
    -    return combineLatestFunction;
    -  }
    -
    -  private Func2 getConcatStringIntegerCombineLatestFunction() {
    -    Func2 combineLatestFunction = new Func2() {
    -
    -      @Override
    -      public String call(String s, Integer i) {
    -        return getStringValue(s) + getStringValue(i);
    -      }
    -
    -    };
    -    return combineLatestFunction;
    -  }
    -
    -  private Func3 getConcatStringIntegerIntArrayCombineLatestFunction() {
    -    Func3 combineLatestFunction = new Func3() {
    -
    -      @Override
    -      public String call(String s, Integer i, int[] iArray) {
    -        return getStringValue(s) + getStringValue(i) + getStringValue(iArray);
    -      }
    -
    -    };
    -    return combineLatestFunction;
    -  }
    -
    -  private static String getStringValue(Object o) {
    -    if (o == null) {
    -      return "";
    -    } else {
    -      if (o instanceof int[]) {
    -        return Arrays.toString((int[]) o);
    -      } else {
    -        return String.valueOf(o);
    -      }
    +    /* mock calls don't do generics */
    +    @Test
    +    public void testAggregateMultipleTypes() {
    +        FuncN combineLatestFunction = getConcatCombineLatestFunction();
    +        /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    +        Aggregator a = new Aggregator(combineLatestFunction);
    +
    +        /* define a Observer to receive aggregated events */
    +        Observer aObserver = mock(Observer.class);
    +        Observable.create(a).subscribe(aObserver);
    +
    +        /* mock the Observable Observers that are 'pushing' data for us */
    +        CombineObserver r1 = mock(CombineObserver.class);
    +        CombineObserver r2 = mock(CombineObserver.class);
    +
    +        /* pretend we're starting up */
    +        a.addObserver(r1);
    +        a.addObserver(r2);
    +
    +        /* simulate the Observables pushing data into the aggregator */
    +        a.next(r1, "hello");
    +        a.next(r2, "world");
    +        a.complete(r2);
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, never()).onCompleted();
    +        verify(aObserver, times(1)).onNext("helloworld");
    +
    +        a.next(r1, "hi");
    +        a.complete(r1);
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +        verify(aObserver, times(1)).onNext("hiworld");
    +    }
    +
    +    @SuppressWarnings("unchecked")
    +    /* mock calls don't do generics */
    +    @Test
    +    public void testAggregate3Types() {
    +        FuncN combineLatestFunction = getConcatCombineLatestFunction();
    +        /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    +        Aggregator a = new Aggregator(combineLatestFunction);
    +
    +        /* define a Observer to receive aggregated events */
    +        Observer aObserver = mock(Observer.class);
    +        Observable.create(a).subscribe(aObserver);
    +
    +        /* mock the Observable Observers that are 'pushing' data for us */
    +        CombineObserver r1 = mock(CombineObserver.class);
    +        CombineObserver r2 = mock(CombineObserver.class);
    +        CombineObserver r3 = mock(CombineObserver.class);
    +
    +        /* pretend we're starting up */
    +        a.addObserver(r1);
    +        a.addObserver(r2);
    +        a.addObserver(r3);
    +
    +        /* simulate the Observables pushing data into the aggregator */
    +        a.next(r1, "hello");
    +        a.next(r2, 2);
    +        a.next(r3, new int[] { 5, 6, 7 });
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, never()).onCompleted();
    +        verify(aObserver, times(1)).onNext("hello2[5, 6, 7]");
    +    }
    +
    +    @SuppressWarnings("unchecked")
    +    /* mock calls don't do generics */
    +    @Test
    +    public void testAggregatorsWithDifferentSizesAndTiming() {
    +        FuncN combineLatestFunction = getConcatCombineLatestFunction();
    +        /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    +        Aggregator a = new Aggregator(combineLatestFunction);
    +
    +        /* define a Observer to receive aggregated events */
    +        Observer aObserver = mock(Observer.class);
    +        Observable.create(a).subscribe(aObserver);
    +
    +        /* mock the Observable Observers that are 'pushing' data for us */
    +        CombineObserver r1 = mock(CombineObserver.class);
    +        CombineObserver r2 = mock(CombineObserver.class);
    +
    +        /* pretend we're starting up */
    +        a.addObserver(r1);
    +        a.addObserver(r2);
    +
    +        /* simulate the Observables pushing data into the aggregator */
    +        a.next(r1, "one");
    +        a.next(r1, "two");
    +        a.next(r1, "three");
    +        a.next(r2, "A");
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, never()).onCompleted();
    +        verify(aObserver, times(1)).onNext("threeA");
    +
    +        a.next(r1, "four");
    +        a.complete(r1);
    +        a.next(r2, "B");
    +        verify(aObserver, times(1)).onNext("fourB");
    +        a.next(r2, "C");
    +        verify(aObserver, times(1)).onNext("fourC");
    +        a.next(r2, "D");
    +        verify(aObserver, times(1)).onNext("fourD");
    +        a.next(r2, "E");
    +        verify(aObserver, times(1)).onNext("fourE");
    +        a.complete(r2);
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @SuppressWarnings("unchecked")
    +    /* mock calls don't do generics */
    +    @Test
    +    public void testAggregatorError() {
    +        FuncN combineLatestFunction = getConcatCombineLatestFunction();
    +        /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    +        Aggregator a = new Aggregator(combineLatestFunction);
    +
    +        /* define a Observer to receive aggregated events */
    +        Observer aObserver = mock(Observer.class);
    +        Observable.create(a).subscribe(aObserver);
    +
    +        /* mock the Observable Observers that are 'pushing' data for us */
    +        CombineObserver r1 = mock(CombineObserver.class);
    +        CombineObserver r2 = mock(CombineObserver.class);
    +
    +        /* pretend we're starting up */
    +        a.addObserver(r1);
    +        a.addObserver(r2);
    +
    +        /* simulate the Observables pushing data into the aggregator */
    +        a.next(r1, "hello");
    +        a.next(r2, "world");
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, never()).onCompleted();
    +        verify(aObserver, times(1)).onNext("helloworld");
    +
    +        a.error(new RuntimeException(""));
    +        a.next(r1, "hello");
    +        a.next(r2, "again");
    +
    +        verify(aObserver, times(1)).onError(any(Throwable.class));
    +        verify(aObserver, never()).onCompleted();
    +        // we don't want to be called again after an error
    +        verify(aObserver, times(0)).onNext("helloagain");
    +    }
    +
    +    @SuppressWarnings("unchecked")
    +    /* mock calls don't do generics */
    +    @Test
    +    public void testAggregatorUnsubscribe() {
    +        FuncN combineLatestFunction = getConcatCombineLatestFunction();
    +        /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    +        Aggregator a = new Aggregator(combineLatestFunction);
    +
    +        /* define a Observer to receive aggregated events */
    +        Observer aObserver = mock(Observer.class);
    +        Subscription subscription = Observable.create(a).subscribe(aObserver);
    +
    +        /* mock the Observable Observers that are 'pushing' data for us */
    +        CombineObserver r1 = mock(CombineObserver.class);
    +        CombineObserver r2 = mock(CombineObserver.class);
    +
    +        /* pretend we're starting up */
    +        a.addObserver(r1);
    +        a.addObserver(r2);
    +
    +        /* simulate the Observables pushing data into the aggregator */
    +        a.next(r1, "hello");
    +        a.next(r2, "world");
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, never()).onCompleted();
    +        verify(aObserver, times(1)).onNext("helloworld");
    +
    +        subscription.unsubscribe();
    +        a.next(r1, "hello");
    +        a.next(r2, "again");
    +
    +        verify(aObserver, times(0)).onError(any(Throwable.class));
    +        verify(aObserver, never()).onCompleted();
    +        // we don't want to be called again after an error
    +        verify(aObserver, times(0)).onNext("helloagain");
    +    }
    +
    +    @SuppressWarnings("unchecked")
    +    /* mock calls don't do generics */
    +    @Test
    +    public void testAggregatorEarlyCompletion() {
    +        FuncN combineLatestFunction = getConcatCombineLatestFunction();
    +        /* create the aggregator which will execute the combineLatest function when all Observables provide values */
    +        Aggregator a = new Aggregator(combineLatestFunction);
    +
    +        /* define a Observer to receive aggregated events */
    +        Observer aObserver = mock(Observer.class);
    +        Observable.create(a).subscribe(aObserver);
    +
    +        /* mock the Observable Observers that are 'pushing' data for us */
    +        CombineObserver r1 = mock(CombineObserver.class);
    +        CombineObserver r2 = mock(CombineObserver.class);
    +
    +        /* pretend we're starting up */
    +        a.addObserver(r1);
    +        a.addObserver(r2);
    +
    +        /* simulate the Observables pushing data into the aggregator */
    +        a.next(r1, "one");
    +        a.next(r1, "two");
    +        a.complete(r1);
    +        a.next(r2, "A");
    +
    +        InOrder inOrder = inOrder(aObserver);
    +
    +        inOrder.verify(aObserver, never()).onError(any(Throwable.class));
    +        inOrder.verify(aObserver, never()).onCompleted();
    +        inOrder.verify(aObserver, times(1)).onNext("twoA");
    +
    +        a.complete(r2);
    +
    +        inOrder.verify(aObserver, never()).onError(any(Throwable.class));
    +        inOrder.verify(aObserver, times(1)).onCompleted();
    +        // we shouldn't get this since completed is called before any other onNext calls could trigger this
    +        inOrder.verify(aObserver, never()).onNext(anyString());
    +    }
    +
    +    @SuppressWarnings("unchecked")
    +    /* mock calls don't do generics */
    +    @Test
    +    public void testCombineLatest2Types() {
    +        Func2 combineLatestFunction = getConcatStringIntegerCombineLatestFunction();
    +
    +        /* define a Observer to receive aggregated events */
    +        Observer aObserver = mock(Observer.class);
    +
    +        Observable w = Observable.create(combineLatest(Observable.from("one", "two"), Observable.from(2, 3, 4), combineLatestFunction));
    +        w.subscribe(aObserver);
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +        verify(aObserver, times(1)).onNext("two2");
    +        verify(aObserver, times(1)).onNext("two3");
    +        verify(aObserver, times(1)).onNext("two4");
    +    }
    +
    +    @SuppressWarnings("unchecked")
    +    /* mock calls don't do generics */
    +    @Test
    +    public void testCombineLatest3TypesA() {
    +        Func3 combineLatestFunction = getConcatStringIntegerIntArrayCombineLatestFunction();
    +
    +        /* define a Observer to receive aggregated events */
    +        Observer aObserver = mock(Observer.class);
    +
    +        Observable w = Observable.create(combineLatest(Observable.from("one", "two"), Observable.from(2), Observable.from(new int[] { 4, 5, 6 }), combineLatestFunction));
    +        w.subscribe(aObserver);
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +        verify(aObserver, times(1)).onNext("two2[4, 5, 6]");
    +    }
    +
    +    @SuppressWarnings("unchecked")
    +    /* mock calls don't do generics */
    +    @Test
    +    public void testCombineLatest3TypesB() {
    +        Func3 combineLatestFunction = getConcatStringIntegerIntArrayCombineLatestFunction();
    +
    +        /* define a Observer to receive aggregated events */
    +        Observer aObserver = mock(Observer.class);
    +
    +        Observable w = Observable.create(combineLatest(Observable.from("one"), Observable.from(2), Observable.from(new int[] { 4, 5, 6 }, new int[] { 7, 8 }), combineLatestFunction));
    +        w.subscribe(aObserver);
    +
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +        verify(aObserver, times(1)).onNext("one2[4, 5, 6]");
    +        verify(aObserver, times(1)).onNext("one2[7, 8]");
         }
    -  }
     
    -  private static class TestObservable implements Observable.OnSubscribeFunc {
    +    private Func3 getConcat3StringsCombineLatestFunction() {
    +        Func3 combineLatestFunction = new Func3() {
    +
    +            @Override
    +            public String call(String a1, String a2, String a3) {
    +                if (a1 == null) {
    +                    a1 = "";
    +                }
    +                if (a2 == null) {
    +                    a2 = "";
    +                }
    +                if (a3 == null) {
    +                    a3 = "";
    +                }
    +                return a1 + a2 + a3;
    +            }
    +
    +        };
    +        return combineLatestFunction;
    +    }
     
    -    Observer observer;
    +    private FuncN getConcatCombineLatestFunction() {
    +        FuncN combineLatestFunction = new FuncN() {
    +
    +            @Override
    +            public String call(Object... args) {
    +                String returnValue = "";
    +                for (Object o : args) {
    +                    if (o != null) {
    +                        returnValue += getStringValue(o);
    +                    }
    +                }
    +                System.out.println("returning: " + returnValue);
    +                return returnValue;
    +            }
    +
    +        };
    +        return combineLatestFunction;
    +    }
    +
    +    private Func2 getConcatStringIntegerCombineLatestFunction() {
    +        Func2 combineLatestFunction = new Func2() {
    +
    +            @Override
    +            public String call(String s, Integer i) {
    +                return getStringValue(s) + getStringValue(i);
    +            }
     
    -    @Override
    -    public Subscription onSubscribe(Observer observer) {
    -      // just store the variable where it can be accessed so we can manually trigger it
    -      this.observer = observer;
    -      return Subscriptions.empty();
    +        };
    +        return combineLatestFunction;
         }
     
    -  }
    +    private Func3 getConcatStringIntegerIntArrayCombineLatestFunction() {
    +        Func3 combineLatestFunction = new Func3() {
    +
    +            @Override
    +            public String call(String s, Integer i, int[] iArray) {
    +                return getStringValue(s) + getStringValue(i) + getStringValue(iArray);
    +            }
    +
    +        };
    +        return combineLatestFunction;
    +    }
    +
    +    private static String getStringValue(Object o) {
    +        if (o == null) {
    +            return "";
    +        } else {
    +            if (o instanceof int[]) {
    +                return Arrays.toString((int[]) o);
    +            } else {
    +                return String.valueOf(o);
    +            }
    +        }
    +    }
    +
    +    private static class TestObservable implements Observable.OnSubscribeFunc {
    +
    +        Observer observer;
    +
    +        @Override
    +        public Subscription onSubscribe(Observer observer) {
    +            // just store the variable where it can be accessed so we can manually trigger it
    +            this.observer = observer;
    +            return Subscriptions.empty();
    +        }
    +
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java b/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java
    index 35fc22bcc2..d6fc1e2d56 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java
    @@ -1,11 +1,9 @@
     package rx.operators;
     
    -import org.junit.Test;
    -import org.mockito.InOrder;
    -import rx.Observable;
    -import rx.Observer;
    -import rx.Subscription;
    -import rx.subscriptions.BooleanSubscription;
    +import static org.junit.Assert.*;
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationConcat.*;
     
     import java.util.ArrayList;
     import java.util.Arrays;
    @@ -14,531 +12,533 @@
     import java.util.concurrent.TimeUnit;
     import java.util.concurrent.atomic.AtomicReference;
     
    -import static org.junit.Assert.fail;
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Matchers.anyString;
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationConcat.concat;
    +import org.junit.Test;
    +import org.mockito.InOrder;
    +
    +import rx.Observable;
    +import rx.Observer;
    +import rx.Subscription;
    +import rx.subscriptions.BooleanSubscription;
     
     public class OperationConcatTest {
     
    -  @Test
    -  public void testConcat() {
    -    @SuppressWarnings("unchecked")
    -    Observer observer = mock(Observer.class);
    +    @Test
    +    public void testConcat() {
    +        @SuppressWarnings("unchecked")
    +        Observer observer = mock(Observer.class);
     
    -    final String[] o = {"1", "3", "5", "7"};
    -    final String[] e = {"2", "4", "6"};
    +        final String[] o = { "1", "3", "5", "7" };
    +        final String[] e = { "2", "4", "6" };
     
    -    final Observable odds = Observable.from(o);
    -    final Observable even = Observable.from(e);
    +        final Observable odds = Observable.from(o);
    +        final Observable even = Observable.from(e);
     
    -    @SuppressWarnings("unchecked")
    -    Observable concat = Observable.create(concat(odds, even));
    -    concat.subscribe(observer);
    +        @SuppressWarnings("unchecked")
    +        Observable concat = Observable.create(concat(odds, even));
    +        concat.subscribe(observer);
     
    -    verify(observer, times(7)).onNext(anyString());
    -  }
    +        verify(observer, times(7)).onNext(anyString());
    +    }
     
    -  @Test
    -  public void testConcatWithList() {
    -    @SuppressWarnings("unchecked")
    -    Observer observer = mock(Observer.class);
    +    @Test
    +    public void testConcatWithList() {
    +        @SuppressWarnings("unchecked")
    +        Observer observer = mock(Observer.class);
     
    -    final String[] o = {"1", "3", "5", "7"};
    -    final String[] e = {"2", "4", "6"};
    +        final String[] o = { "1", "3", "5", "7" };
    +        final String[] e = { "2", "4", "6" };
     
    -    final Observable odds = Observable.from(o);
    -    final Observable even = Observable.from(e);
    -    final List> list = new ArrayList>();
    -    list.add(odds);
    -    list.add(even);
    -    Observable concat = Observable.create(concat(list));
    -    concat.subscribe(observer);
    +        final Observable odds = Observable.from(o);
    +        final Observable even = Observable.from(e);
    +        final List> list = new ArrayList>();
    +        list.add(odds);
    +        list.add(even);
    +        Observable concat = Observable.create(concat(list));
    +        concat.subscribe(observer);
     
    -    verify(observer, times(7)).onNext(anyString());
    -  }
    +        verify(observer, times(7)).onNext(anyString());
    +    }
     
    -  @Test
    -  public void testConcatObservableOfObservables() {
    -    @SuppressWarnings("unchecked")
    -    Observer observer = mock(Observer.class);
    +    @Test
    +    public void testConcatObservableOfObservables() {
    +        @SuppressWarnings("unchecked")
    +        Observer observer = mock(Observer.class);
     
    -    final String[] o = {"1", "3", "5", "7"};
    -    final String[] e = {"2", "4", "6"};
    +        final String[] o = { "1", "3", "5", "7" };
    +        final String[] e = { "2", "4", "6" };
     
    -    final Observable odds = Observable.from(o);
    -    final Observable even = Observable.from(e);
    +        final Observable odds = Observable.from(o);
    +        final Observable even = Observable.from(e);
     
    -    Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() {
    +        Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() {
     
    -      @Override
    -      public Subscription onSubscribe(Observer> observer) {
    -        // simulate what would happen in an observable
    -        observer.onNext(odds);
    -        observer.onNext(even);
    -        observer.onCompleted();
    +            @Override
    +            public Subscription onSubscribe(Observer> observer) {
    +                // simulate what would happen in an observable
    +                observer.onNext(odds);
    +                observer.onNext(even);
    +                observer.onCompleted();
     
    -        return new Subscription() {
    +                return new Subscription() {
     
    -          @Override
    -          public void unsubscribe() {
    -            // unregister ... will never be called here since we are executing synchronously
    -          }
    +                    @Override
    +                    public void unsubscribe() {
    +                        // unregister ... will never be called here since we are executing synchronously
    +                    }
     
    -        };
    -      }
    +                };
    +            }
     
    -    });
    -    Observable concat = Observable.create(concat(observableOfObservables));
    +        });
    +        Observable concat = Observable.create(concat(observableOfObservables));
     
    -    concat.subscribe(observer);
    +        concat.subscribe(observer);
    +
    +        verify(observer, times(7)).onNext(anyString());
    +    }
     
    -    verify(observer, times(7)).onNext(anyString());
    -  }
    +    /**
    +     * Simple concat of 2 asynchronous observables ensuring it emits in correct order.
    +     */
    +    @SuppressWarnings("unchecked")
    +    @Test
    +    public void testSimpleAsyncConcat() {
    +        Observer observer = mock(Observer.class);
     
    -  /**
    -   * Simple concat of 2 asynchronous observables ensuring it emits in correct order.
    -   */
    -  @SuppressWarnings("unchecked")
    -  @Test
    -  public void testSimpleAsyncConcat() {
    -    Observer observer = mock(Observer.class);
    +        TestObservable o1 = new TestObservable("one", "two", "three");
    +        TestObservable o2 = new TestObservable("four", "five", "six");
     
    -    TestObservable o1 = new TestObservable("one", "two", "three");
    -    TestObservable o2 = new TestObservable("four", "five", "six");
    +        Observable.concat(Observable.create(o1), Observable.create(o2)).subscribe(observer);
     
    -    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");
    +        }
     
    -    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");
         }
     
    -    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 an async Observable that emits more async Observables
    -   */
    -  @SuppressWarnings("unchecked")
    -  @Test
    -  public void testNestedAsyncConcat() throws Throwable {
    -    Observer observer = mock(Observer.class);
    -
    -    final TestObservable o1 = new TestObservable("one", "two", "three");
    -    final TestObservable o2 = new TestObservable("four", "five", "six");
    -    final TestObservable o3 = new TestObservable("seven", "eight", "nine");
    -    final CountDownLatch allowThird = new CountDownLatch(1);
    -
    -    final AtomicReference parent = new AtomicReference();
    -    Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() {
    -
    -      @Override
    -      public Subscription onSubscribe(final Observer> 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();
    +    /**
    +     * Test an async Observable that emits more async Observables
    +     */
    +    @SuppressWarnings("unchecked")
    +    @Test
    +    public void testNestedAsyncConcat() throws Throwable {
    +        Observer observer = mock(Observer.class);
    +
    +        final TestObservable o1 = new TestObservable("one", "two", "three");
    +        final TestObservable o2 = new TestObservable("four", "five", "six");
    +        final TestObservable o3 = new TestObservable("seven", "eight", "nine");
    +        final CountDownLatch allowThird = new CountDownLatch(1);
    +
    +        final AtomicReference parent = new AtomicReference();
    +        Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() {
    +
    +            @Override
    +            public Subscription onSubscribe(final Observer> 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();
    +                return s;
                 }
    -          }
    -        }));
    -        parent.get().start();
    -        return s;
    -      }
    -    });
    -
    -    Observable.create(concat(observableOfObservables)).subscribe(observer);
    -
    -    // wait for parent to start
    -    while (parent.get() == null) {
    -      Thread.sleep(1);
    -    }
    +        });
     
    -    try {
    -      // wait for first 2 async observables to complete
    -      while (o1.t == null) {
    -        Thread.sleep(1);
    -      }
    -      System.out.println("Thread1 started ... waiting for it to complete ...");
    -      o1.t.join();
    -      while (o2.t == null) {
    -        Thread.sleep(1);
    -      }
    -      System.out.println("Thread2 started ... waiting for it to complete ...");
    -      o2.t.join();
    -    } catch (Throwable e) {
    -      throw new RuntimeException("failed waiting on threads", e);
    -    }
    +        Observable.create(concat(observableOfObservables)).subscribe(observer);
     
    -    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 {
    -      while (o3.t == null) {
    -        Thread.sleep(1);
    -      }
    -      // wait for 3rd to complete
    -      o3.t.join();
    -    } catch (Throwable e) {
    -      throw new RuntimeException("failed waiting on threads", e);
    -    }
    +        // wait for parent to start
    +        while (parent.get() == null) {
    +            Thread.sleep(1);
    +        }
     
    -    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));
    -  }
    -
    -  @SuppressWarnings("unchecked")
    -  @Test
    -  public void testBlockedObservableOfObservables() {
    -    Observer observer = mock(Observer.class);
    -
    -    final String[] o = {"1", "3", "5", "7"};
    -    final String[] e = {"2", "4", "6"};
    -    final Observable odds = Observable.from(o);
    -    final Observable even = Observable.from(e);
    -    final CountDownLatch callOnce = new CountDownLatch(1);
    -    final CountDownLatch okToContinue = new CountDownLatch(1);
    -    TestObservable> observableOfObservables = new TestObservable>(callOnce, okToContinue, odds, even);
    -    Observable.OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables));
    -    Observable concat = Observable.create(concatF);
    -    concat.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());
    +        try {
    +            // wait for first 2 async observables to complete
    +            while (o1.t == null) {
    +                Thread.sleep(1);
    +            }
    +            System.out.println("Thread1 started ... waiting for it to complete ...");
    +            o1.t.join();
    +            while (o2.t == null) {
    +                Thread.sleep(1);
    +            }
    +            System.out.println("Thread2 started ... waiting for it to complete ...");
    +            o2.t.join();
    +        } 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 {
    +            while (o3.t == null) {
    +                Thread.sleep(1);
    +            }
    +            // wait for 3rd to complete
    +            o3.t.join();
    +        } 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));
         }
    -    // 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 testConcatConcurrentWithInfinity() {
    -    final TestObservable w1 = new TestObservable("one", "two", "three");
    -    //This observable will send "hello" MAX_VALUE time.
    -    final TestObservable w2 = new TestObservable("hello", Integer.MAX_VALUE);
     
         @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    @SuppressWarnings("unchecked")
    -    TestObservable> observableOfObservables = new TestObservable>(Observable.create(w1), Observable.create(w2));
    -    Observable.OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables));
    -
    -    Observable concat = Observable.create(concatF);
    +    @Test
    +    public void testBlockedObservableOfObservables() {
    +        Observer observer = mock(Observer.class);
    +
    +        final String[] o = { "1", "3", "5", "7" };
    +        final String[] e = { "2", "4", "6" };
    +        final Observable odds = Observable.from(o);
    +        final Observable even = Observable.from(e);
    +        final CountDownLatch callOnce = new CountDownLatch(1);
    +        final CountDownLatch okToContinue = new CountDownLatch(1);
    +        TestObservable> observableOfObservables = new TestObservable>(callOnce, okToContinue, odds, even);
    +        Observable.OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables));
    +        Observable concat = Observable.create(concatF);
    +        concat.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");
    +    }
     
    -    concat.take(50).subscribe(aObserver);
    +    @Test
    +    public void testConcatConcurrentWithInfinity() {
    +        final TestObservable w1 = new TestObservable("one", "two", "three");
    +        //This observable will send "hello" MAX_VALUE time.
    +        final TestObservable w2 = new TestObservable("hello", Integer.MAX_VALUE);
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        @SuppressWarnings("unchecked")
    +        TestObservable> observableOfObservables = new TestObservable>(Observable.create(w1), Observable.create(w2));
    +        Observable.OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables));
    +
    +        Observable concat = Observable.create(concatF);
    +
    +        concat.take(50).subscribe(aObserver);
    +
    +        //Wait for the thread to start up.
    +        try {
    +            Thread.sleep(25);
    +            w1.t.join();
    +            w2.t.join();
    +        } catch (InterruptedException e) {
    +            // TODO Auto-generated catch block
    +            e.printStackTrace();
    +        }
     
    -    //Wait for the thread to start up.
    -    try {
    -      Thread.sleep(25);
    -      w1.t.join();
    -      w2.t.join();
    -    } catch (InterruptedException e) {
    -      // TODO Auto-generated catch block
    -      e.printStackTrace();
    +        InOrder inOrder = inOrder(aObserver);
    +        inOrder.verify(aObserver, times(1)).onNext("one");
    +        inOrder.verify(aObserver, times(1)).onNext("two");
    +        inOrder.verify(aObserver, times(1)).onNext("three");
    +        inOrder.verify(aObserver, times(47)).onNext("hello");
    +        verify(aObserver, times(1)).onCompleted();
    +        verify(aObserver, never()).onError(any(Throwable.class));
         }
     
    -    InOrder inOrder = inOrder(aObserver);
    -    inOrder.verify(aObserver, times(1)).onNext("one");
    -    inOrder.verify(aObserver, times(1)).onNext("two");
    -    inOrder.verify(aObserver, times(1)).onNext("three");
    -    inOrder.verify(aObserver, times(47)).onNext("hello");
    -    verify(aObserver, times(1)).onCompleted();
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -  }
    +    @Test
    +    public void testConcatNonBlockingObservables() {
     
    -  @Test
    -  public void testConcatNonBlockingObservables() {
    +        final CountDownLatch okToContinueW1 = new CountDownLatch(1);
    +        final CountDownLatch okToContinueW2 = new CountDownLatch(1);
     
    -    final CountDownLatch okToContinueW1 = new CountDownLatch(1);
    -    final CountDownLatch okToContinueW2 = new CountDownLatch(1);
    +        final TestObservable w1 = new TestObservable(null, okToContinueW1, "one", "two", "three");
    +        final TestObservable w2 = new TestObservable(null, okToContinueW2, "four", "five", "six");
     
    -    final TestObservable w1 = new TestObservable(null, okToContinueW1, "one", "two", "three");
    -    final TestObservable w2 = new TestObservable(null, okToContinueW2, "four", "five", "six");
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() {
     
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() {
    +            @Override
    +            public Subscription onSubscribe(Observer> observer) {
    +                // simulate what would happen in an observable
    +                observer.onNext(Observable.create(w1));
    +                observer.onNext(Observable.create(w2));
    +                observer.onCompleted();
     
    -      @Override
    -      public Subscription onSubscribe(Observer> observer) {
    -        // simulate what would happen in an observable
    -        observer.onNext(Observable.create(w1));
    -        observer.onNext(Observable.create(w2));
    -        observer.onCompleted();
    +                return new Subscription() {
     
    -        return new Subscription() {
    +                    @Override
    +                    public void unsubscribe() {
    +                    }
     
    -          @Override
    -          public void unsubscribe() {
    -          }
    +                };
    +            }
     
    -        };
    -      }
    -
    -    });
    -    Observable concat = Observable.create(concat(observableOfObservables));
    -    concat.subscribe(aObserver);
    -
    -    verify(aObserver, 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) {
    -      // TODO Auto-generated catch block
    -      e.printStackTrace();
    -    }
    +        });
    +        Observable concat = Observable.create(concat(observableOfObservables));
    +        concat.subscribe(aObserver);
    +
    +        verify(aObserver, 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) {
    +            // TODO Auto-generated catch block
    +            e.printStackTrace();
    +        }
     
    -    InOrder inOrder = inOrder(aObserver);
    -    inOrder.verify(aObserver, times(1)).onNext("one");
    -    inOrder.verify(aObserver, times(1)).onNext("two");
    -    inOrder.verify(aObserver, times(1)).onNext("three");
    -    inOrder.verify(aObserver, times(1)).onNext("four");
    -    inOrder.verify(aObserver, times(1)).onNext("five");
    -    inOrder.verify(aObserver, times(1)).onNext("six");
    -    verify(aObserver, times(1)).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 w1 = new TestObservable("one", "two", "three");
    -    final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six");
    +        InOrder inOrder = inOrder(aObserver);
    +        inOrder.verify(aObserver, times(1)).onNext("one");
    +        inOrder.verify(aObserver, times(1)).onNext("two");
    +        inOrder.verify(aObserver, times(1)).onNext("three");
    +        inOrder.verify(aObserver, times(1)).onNext("four");
    +        inOrder.verify(aObserver, times(1)).onNext("five");
    +        inOrder.verify(aObserver, times(1)).onNext("six");
    +        verify(aObserver, times(1)).onCompleted();
     
    -    @SuppressWarnings("unchecked")
    -    final Observer aObserver = mock(Observer.class);
    -    @SuppressWarnings("unchecked")
    -    final Observable concat = Observable.create(concat(Observable.create(w1), Observable.create(w2)));
    -    final SafeObservableSubscription s1 = new SafeObservableSubscription();
    -
    -    try {
    -      // Subscribe
    -      s1.wrap(concat.subscribe(aObserver));
    -      //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(aObserver);
    -    inOrder.verify(aObserver, times(1)).onNext("one");
    -    inOrder.verify(aObserver, times(1)).onNext("two");
    -    inOrder.verify(aObserver, times(1)).onNext("three");
    -    inOrder.verify(aObserver, times(1)).onNext("four");
    -    inOrder.verify(aObserver, never()).onNext("five");
    -    inOrder.verify(aObserver, never()).onNext("six");
    -    inOrder.verify(aObserver, never()).onCompleted();
    -
    -  }
    -
    -  /**
    -   * 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 w1 = new TestObservable("one", "two", "three");
    -    final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six");
    +    /**
    +     * 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 w1 = new TestObservable("one", "two", "three");
    +        final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six");
    +
    +        @SuppressWarnings("unchecked")
    +        final Observer aObserver = mock(Observer.class);
    +        @SuppressWarnings("unchecked")
    +        final Observable concat = Observable.create(concat(Observable.create(w1), Observable.create(w2)));
    +        final SafeObservableSubscription s1 = new SafeObservableSubscription();
    +
    +        try {
    +            // Subscribe
    +            s1.wrap(concat.subscribe(aObserver));
    +            //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());
    +        }
     
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    @SuppressWarnings("unchecked")
    -    TestObservable> observableOfObservables = new TestObservable>(Observable.create(w1), Observable.create(w2));
    -    Observable.OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables));
    -
    -    Observable concat = Observable.create(concatF);
    -
    -    Subscription s1 = concat.subscribe(aObserver);
    -
    -    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(aObserver);
    +        inOrder.verify(aObserver, times(1)).onNext("one");
    +        inOrder.verify(aObserver, times(1)).onNext("two");
    +        inOrder.verify(aObserver, times(1)).onNext("three");
    +        inOrder.verify(aObserver, times(1)).onNext("four");
    +        inOrder.verify(aObserver, never()).onNext("five");
    +        inOrder.verify(aObserver, never()).onNext("six");
    +        inOrder.verify(aObserver, never()).onCompleted();
     
    -    InOrder inOrder = inOrder(aObserver);
    -    inOrder.verify(aObserver, times(1)).onNext("one");
    -    inOrder.verify(aObserver, times(1)).onNext("two");
    -    inOrder.verify(aObserver, times(1)).onNext("three");
    -    inOrder.verify(aObserver, times(1)).onNext("four");
    -    inOrder.verify(aObserver, never()).onNext("five");
    -    inOrder.verify(aObserver, never()).onNext("six");
    -    verify(aObserver, never()).onCompleted();
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -  }
    -
    -  private static class TestObservable implements Observable.OnSubscribeFunc {
    -
    -    private final Subscription s = new Subscription() {
    -
    -      @Override
    -      public void unsubscribe() {
    -        subscribed = false;
    -      }
    -
    -    };
    -    private final List values;
    -    private Thread t = null;
    -    private int count = 0;
    -    private boolean subscribed = true;
    -    private final CountDownLatch once;
    -    private final CountDownLatch okToContinue;
    -    private final T seed;
    -    private final int size;
    -
    -    public TestObservable(T... values) {
    -      this(null, null, values);
         }
     
    -    public TestObservable(CountDownLatch once, CountDownLatch okToContinue, T... values) {
    -      this.values = Arrays.asList(values);
    -      this.size = this.values.size();
    -      this.once = once;
    -      this.okToContinue = okToContinue;
    -      this.seed = null;
    -    }
    +    /**
    +     * 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 w1 = new TestObservable("one", "two", "three");
    +        final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six");
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        @SuppressWarnings("unchecked")
    +        TestObservable> observableOfObservables = new TestObservable>(Observable.create(w1), Observable.create(w2));
    +        Observable.OnSubscribeFunc concatF = concat(Observable.create(observableOfObservables));
    +
    +        Observable concat = Observable.create(concatF);
    +
    +        Subscription s1 = concat.subscribe(aObserver);
    +
    +        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());
    +        }
     
    -    public TestObservable(T seed, int size) {
    -      values = null;
    -      once = null;
    -      okToContinue = null;
    -      this.seed = seed;
    -      this.size = size;
    +        InOrder inOrder = inOrder(aObserver);
    +        inOrder.verify(aObserver, times(1)).onNext("one");
    +        inOrder.verify(aObserver, times(1)).onNext("two");
    +        inOrder.verify(aObserver, times(1)).onNext("three");
    +        inOrder.verify(aObserver, times(1)).onNext("four");
    +        inOrder.verify(aObserver, never()).onNext("five");
    +        inOrder.verify(aObserver, never()).onNext("six");
    +        verify(aObserver, never()).onCompleted();
    +        verify(aObserver, never()).onError(any(Throwable.class));
         }
     
    -    @Override
    -    public Subscription onSubscribe(final Observer observer) {
    -      t = new Thread(new Runnable() {
    +    private static class TestObservable implements Observable.OnSubscribeFunc {
     
    -        @Override
    -        public void run() {
    -          try {
    -            while (count < size && subscribed) {
    -              if (null != values)
    -                observer.onNext(values.get(count));
    -              else
    -                observer.onNext(seed);
    -              count++;
    -              //Unblock the main thread to call unsubscribe.
    -              if (null != once)
    -                once.countDown();
    -              //Block until the main thread has called unsubscribe.
    -              if (null != okToContinue)
    -                okToContinue.await(5, TimeUnit.SECONDS);
    +        private final Subscription s = new Subscription() {
    +
    +            @Override
    +            public void unsubscribe() {
    +                subscribed = false;
                 }
    -            if (subscribed)
    -              observer.onCompleted();
    -          } catch (InterruptedException e) {
    -            e.printStackTrace();
    -            fail(e.getMessage());
    -          }
    +
    +        };
    +        private final List values;
    +        private Thread t = null;
    +        private int count = 0;
    +        private boolean subscribed = true;
    +        private final CountDownLatch once;
    +        private final CountDownLatch okToContinue;
    +        private final T seed;
    +        private final int size;
    +
    +        public TestObservable(T... values) {
    +            this(null, null, values);
    +        }
    +
    +        public TestObservable(CountDownLatch once, CountDownLatch okToContinue, T... values) {
    +            this.values = Arrays.asList(values);
    +            this.size = this.values.size();
    +            this.once = once;
    +            this.okToContinue = okToContinue;
    +            this.seed = null;
             }
     
    -      });
    -      t.start();
    -      return s;
    +        public TestObservable(T seed, int size) {
    +            values = null;
    +            once = null;
    +            okToContinue = null;
    +            this.seed = seed;
    +            this.size = size;
    +        }
    +
    +        @Override
    +        public Subscription onSubscribe(final Observer observer) {
    +            t = new Thread(new Runnable() {
    +
    +                @Override
    +                public void run() {
    +                    try {
    +                        while (count < size && subscribed) {
    +                            if (null != values)
    +                                observer.onNext(values.get(count));
    +                            else
    +                                observer.onNext(seed);
    +                            count++;
    +                            //Unblock the main thread to call unsubscribe.
    +                            if (null != once)
    +                                once.countDown();
    +                            //Block until the main thread has called unsubscribe.
    +                            if (null != okToContinue)
    +                                okToContinue.await(5, TimeUnit.SECONDS);
    +                        }
    +                        if (subscribed)
    +                            observer.onCompleted();
    +                    } catch (InterruptedException e) {
    +                        e.printStackTrace();
    +                        fail(e.getMessage());
    +                    }
    +                }
    +
    +            });
    +            t.start();
    +            return s;
    +        }
         }
    -  }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java b/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java
    index 2e907d8f6b..667c607301 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java
    @@ -1,8 +1,14 @@
     package rx.operators;
     
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +
    +import java.util.concurrent.TimeUnit;
    +
     import org.junit.Before;
     import org.junit.Test;
     import org.mockito.InOrder;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.Subscription;
    @@ -10,137 +16,131 @@
     import rx.subscriptions.Subscriptions;
     import rx.util.functions.Action0;
     
    -import java.util.concurrent.TimeUnit;
    -
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Matchers.anyString;
    -import static org.mockito.Mockito.*;
    -
     public class OperationDebounceTest {
     
    -  private TestScheduler scheduler;
    -  private Observer observer;
    -
    -  @Before
    -  @SuppressWarnings("unchecked")
    -  public void before() {
    -    scheduler = new TestScheduler();
    -    observer = mock(Observer.class);
    -  }
    -
    -  @Test
    -  public void testDebounceWithCompleted() {
    -    Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        publishNext(observer, 100, "one");    // Should be skipped since "two" will arrive before the timeout expires.
    -        publishNext(observer, 400, "two");    // Should be published since "three" will arrive after the timeout expires.
    -        publishNext(observer, 900, "three");   // Should be skipped since onCompleted will arrive before the timeout expires.
    -        publishCompleted(observer, 1000);     // Should be published as soon as the timeout expires.
    -
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Observable sampled = Observable.create(OperationDebounce.debounce(source, 400, TimeUnit.MILLISECONDS, scheduler));
    -    sampled.subscribe(observer);
    -
    -    scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS);
    -    InOrder inOrder = inOrder(observer);
    -    // must go to 800 since it must be 400 after when two is sent, which is at 400
    -    scheduler.advanceTimeTo(800, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer, times(1)).onNext("two");
    -    scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer, times(1)).onCompleted();
    -    inOrder.verifyNoMoreInteractions();
    -  }
    -
    -  @Test
    -  public void testDebounceNeverEmits() {
    -    Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        // all should be skipped since they are happening faster than the 200ms timeout
    -        publishNext(observer, 100, "a");    // Should be skipped
    -        publishNext(observer, 200, "b");    // Should be skipped
    -        publishNext(observer, 300, "c");    // Should be skipped
    -        publishNext(observer, 400, "d");    // Should be skipped
    -        publishNext(observer, 500, "e");    // Should be skipped
    -        publishNext(observer, 600, "f");    // Should be skipped
    -        publishNext(observer, 700, "g");    // Should be skipped
    -        publishNext(observer, 800, "h");    // Should be skipped
    -        publishCompleted(observer, 900);     // Should be published as soon as the timeout expires.
    -
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Observable sampled = Observable.create(OperationDebounce.debounce(source, 200, TimeUnit.MILLISECONDS, scheduler));
    -    sampled.subscribe(observer);
    -
    -    scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS);
    -    InOrder inOrder = inOrder(observer);
    -    inOrder.verify(observer, times(0)).onNext(anyString());
    -    scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer, times(1)).onCompleted();
    -    inOrder.verifyNoMoreInteractions();
    -  }
    -
    -  @Test
    -  public void testDebounceWithError() {
    -    Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    -      @Override
    -      public Subscription onSubscribe(Observer observer) {
    -        Exception error = new TestException();
    -        publishNext(observer, 100, "one");    // Should be published since "two" will arrive after the timeout expires.
    -        publishNext(observer, 600, "two");    // Should be skipped since onError will arrive before the timeout expires.
    -        publishError(observer, 700, error);   // Should be published as soon as the timeout expires.
    -
    -        return Subscriptions.empty();
    -      }
    -    });
    -
    -    Observable sampled = Observable.create(OperationDebounce.debounce(source, 400, TimeUnit.MILLISECONDS, scheduler));
    -    sampled.subscribe(observer);
    -
    -    scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS);
    -    InOrder inOrder = inOrder(observer);
    -    // 100 + 400 means it triggers at 500
    -    scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer).onNext("one");
    -    scheduler.advanceTimeTo(701, TimeUnit.MILLISECONDS);
    -    inOrder.verify(observer).onError(any(TestException.class));
    -    inOrder.verifyNoMoreInteractions();
    -  }
    -
    -  private  void publishCompleted(final Observer observer, long delay) {
    -    scheduler.schedule(new Action0() {
    -      @Override
    -      public void call() {
    -        observer.onCompleted();
    -      }
    -    }, delay, TimeUnit.MILLISECONDS);
    -  }
    -
    -  private  void publishError(final Observer observer, long delay, final Exception error) {
    -    scheduler.schedule(new Action0() {
    -      @Override
    -      public void call() {
    -        observer.onError(error);
    -      }
    -    }, delay, TimeUnit.MILLISECONDS);
    -  }
    -
    -  private  void publishNext(final Observer observer, final long delay, final T value) {
    -    scheduler.schedule(new Action0() {
    -      @Override
    -      public void call() {
    -        observer.onNext(value);
    -      }
    -    }, delay, TimeUnit.MILLISECONDS);
    -  }
    -
    -  @SuppressWarnings("serial")
    -  private class TestException extends Exception {
    -  }
    +    private TestScheduler scheduler;
    +    private Observer observer;
    +
    +    @Before
    +    @SuppressWarnings("unchecked")
    +    public void before() {
    +        scheduler = new TestScheduler();
    +        observer = mock(Observer.class);
    +    }
    +
    +    @Test
    +    public void testDebounceWithCompleted() {
    +        Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                publishNext(observer, 100, "one");    // Should be skipped since "two" will arrive before the timeout expires.
    +                publishNext(observer, 400, "two");    // Should be published since "three" will arrive after the timeout expires.
    +                publishNext(observer, 900, "three");   // Should be skipped since onCompleted will arrive before the timeout expires.
    +                publishCompleted(observer, 1000);     // Should be published as soon as the timeout expires.
    +
    +                return Subscriptions.empty();
    +            }
    +        });
    +
    +        Observable sampled = Observable.create(OperationDebounce.debounce(source, 400, TimeUnit.MILLISECONDS, scheduler));
    +        sampled.subscribe(observer);
    +
    +        scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS);
    +        InOrder inOrder = inOrder(observer);
    +        // must go to 800 since it must be 400 after when two is sent, which is at 400
    +        scheduler.advanceTimeTo(800, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer, times(1)).onNext("two");
    +        scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer, times(1)).onCompleted();
    +        inOrder.verifyNoMoreInteractions();
    +    }
    +
    +    @Test
    +    public void testDebounceNeverEmits() {
    +        Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                // all should be skipped since they are happening faster than the 200ms timeout
    +                publishNext(observer, 100, "a");    // Should be skipped
    +                publishNext(observer, 200, "b");    // Should be skipped
    +                publishNext(observer, 300, "c");    // Should be skipped
    +                publishNext(observer, 400, "d");    // Should be skipped
    +                publishNext(observer, 500, "e");    // Should be skipped
    +                publishNext(observer, 600, "f");    // Should be skipped
    +                publishNext(observer, 700, "g");    // Should be skipped
    +                publishNext(observer, 800, "h");    // Should be skipped
    +                publishCompleted(observer, 900);     // Should be published as soon as the timeout expires.
    +
    +                return Subscriptions.empty();
    +            }
    +        });
    +
    +        Observable sampled = Observable.create(OperationDebounce.debounce(source, 200, TimeUnit.MILLISECONDS, scheduler));
    +        sampled.subscribe(observer);
    +
    +        scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS);
    +        InOrder inOrder = inOrder(observer);
    +        inOrder.verify(observer, times(0)).onNext(anyString());
    +        scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer, times(1)).onCompleted();
    +        inOrder.verifyNoMoreInteractions();
    +    }
    +
    +    @Test
    +    public void testDebounceWithError() {
    +        Observable source = Observable.create(new Observable.OnSubscribeFunc() {
    +            @Override
    +            public Subscription onSubscribe(Observer observer) {
    +                Exception error = new TestException();
    +                publishNext(observer, 100, "one");    // Should be published since "two" will arrive after the timeout expires.
    +                publishNext(observer, 600, "two");    // Should be skipped since onError will arrive before the timeout expires.
    +                publishError(observer, 700, error);   // Should be published as soon as the timeout expires.
    +
    +                return Subscriptions.empty();
    +            }
    +        });
    +
    +        Observable sampled = Observable.create(OperationDebounce.debounce(source, 400, TimeUnit.MILLISECONDS, scheduler));
    +        sampled.subscribe(observer);
    +
    +        scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS);
    +        InOrder inOrder = inOrder(observer);
    +        // 100 + 400 means it triggers at 500
    +        scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer).onNext("one");
    +        scheduler.advanceTimeTo(701, TimeUnit.MILLISECONDS);
    +        inOrder.verify(observer).onError(any(TestException.class));
    +        inOrder.verifyNoMoreInteractions();
    +    }
    +
    +    private  void publishCompleted(final Observer observer, long delay) {
    +        scheduler.schedule(new Action0() {
    +            @Override
    +            public void call() {
    +                observer.onCompleted();
    +            }
    +        }, delay, TimeUnit.MILLISECONDS);
    +    }
    +
    +    private  void publishError(final Observer observer, long delay, final Exception error) {
    +        scheduler.schedule(new Action0() {
    +            @Override
    +            public void call() {
    +                observer.onError(error);
    +            }
    +        }, delay, TimeUnit.MILLISECONDS);
    +    }
    +
    +    private  void publishNext(final Observer observer, final long delay, final T value) {
    +        scheduler.schedule(new Action0() {
    +            @Override
    +            public void call() {
    +                observer.onNext(value);
    +            }
    +        }, delay, TimeUnit.MILLISECONDS);
    +    }
    +
    +    @SuppressWarnings("serial")
    +    private class TestException extends Exception {
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java b/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java
    index fb2166dfc1..fa4910707c 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java
    @@ -1,44 +1,45 @@
     package rx.operators;
     
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationDefaultIfEmpty.*;
    +
     import org.junit.Test;
    +
     import rx.Observable;
     import rx.Observer;
     
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationDefaultIfEmpty.defaultIfEmpty;
    -
     public class OperationDefaultIfEmptyTest {
     
    -  @Test
    -  public void testDefaultIfEmpty() {
    -    Observable source = Observable.from(1, 2, 3);
    -    Observable observable = Observable.create(defaultIfEmpty(
    -        source, 10));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, never()).onNext(10);
    -    verify(aObserver, times(1)).onNext(1);
    -    verify(aObserver, times(1)).onNext(2);
    -    verify(aObserver, times(1)).onNext(3);
    -    verify(aObserver, never()).onError(
    -        org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testDefaultIfEmptyWithEmpty() {
    -    Observable source = Observable.empty();
    -    Observable observable = Observable.create(defaultIfEmpty(
    -        source, 10));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, times(1)).onNext(10);
    -    verify(aObserver, never()).onError(
    -        org.mockito.Matchers.any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    +    @Test
    +    public void testDefaultIfEmpty() {
    +        Observable source = Observable.from(1, 2, 3);
    +        Observable observable = Observable.create(defaultIfEmpty(
    +                source, 10));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, never()).onNext(10);
    +        verify(aObserver, times(1)).onNext(1);
    +        verify(aObserver, times(1)).onNext(2);
    +        verify(aObserver, times(1)).onNext(3);
    +        verify(aObserver, never()).onError(
    +                org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testDefaultIfEmptyWithEmpty() {
    +        Observable source = Observable.empty();
    +        Observable observable = Observable.create(defaultIfEmpty(
    +                source, 10));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, times(1)).onNext(10);
    +        verify(aObserver, never()).onError(
    +                org.mockito.Matchers.any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java b/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java
    index 8e02b17647..6ad98c9a8d 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java
    @@ -1,47 +1,48 @@
     package rx.operators;
     
    +import static org.mockito.Mockito.*;
    +
     import org.junit.Test;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.util.functions.Func0;
     
    -import static org.mockito.Mockito.*;
    -
     public class OperationDeferTest {
     
    -  @Test
    -  @SuppressWarnings("unchecked")
    -  public void testDefer() throws Throwable {
    +    @Test
    +    @SuppressWarnings("unchecked")
    +    public void testDefer() throws Throwable {
     
    -    Func0> factory = mock(Func0.class);
    +        Func0> factory = mock(Func0.class);
     
    -    Observable firstObservable = Observable.from("one", "two");
    -    Observable secondObservable = Observable.from("three", "four");
    -    when(factory.call()).thenReturn(firstObservable, secondObservable);
    +        Observable firstObservable = Observable.from("one", "two");
    +        Observable secondObservable = Observable.from("three", "four");
    +        when(factory.call()).thenReturn(firstObservable, secondObservable);
     
    -    Observable deferred = Observable.defer(factory);
    +        Observable deferred = Observable.defer(factory);
     
    -    verifyZeroInteractions(factory);
    +        verifyZeroInteractions(factory);
     
    -    Observer firstObserver = mock(Observer.class);
    -    deferred.subscribe(firstObserver);
    +        Observer firstObserver = mock(Observer.class);
    +        deferred.subscribe(firstObserver);
     
    -    verify(factory, times(1)).call();
    -    verify(firstObserver, times(1)).onNext("one");
    -    verify(firstObserver, times(1)).onNext("two");
    -    verify(firstObserver, times(0)).onNext("three");
    -    verify(firstObserver, times(0)).onNext("four");
    -    verify(firstObserver, times(1)).onCompleted();
    +        verify(factory, times(1)).call();
    +        verify(firstObserver, times(1)).onNext("one");
    +        verify(firstObserver, times(1)).onNext("two");
    +        verify(firstObserver, times(0)).onNext("three");
    +        verify(firstObserver, times(0)).onNext("four");
    +        verify(firstObserver, times(1)).onCompleted();
     
    -    Observer secondObserver = mock(Observer.class);
    -    deferred.subscribe(secondObserver);
    +        Observer secondObserver = mock(Observer.class);
    +        deferred.subscribe(secondObserver);
     
    -    verify(factory, times(2)).call();
    -    verify(secondObserver, times(0)).onNext("one");
    -    verify(secondObserver, times(0)).onNext("two");
    -    verify(secondObserver, times(1)).onNext("three");
    -    verify(secondObserver, times(1)).onNext("four");
    -    verify(secondObserver, times(1)).onCompleted();
    +        verify(factory, times(2)).call();
    +        verify(secondObserver, times(0)).onNext("one");
    +        verify(secondObserver, times(0)).onNext("two");
    +        verify(secondObserver, times(1)).onNext("three");
    +        verify(secondObserver, times(1)).onNext("four");
    +        verify(secondObserver, times(1)).onCompleted();
     
    -  }
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java b/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java
    index fcc1898d4a..3ca31f160c 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java
    @@ -1,58 +1,59 @@
     package rx.operators;
     
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationDematerialize.*;
    +
     import org.junit.Test;
    +
     import rx.Notification;
     import rx.Observable;
     import rx.Observer;
     
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationDematerialize.dematerialize;
    -
     public class OperationDematerializeTest {
     
    -  @Test
    -  @SuppressWarnings("unchecked")
    -  public void testDematerialize1() {
    -    Observable> notifications = Observable.from(1, 2).materialize();
    -    Observable dematerialize = notifications.dematerialize();
    -
    -    Observer aObserver = mock(Observer.class);
    -    dematerialize.subscribe(aObserver);
    -
    -    verify(aObserver, times(1)).onNext(1);
    -    verify(aObserver, times(1)).onNext(2);
    -    verify(aObserver, times(1)).onCompleted();
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  @SuppressWarnings("unchecked")
    -  public void testDematerialize2() {
    -    Throwable exception = new Throwable("test");
    -    Observable observable = Observable.error(exception);
    -    Observable dematerialize = Observable.create(dematerialize(observable.materialize()));
    -
    -    Observer aObserver = mock(Observer.class);
    -    dematerialize.subscribe(aObserver);
    -
    -    verify(aObserver, times(1)).onError(exception);
    -    verify(aObserver, times(0)).onCompleted();
    -    verify(aObserver, times(0)).onNext(any(Integer.class));
    -  }
    -
    -  @Test
    -  @SuppressWarnings("unchecked")
    -  public void testDematerialize3() {
    -    Exception exception = new Exception("test");
    -    Observable observable = Observable.error(exception);
    -    Observable dematerialize = Observable.create(dematerialize(observable.materialize()));
    -
    -    Observer aObserver = mock(Observer.class);
    -    dematerialize.subscribe(aObserver);
    -
    -    verify(aObserver, times(1)).onError(exception);
    -    verify(aObserver, times(0)).onCompleted();
    -    verify(aObserver, times(0)).onNext(any(Integer.class));
    -  }
    +    @Test
    +    @SuppressWarnings("unchecked")
    +    public void testDematerialize1() {
    +        Observable> notifications = Observable.from(1, 2).materialize();
    +        Observable dematerialize = notifications.dematerialize();
    +
    +        Observer aObserver = mock(Observer.class);
    +        dematerialize.subscribe(aObserver);
    +
    +        verify(aObserver, times(1)).onNext(1);
    +        verify(aObserver, times(1)).onNext(2);
    +        verify(aObserver, times(1)).onCompleted();
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    @SuppressWarnings("unchecked")
    +    public void testDematerialize2() {
    +        Throwable exception = new Throwable("test");
    +        Observable observable = Observable.error(exception);
    +        Observable dematerialize = Observable.create(dematerialize(observable.materialize()));
    +
    +        Observer aObserver = mock(Observer.class);
    +        dematerialize.subscribe(aObserver);
    +
    +        verify(aObserver, times(1)).onError(exception);
    +        verify(aObserver, times(0)).onCompleted();
    +        verify(aObserver, times(0)).onNext(any(Integer.class));
    +    }
    +
    +    @Test
    +    @SuppressWarnings("unchecked")
    +    public void testDematerialize3() {
    +        Exception exception = new Exception("test");
    +        Observable observable = Observable.error(exception);
    +        Observable dematerialize = Observable.create(dematerialize(observable.materialize()));
    +
    +        Observer aObserver = mock(Observer.class);
    +        dematerialize.subscribe(aObserver);
    +
    +        verify(aObserver, times(1)).onError(exception);
    +        verify(aObserver, times(0)).onCompleted();
    +        verify(aObserver, times(0)).onNext(any(Integer.class));
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java b/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java
    index f1c91c0fed..9d0e1569b5 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java
    @@ -18,163 +18,163 @@
     
     public class OperationDistinctTest {
     
    -  @Mock
    -  Observer w;
    -  @Mock
    -  Observer w2;
    -
    -  // nulls lead to exceptions
    -  final Func1 TO_UPPER_WITH_EXCEPTION = new Func1() {
    -    @Override
    -    public String call(String s) {
    -      if (s.equals("x")) {
    -        return "XX";
    -      }
    -      return s.toUpperCase();
    +    @Mock
    +    Observer w;
    +    @Mock
    +    Observer w2;
    +
    +    // nulls lead to exceptions
    +    final Func1 TO_UPPER_WITH_EXCEPTION = new Func1() {
    +        @Override
    +        public String call(String s) {
    +            if (s.equals("x")) {
    +                return "XX";
    +            }
    +            return s.toUpperCase();
    +        }
    +    };
    +
    +    final Comparator COMPARE_LENGTH = new Comparator() {
    +        @Override
    +        public int compare(String s1, String s2) {
    +            return s1.length() - s2.length();
    +        }
    +    };
    +
    +    @Before
    +    public void before() {
    +        initMocks(this);
         }
    -  };
     
    -  final Comparator COMPARE_LENGTH = new Comparator() {
    -    @Override
    -    public int compare(String s1, String s2) {
    -      return s1.length() - s2.length();
    +    @Test
    +    public void testDistinctOfNone() {
    +        Observable src = Observable.empty();
    +        Observable.create(distinct(src)).subscribe(w);
    +
    +        verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +        verify(w, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testDistinctOfNoneWithKeySelector() {
    +        Observable src = Observable.empty();
    +        Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    +
    +        verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +        verify(w, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testDistinctOfNormalSource() {
    +        Observable src = Observable.from("a", "b", "c", "c", "c", "b", "b", "a", "e");
    +        Observable.create(distinct(src)).subscribe(w);
    +
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("b");
    +        inOrder.verify(w, times(1)).onNext("c");
    +        inOrder.verify(w, times(1)).onNext("e");
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testDistinctOfNormalSourceWithKeySelector() {
    +        Observable src = Observable.from("a", "B", "c", "C", "c", "B", "b", "a", "E");
    +        Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    +
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("B");
    +        inOrder.verify(w, times(1)).onNext("c");
    +        inOrder.verify(w, times(1)).onNext("E");
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testDistinctOfNormalSourceWithComparator() {
    +        Observable src = Observable.from("1", "12", "123", "aaa", "321", "12", "21", "1", "12345");
    +        Observable.create(distinct(src, COMPARE_LENGTH)).subscribe(w);
    +
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("1");
    +        inOrder.verify(w, times(1)).onNext("12");
    +        inOrder.verify(w, times(1)).onNext("123");
    +        inOrder.verify(w, times(1)).onNext("12345");
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testDistinctOfNormalSourceWithKeySelectorAndComparator() {
    +        Observable src = Observable.from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd");
    +        Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w);
    +
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("x");
    +        inOrder.verify(w, times(1)).onNext("abc");
    +        inOrder.verify(w, times(1)).onNext("abcd");
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testDistinctOfNormalSourceWithKeySelectorAndComparatorAndTwoSubscriptions() {
    +        Observable src = Observable.from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd");
    +        Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w);
    +
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("x");
    +        Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2);
    +        inOrder.verify(w, times(1)).onNext("abc");
    +        inOrder.verify(w, times(1)).onNext("abcd");
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +
    +        InOrder inOrder2 = inOrder(w2);
    +        inOrder2.verify(w2, times(1)).onNext("a");
    +        inOrder2.verify(w2, times(1)).onNext("x");
    +        inOrder2.verify(w2, times(1)).onNext("abc");
    +        inOrder2.verify(w2, times(1)).onNext("abcd");
    +        inOrder2.verify(w2, times(1)).onCompleted();
    +        inOrder2.verify(w2, never()).onNext(anyString());
    +        verify(w2, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testDistinctOfSourceWithNulls() {
    +        Observable src = Observable.from(null, "a", "a", null, null, "b", null);
    +        Observable.create(distinct(src)).subscribe(w);
    +
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext(null);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("b");
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testDistinctOfSourceWithExceptionsFromKeySelector() {
    +        Observable src = Observable.from("a", "b", null, "c");
    +        Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    +
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("b");
    +        inOrder.verify(w, times(1)).onError(any(NullPointerException.class));
    +        inOrder.verify(w, never()).onNext(anyString());
    +        inOrder.verify(w, never()).onCompleted();
         }
    -  };
    -
    -  @Before
    -  public void before() {
    -    initMocks(this);
    -  }
    -
    -  @Test
    -  public void testDistinctOfNone() {
    -    Observable src = Observable.empty();
    -    Observable.create(distinct(src)).subscribe(w);
    -
    -    verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -    verify(w, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testDistinctOfNoneWithKeySelector() {
    -    Observable src = Observable.empty();
    -    Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    -
    -    verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -    verify(w, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testDistinctOfNormalSource() {
    -    Observable src = Observable.from("a", "b", "c", "c", "c", "b", "b", "a", "e");
    -    Observable.create(distinct(src)).subscribe(w);
    -
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("b");
    -    inOrder.verify(w, times(1)).onNext("c");
    -    inOrder.verify(w, times(1)).onNext("e");
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testDistinctOfNormalSourceWithKeySelector() {
    -    Observable src = Observable.from("a", "B", "c", "C", "c", "B", "b", "a", "E");
    -    Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    -
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("B");
    -    inOrder.verify(w, times(1)).onNext("c");
    -    inOrder.verify(w, times(1)).onNext("E");
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testDistinctOfNormalSourceWithComparator() {
    -    Observable src = Observable.from("1", "12", "123", "aaa", "321", "12", "21", "1", "12345");
    -    Observable.create(distinct(src, COMPARE_LENGTH)).subscribe(w);
    -
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("1");
    -    inOrder.verify(w, times(1)).onNext("12");
    -    inOrder.verify(w, times(1)).onNext("123");
    -    inOrder.verify(w, times(1)).onNext("12345");
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testDistinctOfNormalSourceWithKeySelectorAndComparator() {
    -    Observable src = Observable.from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd");
    -    Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w);
    -
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("x");
    -    inOrder.verify(w, times(1)).onNext("abc");
    -    inOrder.verify(w, times(1)).onNext("abcd");
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testDistinctOfNormalSourceWithKeySelectorAndComparatorAndTwoSubscriptions() {
    -    Observable src = Observable.from("a", "x", "ab", "abc", "cba", "de", "x", "a", "abcd");
    -    Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w);
    -
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("x");
    -    Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2);
    -    inOrder.verify(w, times(1)).onNext("abc");
    -    inOrder.verify(w, times(1)).onNext("abcd");
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -
    -    InOrder inOrder2 = inOrder(w2);
    -    inOrder2.verify(w2, times(1)).onNext("a");
    -    inOrder2.verify(w2, times(1)).onNext("x");
    -    inOrder2.verify(w2, times(1)).onNext("abc");
    -    inOrder2.verify(w2, times(1)).onNext("abcd");
    -    inOrder2.verify(w2, times(1)).onCompleted();
    -    inOrder2.verify(w2, never()).onNext(anyString());
    -    verify(w2, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testDistinctOfSourceWithNulls() {
    -    Observable src = Observable.from(null, "a", "a", null, null, "b", null);
    -    Observable.create(distinct(src)).subscribe(w);
    -
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext(null);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("b");
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testDistinctOfSourceWithExceptionsFromKeySelector() {
    -    Observable src = Observable.from("a", "b", null, "c");
    -    Observable.create(distinct(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    -
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("b");
    -    inOrder.verify(w, times(1)).onError(any(NullPointerException.class));
    -    inOrder.verify(w, never()).onNext(anyString());
    -    inOrder.verify(w, never()).onCompleted();
    -  }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java b/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java
    index 2ea0167529..bd31b91ebc 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java
    @@ -18,166 +18,166 @@
     
     public class OperationDistinctUntilChangedTest {
     
    -  @Mock
    -  Observer w;
    -  @Mock
    -  Observer w2;
    -
    -  // nulls lead to exceptions
    -  final Func1 TO_UPPER_WITH_EXCEPTION = new Func1() {
    -    @Override
    -    public String call(String s) {
    -      if (s.equals("x")) {
    -        return "xx";
    -      }
    -      return s.toUpperCase();
    +    @Mock
    +    Observer w;
    +    @Mock
    +    Observer w2;
    +
    +    // nulls lead to exceptions
    +    final Func1 TO_UPPER_WITH_EXCEPTION = new Func1() {
    +        @Override
    +        public String call(String s) {
    +            if (s.equals("x")) {
    +                return "xx";
    +            }
    +            return s.toUpperCase();
    +        }
    +    };
    +
    +    final Comparator COMPARE_LENGTH = new Comparator() {
    +        @Override
    +        public int compare(String s1, String s2) {
    +            return s1.length() - s2.length();
    +        }
    +    };
    +
    +    @Before
    +    public void before() {
    +        initMocks(this);
         }
    -  };
     
    -  final Comparator COMPARE_LENGTH = new Comparator() {
    -    @Override
    -    public int compare(String s1, String s2) {
    -      return s1.length() - s2.length();
    +    @Test
    +    public void testDistinctUntilChangedOfNone() {
    +        Observable src = Observable.empty();
    +        Observable.create(distinctUntilChanged(src)).subscribe(w);
    +
    +        verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +        verify(w, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testDistinctUntilChangedOfNoneWithKeySelector() {
    +        Observable src = Observable.empty();
    +        Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    +
    +        verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +        verify(w, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testDistinctUntilChangedOfNormalSource() {
    +        Observable src = Observable.from("a", "b", "c", "c", "c", "b", "b", "a", "e");
    +        Observable.create(distinctUntilChanged(src)).subscribe(w);
    +
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("b");
    +        inOrder.verify(w, times(1)).onNext("c");
    +        inOrder.verify(w, times(1)).onNext("b");
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("e");
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testDistinctUntilChangedOfNormalSourceWithKeySelector() {
    +        Observable src = Observable.from("a", "b", "c", "C", "c", "B", "b", "a", "e");
    +        Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    +
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("b");
    +        inOrder.verify(w, times(1)).onNext("c");
    +        inOrder.verify(w, times(1)).onNext("B");
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("e");
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testDistinctUntilChangedOfSourceWithNulls() {
    +        Observable src = Observable.from(null, "a", "a", null, null, "b", null, null);
    +        Observable.create(distinctUntilChanged(src)).subscribe(w);
    +
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext(null);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext(null);
    +        inOrder.verify(w, times(1)).onNext("b");
    +        inOrder.verify(w, times(1)).onNext(null);
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testDistinctUntilChangedOfSourceWithExceptionsFromKeySelector() {
    +        Observable src = Observable.from("a", "b", null, "c");
    +        Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    +
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("b");
    +        verify(w, times(1)).onError(any(NullPointerException.class));
    +        inOrder.verify(w, never()).onNext(anyString());
    +        inOrder.verify(w, never()).onCompleted();
    +    }
    +
    +    @Test
    +    public void testDistinctUntilChangedWithComparator() {
    +        Observable src = Observable.from("a", "b", "c", "aa", "bb", "c", "ddd");
    +        Observable.create(distinctUntilChanged(src, COMPARE_LENGTH)).subscribe(w);
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("aa");
    +        inOrder.verify(w, times(1)).onNext("c");
    +        inOrder.verify(w, times(1)).onNext("ddd");
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testDistinctUntilChangedWithComparatorAndKeySelector() {
    +        Observable src = Observable.from("a", "b", "x", "aa", "bb", "c", "ddd");
    +        Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w);
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("x");
    +        inOrder.verify(w, times(1)).onNext("c");
    +        inOrder.verify(w, times(1)).onNext("ddd");
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testDistinctUntilChangedWithComparatorAndKeySelectorandTwoSubscriptions() {
    +        Observable src = Observable.from("a", "b", "x", "aa", "bb", "c", "ddd");
    +        Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w);
    +        InOrder inOrder = inOrder(w);
    +        inOrder.verify(w, times(1)).onNext("a");
    +        inOrder.verify(w, times(1)).onNext("x");
    +        Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2);
    +        inOrder.verify(w, times(1)).onNext("c");
    +        inOrder.verify(w, times(1)).onNext("ddd");
    +        inOrder.verify(w, times(1)).onCompleted();
    +        inOrder.verify(w, never()).onNext(anyString());
    +        verify(w, never()).onError(any(Throwable.class));
    +
    +        InOrder inOrder2 = inOrder(w2);
    +        inOrder2.verify(w2, times(1)).onNext("a");
    +        inOrder2.verify(w2, times(1)).onNext("x");
    +        inOrder2.verify(w2, times(1)).onNext("c");
    +        inOrder2.verify(w2, times(1)).onNext("ddd");
    +        inOrder2.verify(w2, times(1)).onCompleted();
    +        inOrder2.verify(w2, never()).onNext(anyString());
    +        verify(w2, never()).onError(any(Throwable.class));
         }
    -  };
    -
    -  @Before
    -  public void before() {
    -    initMocks(this);
    -  }
    -
    -  @Test
    -  public void testDistinctUntilChangedOfNone() {
    -    Observable src = Observable.empty();
    -    Observable.create(distinctUntilChanged(src)).subscribe(w);
    -
    -    verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -    verify(w, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testDistinctUntilChangedOfNoneWithKeySelector() {
    -    Observable src = Observable.empty();
    -    Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    -
    -    verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -    verify(w, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testDistinctUntilChangedOfNormalSource() {
    -    Observable src = Observable.from("a", "b", "c", "c", "c", "b", "b", "a", "e");
    -    Observable.create(distinctUntilChanged(src)).subscribe(w);
    -
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("b");
    -    inOrder.verify(w, times(1)).onNext("c");
    -    inOrder.verify(w, times(1)).onNext("b");
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("e");
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testDistinctUntilChangedOfNormalSourceWithKeySelector() {
    -    Observable src = Observable.from("a", "b", "c", "C", "c", "B", "b", "a", "e");
    -    Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    -
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("b");
    -    inOrder.verify(w, times(1)).onNext("c");
    -    inOrder.verify(w, times(1)).onNext("B");
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("e");
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testDistinctUntilChangedOfSourceWithNulls() {
    -    Observable src = Observable.from(null, "a", "a", null, null, "b", null, null);
    -    Observable.create(distinctUntilChanged(src)).subscribe(w);
    -
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext(null);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext(null);
    -    inOrder.verify(w, times(1)).onNext("b");
    -    inOrder.verify(w, times(1)).onNext(null);
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testDistinctUntilChangedOfSourceWithExceptionsFromKeySelector() {
    -    Observable src = Observable.from("a", "b", null, "c");
    -    Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION)).subscribe(w);
    -
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("b");
    -    verify(w, times(1)).onError(any(NullPointerException.class));
    -    inOrder.verify(w, never()).onNext(anyString());
    -    inOrder.verify(w, never()).onCompleted();
    -  }
    -
    -  @Test
    -  public void testDistinctUntilChangedWithComparator() {
    -    Observable src = Observable.from("a", "b", "c", "aa", "bb", "c", "ddd");
    -    Observable.create(distinctUntilChanged(src, COMPARE_LENGTH)).subscribe(w);
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("aa");
    -    inOrder.verify(w, times(1)).onNext("c");
    -    inOrder.verify(w, times(1)).onNext("ddd");
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testDistinctUntilChangedWithComparatorAndKeySelector() {
    -    Observable src = Observable.from("a", "b", "x", "aa", "bb", "c", "ddd");
    -    Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w);
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("x");
    -    inOrder.verify(w, times(1)).onNext("c");
    -    inOrder.verify(w, times(1)).onNext("ddd");
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testDistinctUntilChangedWithComparatorAndKeySelectorandTwoSubscriptions() {
    -    Observable src = Observable.from("a", "b", "x", "aa", "bb", "c", "ddd");
    -    Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w);
    -    InOrder inOrder = inOrder(w);
    -    inOrder.verify(w, times(1)).onNext("a");
    -    inOrder.verify(w, times(1)).onNext("x");
    -    Observable.create(distinctUntilChanged(src, TO_UPPER_WITH_EXCEPTION, COMPARE_LENGTH)).subscribe(w2);
    -    inOrder.verify(w, times(1)).onNext("c");
    -    inOrder.verify(w, times(1)).onNext("ddd");
    -    inOrder.verify(w, times(1)).onCompleted();
    -    inOrder.verify(w, never()).onNext(anyString());
    -    verify(w, never()).onError(any(Throwable.class));
    -
    -    InOrder inOrder2 = inOrder(w2);
    -    inOrder2.verify(w2, times(1)).onNext("a");
    -    inOrder2.verify(w2, times(1)).onNext("x");
    -    inOrder2.verify(w2, times(1)).onNext("c");
    -    inOrder2.verify(w2, times(1)).onNext("ddd");
    -    inOrder2.verify(w2, times(1)).onCompleted();
    -    inOrder2.verify(w2, never()).onNext(anyString());
    -    verify(w2, never()).onError(any(Throwable.class));
    -  }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java b/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java
    index 4f1326df06..8642e78300 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java
    @@ -1,113 +1,112 @@
     package rx.operators;
     
    -import org.junit.Test;
    -import rx.Observable;
    -import rx.Observer;
    +import static org.junit.Assert.*;
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationElementAt.*;
     
     import java.util.Iterator;
     import java.util.concurrent.ExecutionException;
     
    -import static org.junit.Assert.assertTrue;
    -import static org.junit.Assert.fail;
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationElementAt.elementAt;
    -import static rx.operators.OperationElementAt.elementAtOrDefault;
    +import org.junit.Test;
    +
    +import rx.Observable;
    +import rx.Observer;
     
     public class OperationElementAtTest {
     
    -  @Test
    -  public void testElementAt() {
    -    Observable w = Observable.from(1, 2);
    -    Observable observable = Observable.create(elementAt(w, 1));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, never()).onNext(1);
    -    verify(aObserver, times(1)).onNext(2);
    -    verify(aObserver, never()).onError(
    -        any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testElementAtWithMinusIndex() {
    -    Observable w = Observable.from(1, 2);
    -    Observable observable = Observable
    -        .create(elementAt(w, -1));
    -
    -    try {
    -      Iterator iter = OperationToIterator
    -          .toIterator(observable);
    -      assertTrue(iter.hasNext());
    -      iter.next();
    -      fail("expect an IndexOutOfBoundsException when index is out of bounds");
    -    } catch (IndexOutOfBoundsException e) {
    +    @Test
    +    public void testElementAt() {
    +        Observable w = Observable.from(1, 2);
    +        Observable observable = Observable.create(elementAt(w, 1));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, never()).onNext(1);
    +        verify(aObserver, times(1)).onNext(2);
    +        verify(aObserver, never()).onError(
    +                any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testElementAtWithMinusIndex() {
    +        Observable w = Observable.from(1, 2);
    +        Observable observable = Observable
    +                .create(elementAt(w, -1));
    +
    +        try {
    +            Iterator iter = OperationToIterator
    +                    .toIterator(observable);
    +            assertTrue(iter.hasNext());
    +            iter.next();
    +            fail("expect an IndexOutOfBoundsException when index is out of bounds");
    +        } catch (IndexOutOfBoundsException e) {
    +        }
    +    }
    +
    +    @Test
    +    public void testElementAtWithIndexOutOfBounds()
    +            throws InterruptedException, ExecutionException {
    +        Observable w = Observable.from(1, 2);
    +        Observable observable = Observable.create(elementAt(w, 2));
    +        try {
    +            Iterator iter = OperationToIterator
    +                    .toIterator(observable);
    +            assertTrue(iter.hasNext());
    +            iter.next();
    +            fail("expect an IndexOutOfBoundsException when index is out of bounds");
    +        } catch (IndexOutOfBoundsException e) {
    +        }
         }
    -  }
    -
    -  @Test
    -  public void testElementAtWithIndexOutOfBounds()
    -      throws InterruptedException, ExecutionException {
    -    Observable w = Observable.from(1, 2);
    -    Observable observable = Observable.create(elementAt(w, 2));
    -    try {
    -      Iterator iter = OperationToIterator
    -          .toIterator(observable);
    -      assertTrue(iter.hasNext());
    -      iter.next();
    -      fail("expect an IndexOutOfBoundsException when index is out of bounds");
    -    } catch (IndexOutOfBoundsException e) {
    +
    +    @Test
    +    public void testElementAtOrDefault() throws InterruptedException,
    +            ExecutionException {
    +        Observable w = Observable.from(1, 2);
    +        Observable observable = Observable
    +                .create(elementAtOrDefault(w, 1, 0));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, never()).onNext(1);
    +        verify(aObserver, times(1)).onNext(2);
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
         }
    -  }
    -
    -  @Test
    -  public void testElementAtOrDefault() throws InterruptedException,
    -      ExecutionException {
    -    Observable w = Observable.from(1, 2);
    -    Observable observable = Observable
    -        .create(elementAtOrDefault(w, 1, 0));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, never()).onNext(1);
    -    verify(aObserver, times(1)).onNext(2);
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testElementAtOrDefaultWithIndexOutOfBounds()
    -      throws InterruptedException, ExecutionException {
    -    Observable w = Observable.from(1, 2);
    -    Observable observable = Observable
    -        .create(elementAtOrDefault(w, 2, 0));
    -
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, never()).onNext(1);
    -    verify(aObserver, never()).onNext(2);
    -    verify(aObserver, times(1)).onNext(0);
    -    verify(aObserver, never()).onError(any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testElementAtOrDefaultWithMinusIndex() {
    -    Observable w = Observable.from(1, 2);
    -    Observable observable = Observable
    -        .create(elementAtOrDefault(w, -1, 0));
    -
    -    try {
    -      Iterator iter = OperationToIterator
    -          .toIterator(observable);
    -      assertTrue(iter.hasNext());
    -      iter.next();
    -      fail("expect an IndexOutOfBoundsException when index is out of bounds");
    -    } catch (IndexOutOfBoundsException e) {
    +
    +    @Test
    +    public void testElementAtOrDefaultWithIndexOutOfBounds()
    +            throws InterruptedException, ExecutionException {
    +        Observable w = Observable.from(1, 2);
    +        Observable observable = Observable
    +                .create(elementAtOrDefault(w, 2, 0));
    +
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, never()).onNext(1);
    +        verify(aObserver, never()).onNext(2);
    +        verify(aObserver, times(1)).onNext(0);
    +        verify(aObserver, never()).onError(any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
    +
    +    @Test
    +    public void testElementAtOrDefaultWithMinusIndex() {
    +        Observable w = Observable.from(1, 2);
    +        Observable observable = Observable
    +                .create(elementAtOrDefault(w, -1, 0));
    +
    +        try {
    +            Iterator iter = OperationToIterator
    +                    .toIterator(observable);
    +            assertTrue(iter.hasNext());
    +            iter.next();
    +            fail("expect an IndexOutOfBoundsException when index is out of bounds");
    +        } catch (IndexOutOfBoundsException e) {
    +        }
         }
    -  }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java b/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java
    index a6da7261e1..88fb4deacb 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java
    @@ -1,35 +1,36 @@
     package rx.operators;
     
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationFilter.*;
    +
     import org.junit.Test;
     import org.mockito.Mockito;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.util.functions.Func1;
     
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationFilter.filter;
    -
     public class OperationFilterTest {
     
    -  @Test
    -  public void testFilter() {
    -    Observable w = Observable.from("one", "two", "three");
    -    Observable observable = Observable.create(filter(w, new Func1() {
    +    @Test
    +    public void testFilter() {
    +        Observable w = Observable.from("one", "two", "three");
    +        Observable observable = Observable.create(filter(w, new Func1() {
     
    -      @Override
    -      public Boolean call(String t1) {
    -        return t1.equals("two");
    -      }
    -    }));
    +            @Override
    +            public Boolean call(String t1) {
    +                return t1.equals("two");
    +            }
    +        }));
     
    -    @SuppressWarnings("unchecked")
    -    Observer aObserver = mock(Observer.class);
    -    observable.subscribe(aObserver);
    -    verify(aObserver, Mockito.never()).onNext("one");
    -    verify(aObserver, times(1)).onNext("two");
    -    verify(aObserver, Mockito.never()).onNext("three");
    -    verify(aObserver, Mockito.never()).onError(any(Throwable.class));
    -    verify(aObserver, times(1)).onCompleted();
    -  }
    +        @SuppressWarnings("unchecked")
    +        Observer aObserver = mock(Observer.class);
    +        observable.subscribe(aObserver);
    +        verify(aObserver, Mockito.never()).onNext("one");
    +        verify(aObserver, times(1)).onNext("two");
    +        verify(aObserver, Mockito.never()).onNext("three");
    +        verify(aObserver, Mockito.never()).onError(any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java b/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java
    index b856c86fa6..c0fd0d9ef2 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java
    @@ -1,38 +1,40 @@
     package rx.operators;
     
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationFinally.*;
    +
     import org.junit.Before;
     import org.junit.Test;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.util.functions.Action0;
     
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationFinally.finallyDo;
    -
     public class OperationFinallyTest {
     
    -  private Action0 aAction0;
    -  private Observer aObserver;
    -
    -  @SuppressWarnings("unchecked") // mocking has to be unchecked, unfortunately
    -  @Before
    -  public void before() {
    -    aAction0 = mock(Action0.class);
    -    aObserver = mock(Observer.class);
    -  }
    -
    -  private void checkActionCalled(Observable input) {
    -    Observable.create(finallyDo(input, aAction0)).subscribe(aObserver);
    -    verify(aAction0, times(1)).call();
    -  }
    -
    -  @Test
    -  public void testFinallyCalledOnComplete() {
    -    checkActionCalled(Observable.from(new String[]{"1", "2", "3"}));
    -  }
    -
    -  @Test
    -  public void testFinallyCalledOnError() {
    -    checkActionCalled(Observable.error(new RuntimeException("expected")));
    -  }
    +    private Action0 aAction0;
    +    private Observer aObserver;
    +
    +    @SuppressWarnings("unchecked")
    +    // mocking has to be unchecked, unfortunately
    +    @Before
    +    public void before() {
    +        aAction0 = mock(Action0.class);
    +        aObserver = mock(Observer.class);
    +    }
    +
    +    private void checkActionCalled(Observable input) {
    +        Observable.create(finallyDo(input, aAction0)).subscribe(aObserver);
    +        verify(aAction0, times(1)).call();
    +    }
    +
    +    @Test
    +    public void testFinallyCalledOnComplete() {
    +        checkActionCalled(Observable.from(new String[] { "1", "2", "3" }));
    +    }
    +
    +    @Test
    +    public void testFinallyCalledOnError() {
    +        checkActionCalled(Observable. error(new RuntimeException("expected")));
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java b/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java
    index f645fd785e..b17ba4740e 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java
    @@ -15,62 +15,62 @@
     
     public class OperationFirstOrDefaultTest {
     
    -  @Mock
    -  Observer w;
    +    @Mock
    +    Observer w;
     
    -  private static final Func1 IS_D = new Func1() {
    -    @Override
    -    public Boolean call(String value) {
    -      return "d".equals(value);
    -    }
    -  };
    +    private static final Func1 IS_D = new Func1() {
    +        @Override
    +        public Boolean call(String value) {
    +            return "d".equals(value);
    +        }
    +    };
     
    -  @Before
    -  public void before() {
    -    initMocks(this);
    -  }
    +    @Before
    +    public void before() {
    +        initMocks(this);
    +    }
     
    -  @Test
    -  public void testFirstOrElseOfNone() {
    -    Observable src = Observable.empty();
    -    Observable.create(firstOrDefault(src, "default")).subscribe(w);
    +    @Test
    +    public void testFirstOrElseOfNone() {
    +        Observable src = Observable.empty();
    +        Observable.create(firstOrDefault(src, "default")).subscribe(w);
     
    -    verify(w, times(1)).onNext(anyString());
    -    verify(w, times(1)).onNext("default");
    -    verify(w, never()).onError(any(Throwable.class));
    -    verify(w, times(1)).onCompleted();
    -  }
    +        verify(w, times(1)).onNext(anyString());
    +        verify(w, times(1)).onNext("default");
    +        verify(w, never()).onError(any(Throwable.class));
    +        verify(w, times(1)).onCompleted();
    +    }
     
    -  @Test
    -  public void testFirstOrElseOfSome() {
    -    Observable src = Observable.from("a", "b", "c");
    -    Observable.create(firstOrDefault(src, "default")).subscribe(w);
    +    @Test
    +    public void testFirstOrElseOfSome() {
    +        Observable src = Observable.from("a", "b", "c");
    +        Observable.create(firstOrDefault(src, "default")).subscribe(w);
     
    -    verify(w, times(1)).onNext(anyString());
    -    verify(w, times(1)).onNext("a");
    -    verify(w, never()).onError(any(Throwable.class));
    -    verify(w, times(1)).onCompleted();
    -  }
    +        verify(w, times(1)).onNext(anyString());
    +        verify(w, times(1)).onNext("a");
    +        verify(w, never()).onError(any(Throwable.class));
    +        verify(w, times(1)).onCompleted();
    +    }
     
    -  @Test
    -  public void testFirstOrElseWithPredicateOfNoneMatchingThePredicate() {
    -    Observable src = Observable.from("a", "b", "c");
    -    Observable.create(firstOrDefault(src, IS_D, "default")).subscribe(w);
    +    @Test
    +    public void testFirstOrElseWithPredicateOfNoneMatchingThePredicate() {
    +        Observable src = Observable.from("a", "b", "c");
    +        Observable.create(firstOrDefault(src, IS_D, "default")).subscribe(w);
     
    -    verify(w, times(1)).onNext(anyString());
    -    verify(w, times(1)).onNext("default");
    -    verify(w, never()).onError(any(Throwable.class));
    -    verify(w, times(1)).onCompleted();
    -  }
    +        verify(w, times(1)).onNext(anyString());
    +        verify(w, times(1)).onNext("default");
    +        verify(w, never()).onError(any(Throwable.class));
    +        verify(w, times(1)).onCompleted();
    +    }
     
    -  @Test
    -  public void testFirstOrElseWithPredicateOfSome() {
    -    Observable src = Observable.from("a", "b", "c", "d", "e", "f");
    -    Observable.create(firstOrDefault(src, IS_D, "default")).subscribe(w);
    +    @Test
    +    public void testFirstOrElseWithPredicateOfSome() {
    +        Observable src = Observable.from("a", "b", "c", "d", "e", "f");
    +        Observable.create(firstOrDefault(src, IS_D, "default")).subscribe(w);
     
    -    verify(w, times(1)).onNext(anyString());
    -    verify(w, times(1)).onNext("d");
    -    verify(w, never()).onError(any(Throwable.class));
    -    verify(w, times(1)).onCompleted();
    -  }
    +        verify(w, times(1)).onNext(anyString());
    +        verify(w, times(1)).onNext("d");
    +        verify(w, never()).onError(any(Throwable.class));
    +        verify(w, times(1)).onCompleted();
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java b/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java
    index e62be97d11..86d5af6e46 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java
    @@ -1,14 +1,7 @@
     package rx.operators;
     
    -import org.junit.Test;
    -import rx.Observable;
    -import rx.Observer;
    -import rx.Subscription;
    -import rx.observables.GroupedObservable;
    -import rx.subscriptions.BooleanSubscription;
    -import rx.subscriptions.Subscriptions;
    -import rx.util.functions.Action1;
    -import rx.util.functions.Func1;
    +import static org.junit.Assert.*;
    +import static rx.operators.OperationGroupBy.*;
     
     import java.util.Arrays;
     import java.util.Collection;
    @@ -20,312 +13,320 @@
     import java.util.concurrent.atomic.AtomicInteger;
     import java.util.concurrent.atomic.AtomicReference;
     
    -import static org.junit.Assert.*;
    -import static rx.operators.OperationGroupBy.groupBy;
    +import org.junit.Test;
    +
    +import rx.Observable;
    +import rx.Observer;
    +import rx.Subscription;
    +import rx.observables.GroupedObservable;
    +import rx.subscriptions.BooleanSubscription;
    +import rx.subscriptions.Subscriptions;
    +import rx.util.functions.Action1;
    +import rx.util.functions.Func1;
     
     public class OperationGroupByTest {
     
    -  final Func1 length = new Func1() {
    -    @Override
    -    public Integer call(String s) {
    -      return s.length();
    +    final Func1 length = new Func1() {
    +        @Override
    +        public Integer call(String s) {
    +            return s.length();
    +        }
    +    };
    +
    +    @Test
    +    public void testGroupBy() {
    +        Observable source = Observable.from("one", "two", "three", "four", "five", "six");
    +        Observable> grouped = Observable.create(groupBy(source, length));
    +
    +        Map> map = toMap(grouped);
    +
    +        assertEquals(3, map.size());
    +        assertArrayEquals(Arrays.asList("one", "two", "six").toArray(), map.get(3).toArray());
    +        assertArrayEquals(Arrays.asList("four", "five").toArray(), map.get(4).toArray());
    +        assertArrayEquals(Arrays.asList("three").toArray(), map.get(5).toArray());
         }
    -  };
     
    -  @Test
    -  public void testGroupBy() {
    -    Observable source = Observable.from("one", "two", "three", "four", "five", "six");
    -    Observable> grouped = Observable.create(groupBy(source, length));
    +    @Test
    +    public void testEmpty() {
    +        Observable source = Observable.empty();
    +        Observable> grouped = Observable.create(groupBy(source, length));
     
    -    Map> map = toMap(grouped);
    +        Map> map = toMap(grouped);
    +
    +        assertTrue(map.isEmpty());
    +    }
     
    -    assertEquals(3, map.size());
    -    assertArrayEquals(Arrays.asList("one", "two", "six").toArray(), map.get(3).toArray());
    -    assertArrayEquals(Arrays.asList("four", "five").toArray(), map.get(4).toArray());
    -    assertArrayEquals(Arrays.asList("three").toArray(), map.get(5).toArray());
    -  }
    +    @Test
    +    public void testError() {
    +        Observable sourceStrings = Observable.from("one", "two", "three", "four", "five", "six");
    +        Observable errorSource = Observable.error(new RuntimeException("forced failure"));
    +        Observable source = Observable.concat(sourceStrings, errorSource);
     
    -  @Test
    -  public void testEmpty() {
    -    Observable source = Observable.empty();
    -    Observable> grouped = Observable.create(groupBy(source, length));
    +        Observable> grouped = Observable.create(groupBy(source, length));
     
    -    Map> map = toMap(grouped);
    +        final AtomicInteger groupCounter = new AtomicInteger();
    +        final AtomicInteger eventCounter = new AtomicInteger();
    +        final AtomicReference error = new AtomicReference();
     
    -    assertTrue(map.isEmpty());
    -  }
    +        grouped.mapMany(new Func1, Observable>() {
     
    -  @Test
    -  public void testError() {
    -    Observable sourceStrings = Observable.from("one", "two", "three", "four", "five", "six");
    -    Observable errorSource = Observable.error(new RuntimeException("forced failure"));
    -    Observable source = Observable.concat(sourceStrings, errorSource);
    +            @Override
    +            public Observable call(final GroupedObservable o) {
    +                groupCounter.incrementAndGet();
    +                return o.map(new Func1() {
    +
    +                    @Override
    +                    public String call(String v) {
    +                        return "Event => key: " + o.getKey() + " value: " + v;
    +                    }
    +                });
    +            }
    +        }).subscribe(new Observer() {
     
    -    Observable> grouped = Observable.create(groupBy(source, length));
    +            @Override
    +            public void onCompleted() {
     
    -    final AtomicInteger groupCounter = new AtomicInteger();
    -    final AtomicInteger eventCounter = new AtomicInteger();
    -    final AtomicReference error = new AtomicReference();
    +            }
     
    -    grouped.mapMany(new Func1, Observable>() {
    +            @Override
    +            public void onError(Throwable e) {
    +                e.printStackTrace();
    +                error.set(e);
    +            }
     
    -      @Override
    -      public Observable call(final GroupedObservable o) {
    -        groupCounter.incrementAndGet();
    -        return o.map(new Func1() {
    +            @Override
    +            public void onNext(String v) {
    +                eventCounter.incrementAndGet();
    +                System.out.println(v);
     
    -          @Override
    -          public String call(String v) {
    -            return "Event => key: " + o.getKey() + " value: " + v;
    -          }
    +            }
             });
    -      }
    -    }).subscribe(new Observer() {
     
    -      @Override
    -      public void onCompleted() {
    +        assertEquals(3, groupCounter.get());
    +        assertEquals(6, eventCounter.get());
    +        assertNotNull(error.get());
    +    }
     
    -      }
    +    private static  Map> toMap(Observable> observable) {
     
    -      @Override
    -      public void onError(Throwable e) {
    -        e.printStackTrace();
    -        error.set(e);
    -      }
    +        final ConcurrentHashMap> result = new ConcurrentHashMap>();
     
    -      @Override
    -      public void onNext(String v) {
    -        eventCounter.incrementAndGet();
    -        System.out.println(v);
    +        observable.toBlockingObservable().forEach(new Action1>() {
     
    -      }
    -    });
    +            @Override
    +            public void call(final GroupedObservable o) {
    +                result.put(o.getKey(), new ConcurrentLinkedQueue());
    +                o.subscribe(new Action1() {
     
    -    assertEquals(3, groupCounter.get());
    -    assertEquals(6, eventCounter.get());
    -    assertNotNull(error.get());
    -  }
    +                    @Override
    +                    public void call(V v) {
    +                        result.get(o.getKey()).add(v);
    +                    }
     
    -  private static  Map> toMap(Observable> observable) {
    +                });
    +            }
    +        });
     
    -    final ConcurrentHashMap> result = new ConcurrentHashMap>();
    +        return result;
    +    }
     
    -    observable.toBlockingObservable().forEach(new Action1>() {
    +    /**
    +     * Assert that only a single subscription to a stream occurs and that all events are received.
    +     * 
    +     * @throws Throwable
    +     */
    +    @Test
    +    public void testGroupedEventStream() throws Throwable {
    +
    +        final AtomicInteger eventCounter = new AtomicInteger();
    +        final AtomicInteger subscribeCounter = new AtomicInteger();
    +        final AtomicInteger groupCounter = new AtomicInteger();
    +        final CountDownLatch latch = new CountDownLatch(1);
    +        final int count = 100;
    +        final int groupCount = 2;
    +
    +        Observable es = Observable.create(new Observable.OnSubscribeFunc() {
    +
    +            @Override
    +            public Subscription onSubscribe(final Observer observer) {
    +                System.out.println("*** Subscribing to EventStream ***");
    +                subscribeCounter.incrementAndGet();
    +                new Thread(new Runnable() {
    +
    +                    @Override
    +                    public void run() {
    +                        for (int i = 0; i < count; i++) {
    +                            Event e = new Event();
    +                            e.source = i % groupCount;
    +                            e.message = "Event-" + i;
    +                            observer.onNext(e);
    +                        }
    +                        observer.onCompleted();
    +                    }
    +
    +                }).start();
    +                return Subscriptions.empty();
    +            }
     
    -      @Override
    -      public void call(final GroupedObservable o) {
    -        result.put(o.getKey(), new ConcurrentLinkedQueue());
    -        o.subscribe(new Action1() {
    +        });
     
    -          @Override
    -          public void call(V v) {
    -            result.get(o.getKey()).add(v);
    -          }
    +        es.groupBy(new Func1() {
     
    -        });
    -      }
    -    });
    -
    -    return result;
    -  }
    -
    -  /**
    -   * Assert that only a single subscription to a stream occurs and that all events are received.
    -   *
    -   * @throws Throwable
    -   */
    -  @Test
    -  public void testGroupedEventStream() throws Throwable {
    -
    -    final AtomicInteger eventCounter = new AtomicInteger();
    -    final AtomicInteger subscribeCounter = new AtomicInteger();
    -    final AtomicInteger groupCounter = new AtomicInteger();
    -    final CountDownLatch latch = new CountDownLatch(1);
    -    final int count = 100;
    -    final int groupCount = 2;
    -
    -    Observable es = Observable.create(new Observable.OnSubscribeFunc() {
    -
    -      @Override
    -      public Subscription onSubscribe(final Observer observer) {
    -        System.out.println("*** Subscribing to EventStream ***");
    -        subscribeCounter.incrementAndGet();
    -        new Thread(new Runnable() {
    -
    -          @Override
    -          public void run() {
    -            for (int i = 0; i < count; i++) {
    -              Event e = new Event();
    -              e.source = i % groupCount;
    -              e.message = "Event-" + i;
    -              observer.onNext(e);
    +            @Override
    +            public Integer call(Event e) {
    +                return e.source;
                 }
    -            observer.onCompleted();
    -          }
    +        }).mapMany(new Func1, Observable>() {
     
    -        }).start();
    -        return Subscriptions.empty();
    -      }
    +            @Override
    +            public Observable call(GroupedObservable eventGroupedObservable) {
    +                System.out.println("GroupedObservable Key: " + eventGroupedObservable.getKey());
    +                groupCounter.incrementAndGet();
     
    -    });
    +                return eventGroupedObservable.map(new Func1() {
     
    -    es.groupBy(new Func1() {
    +                    @Override
    +                    public String call(Event event) {
    +                        return "Source: " + event.source + "  Message: " + event.message;
    +                    }
    +                });
     
    -      @Override
    -      public Integer call(Event e) {
    -        return e.source;
    -      }
    -    }).mapMany(new Func1, Observable>() {
    +            }
    +        }).subscribe(new Observer() {
     
    -      @Override
    -      public Observable call(GroupedObservable eventGroupedObservable) {
    -        System.out.println("GroupedObservable Key: " + eventGroupedObservable.getKey());
    -        groupCounter.incrementAndGet();
    +            @Override
    +            public void onCompleted() {
    +                latch.countDown();
    +            }
    +
    +            @Override
    +            public void onError(Throwable e) {
    +                e.printStackTrace();
    +                latch.countDown();
    +            }
    +
    +            @Override
    +            public void onNext(String outputMessage) {
    +                System.out.println(outputMessage);
    +                eventCounter.incrementAndGet();
    +            }
    +        });
    +
    +        latch.await(5000, TimeUnit.MILLISECONDS);
    +        assertEquals(1, subscribeCounter.get());
    +        assertEquals(groupCount, groupCounter.get());
    +        assertEquals(count, eventCounter.get());
     
    -        return eventGroupedObservable.map(new Func1() {
    +    }
    +
    +    /*
    +     * We will only take 1 group with 20 events from it and then unsubscribe.
    +     */
    +    @Test
    +    public void testUnsubscribe() throws InterruptedException {
    +
    +        final AtomicInteger eventCounter = new AtomicInteger();
    +        final AtomicInteger subscribeCounter = new AtomicInteger();
    +        final AtomicInteger groupCounter = new AtomicInteger();
    +        final AtomicInteger sentEventCounter = new AtomicInteger();
    +        final CountDownLatch latch = new CountDownLatch(1);
    +        final int count = 100;
    +        final int groupCount = 2;
    +
    +        Observable es = Observable.create(new Observable.OnSubscribeFunc() {
    +
    +            @Override
    +            public Subscription onSubscribe(final Observer observer) {
    +                final BooleanSubscription s = new BooleanSubscription();
    +                System.out.println("testUnsubscribe => *** Subscribing to EventStream ***");
    +                subscribeCounter.incrementAndGet();
    +                new Thread(new Runnable() {
    +
    +                    @Override
    +                    public void run() {
    +                        for (int i = 0; i < count; i++) {
    +                            if (s.isUnsubscribed()) {
    +                                break;
    +                            }
    +                            Event e = new Event();
    +                            e.source = i % groupCount;
    +                            e.message = "Event-" + i;
    +                            observer.onNext(e);
    +                            sentEventCounter.incrementAndGet();
    +                        }
    +                        observer.onCompleted();
    +                    }
    +
    +                }).start();
    +                return s;
    +            }
     
    -          @Override
    -          public String call(Event event) {
    -            return "Source: " + event.source + "  Message: " + event.message;
    -          }
             });
     
    -      }
    -    }).subscribe(new Observer() {
    -
    -      @Override
    -      public void onCompleted() {
    -        latch.countDown();
    -      }
    -
    -      @Override
    -      public void onError(Throwable e) {
    -        e.printStackTrace();
    -        latch.countDown();
    -      }
    -
    -      @Override
    -      public void onNext(String outputMessage) {
    -        System.out.println(outputMessage);
    -        eventCounter.incrementAndGet();
    -      }
    -    });
    -
    -    latch.await(5000, TimeUnit.MILLISECONDS);
    -    assertEquals(1, subscribeCounter.get());
    -    assertEquals(groupCount, groupCounter.get());
    -    assertEquals(count, eventCounter.get());
    -
    -  }
    -
    -  /*
    -   * We will only take 1 group with 20 events from it and then unsubscribe.
    -   */
    -  @Test
    -  public void testUnsubscribe() throws InterruptedException {
    -
    -    final AtomicInteger eventCounter = new AtomicInteger();
    -    final AtomicInteger subscribeCounter = new AtomicInteger();
    -    final AtomicInteger groupCounter = new AtomicInteger();
    -    final AtomicInteger sentEventCounter = new AtomicInteger();
    -    final CountDownLatch latch = new CountDownLatch(1);
    -    final int count = 100;
    -    final int groupCount = 2;
    -
    -    Observable es = Observable.create(new Observable.OnSubscribeFunc() {
    -
    -      @Override
    -      public Subscription onSubscribe(final Observer observer) {
    -        final BooleanSubscription s = new BooleanSubscription();
    -        System.out.println("testUnsubscribe => *** Subscribing to EventStream ***");
    -        subscribeCounter.incrementAndGet();
    -        new Thread(new Runnable() {
    -
    -          @Override
    -          public void run() {
    -            for (int i = 0; i < count; i++) {
    -              if (s.isUnsubscribed()) {
    -                break;
    -              }
    -              Event e = new Event();
    -              e.source = i % groupCount;
    -              e.message = "Event-" + i;
    -              observer.onNext(e);
    -              sentEventCounter.incrementAndGet();
    +        es.groupBy(new Func1() {
    +
    +            @Override
    +            public Integer call(Event e) {
    +                return e.source;
                 }
    -            observer.onCompleted();
    -          }
    -
    -        }).start();
    -        return s;
    -      }
    -
    -    });
    -
    -    es.groupBy(new Func1() {
    -
    -      @Override
    -      public Integer call(Event e) {
    -        return e.source;
    -      }
    -    })
    -        .take(1) // we want only the first group
    -        .mapMany(new Func1, Observable>() {
    -
    -          @Override
    -          public Observable call(GroupedObservable eventGroupedObservable) {
    -            System.out.println("testUnsubscribe => GroupedObservable Key: " + eventGroupedObservable.getKey());
    -            groupCounter.incrementAndGet();
    -
    -            return eventGroupedObservable
    -                .take(20) // limit to only 20 events on this group
    -                .map(new Func1() {
    -
    -                  @Override
    -                  public String call(Event event) {
    -                    return "testUnsubscribe => Source: " + event.source + "  Message: " + event.message;
    -                  }
    +        })
    +                .take(1) // we want only the first group
    +                .mapMany(new Func1, Observable>() {
    +
    +                    @Override
    +                    public Observable call(GroupedObservable eventGroupedObservable) {
    +                        System.out.println("testUnsubscribe => GroupedObservable Key: " + eventGroupedObservable.getKey());
    +                        groupCounter.incrementAndGet();
    +
    +                        return eventGroupedObservable
    +                                .take(20) // limit to only 20 events on this group
    +                                .map(new Func1() {
    +
    +                                    @Override
    +                                    public String call(Event event) {
    +                                        return "testUnsubscribe => Source: " + event.source + "  Message: " + event.message;
    +                                    }
    +                                });
    +
    +                    }
    +                }).subscribe(new Observer() {
    +
    +                    @Override
    +                    public void onCompleted() {
    +                        latch.countDown();
    +                    }
    +
    +                    @Override
    +                    public void onError(Throwable e) {
    +                        e.printStackTrace();
    +                        latch.countDown();
    +                    }
    +
    +                    @Override
    +                    public void onNext(String outputMessage) {
    +                        System.out.println(outputMessage);
    +                        eventCounter.incrementAndGet();
    +                    }
                     });
     
    -          }
    -        }).subscribe(new Observer() {
    +        latch.await(5000, TimeUnit.MILLISECONDS);
    +        assertEquals(1, subscribeCounter.get());
    +        assertEquals(1, groupCounter.get());
    +        assertEquals(20, eventCounter.get());
    +        // sentEvents will go until 'eventCounter' hits 20 and then unsubscribes
    +        // which means it will also send (but ignore) the 19/20 events for the other group
    +        // It will not however send all 100 events.
    +        assertEquals(39, sentEventCounter.get(), 10);
    +        // gave it a delta of 10 to account for the threading/unsubscription race condition which can vary depending on a machines performance, thread-scheduler, etc
    +    }
    +
    +    private static class Event {
    +        int source;
    +        String message;
     
    -      @Override
    -      public void onCompleted() {
    -        latch.countDown();
    -      }
    -
    -      @Override
    -      public void onError(Throwable e) {
    -        e.printStackTrace();
    -        latch.countDown();
    -      }
    -
    -      @Override
    -      public void onNext(String outputMessage) {
    -        System.out.println(outputMessage);
    -        eventCounter.incrementAndGet();
    -      }
    -    });
    -
    -    latch.await(5000, TimeUnit.MILLISECONDS);
    -    assertEquals(1, subscribeCounter.get());
    -    assertEquals(1, groupCounter.get());
    -    assertEquals(20, eventCounter.get());
    -    // sentEvents will go until 'eventCounter' hits 20 and then unsubscribes
    -    // which means it will also send (but ignore) the 19/20 events for the other group
    -    // It will not however send all 100 events.
    -    assertEquals(39, sentEventCounter.get(), 10);
    -    // gave it a delta of 10 to account for the threading/unsubscription race condition which can vary depending on a machines performance, thread-scheduler, etc
    -  }
    -
    -  private static class Event {
    -    int source;
    -    String message;
    -
    -    @Override
    -    public String toString() {
    -      return "Event => source: " + source + " message: " + message;
    +        @Override
    +        public String toString() {
    +            return "Event => source: " + source + " message: " + message;
    +        }
         }
    -  }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java b/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java
    index 0ffd27c85b..86764bd6ae 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java
    @@ -1,176 +1,177 @@
     package rx.operators;
     
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +
    +import java.util.concurrent.TimeUnit;
    +
     import org.junit.Before;
     import org.junit.Test;
     import org.mockito.InOrder;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.Subscription;
     import rx.concurrency.TestScheduler;
     import rx.observables.ConnectableObservable;
     
    -import java.util.concurrent.TimeUnit;
    -
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Matchers.anyLong;
    -import static org.mockito.Mockito.*;
    -
     public class OperationIntervalTest {
     
    -  private TestScheduler scheduler;
    -  private Observer observer;
    -  private Observer observer2;
    -
    -  @Before
    -  @SuppressWarnings("unchecked") // due to mocking
    -  public void before() {
    -    scheduler = new TestScheduler();
    -    observer = mock(Observer.class);
    -    observer2 = mock(Observer.class);
    -  }
    -
    -  @Test
    -  public void testInterval() {
    -    Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler));
    -    Subscription sub = w.subscribe(observer);
    -
    -    verify(observer, never()).onNext(0L);
    -    verify(observer, never()).onCompleted();
    -    verify(observer, never()).onError(any(Throwable.class));
    -
    -    scheduler.advanceTimeTo(2, TimeUnit.SECONDS);
    -
    -    InOrder inOrder = inOrder(observer);
    -    inOrder.verify(observer, times(1)).onNext(0L);
    -    inOrder.verify(observer, times(1)).onNext(1L);
    -    inOrder.verify(observer, never()).onNext(2L);
    -    verify(observer, never()).onCompleted();
    -    verify(observer, never()).onError(any(Throwable.class));
    -
    -    sub.unsubscribe();
    -    scheduler.advanceTimeTo(4, TimeUnit.SECONDS);
    -    verify(observer, never()).onNext(2L);
    -    verify(observer, times(1)).onCompleted();
    -    verify(observer, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testWithMultipleSubscribersStartingAtSameTime() {
    -    Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler));
    -    Subscription sub1 = w.subscribe(observer);
    -    Subscription sub2 = w.subscribe(observer2);
    -
    -    verify(observer, never()).onNext(anyLong());
    -    verify(observer2, never()).onNext(anyLong());
    -
    -    scheduler.advanceTimeTo(2, TimeUnit.SECONDS);
    +    private TestScheduler scheduler;
    +    private Observer observer;
    +    private Observer observer2;
    +
    +    @Before
    +    @SuppressWarnings("unchecked")
    +    // due to mocking
    +    public void before() {
    +        scheduler = new TestScheduler();
    +        observer = mock(Observer.class);
    +        observer2 = mock(Observer.class);
    +    }
    +
    +    @Test
    +    public void testInterval() {
    +        Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler));
    +        Subscription sub = w.subscribe(observer);
    +
    +        verify(observer, never()).onNext(0L);
    +        verify(observer, never()).onCompleted();
    +        verify(observer, never()).onError(any(Throwable.class));
    +
    +        scheduler.advanceTimeTo(2, TimeUnit.SECONDS);
    +
    +        InOrder inOrder = inOrder(observer);
    +        inOrder.verify(observer, times(1)).onNext(0L);
    +        inOrder.verify(observer, times(1)).onNext(1L);
    +        inOrder.verify(observer, never()).onNext(2L);
    +        verify(observer, never()).onCompleted();
    +        verify(observer, never()).onError(any(Throwable.class));
    +
    +        sub.unsubscribe();
    +        scheduler.advanceTimeTo(4, TimeUnit.SECONDS);
    +        verify(observer, never()).onNext(2L);
    +        verify(observer, times(1)).onCompleted();
    +        verify(observer, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testWithMultipleSubscribersStartingAtSameTime() {
    +        Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler));
    +        Subscription sub1 = w.subscribe(observer);
    +        Subscription sub2 = w.subscribe(observer2);
    +
    +        verify(observer, never()).onNext(anyLong());
    +        verify(observer2, never()).onNext(anyLong());
    +
    +        scheduler.advanceTimeTo(2, TimeUnit.SECONDS);
     
    -    InOrder inOrder1 = inOrder(observer);
    -    InOrder inOrder2 = inOrder(observer2);
    -
    -    inOrder1.verify(observer, times(1)).onNext(0L);
    -    inOrder1.verify(observer, times(1)).onNext(1L);
    -    inOrder1.verify(observer, never()).onNext(2L);
    -    verify(observer, never()).onCompleted();
    -    verify(observer, never()).onError(any(Throwable.class));
    -
    -    inOrder2.verify(observer2, times(1)).onNext(0L);
    -    inOrder2.verify(observer2, times(1)).onNext(1L);
    -    inOrder2.verify(observer2, never()).onNext(2L);
    -    verify(observer2, never()).onCompleted();
    -    verify(observer2, never()).onError(any(Throwable.class));
    -
    -    sub1.unsubscribe();
    -    sub2.unsubscribe();
    -    scheduler.advanceTimeTo(4, TimeUnit.SECONDS);
    -
    -    verify(observer, never()).onNext(2L);
    -    verify(observer, times(1)).onCompleted();
    -    verify(observer, never()).onError(any(Throwable.class));
    -
    -    verify(observer2, never()).onNext(2L);
    -    verify(observer2, times(1)).onCompleted();
    -    verify(observer2, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testWithMultipleStaggeredSubscribers() {
    -    Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler));
    -    Subscription sub1 = w.subscribe(observer);
    -
    -    verify(observer, never()).onNext(anyLong());
    -
    -    scheduler.advanceTimeTo(2, TimeUnit.SECONDS);
    -    Subscription sub2 = w.subscribe(observer2);
    -
    -    InOrder inOrder1 = inOrder(observer);
    -    inOrder1.verify(observer, times(1)).onNext(0L);
    -    inOrder1.verify(observer, times(1)).onNext(1L);
    -    inOrder1.verify(observer, never()).onNext(2L);
    -
    -    verify(observer, never()).onCompleted();
    -    verify(observer, never()).onError(any(Throwable.class));
    -    verify(observer2, never()).onNext(anyLong());
    -
    -    scheduler.advanceTimeTo(4, TimeUnit.SECONDS);
    -
    -    inOrder1.verify(observer, times(1)).onNext(2L);
    -    inOrder1.verify(observer, times(1)).onNext(3L);
    -
    -    InOrder inOrder2 = inOrder(observer2);
    -    inOrder2.verify(observer2, times(1)).onNext(0L);
    -    inOrder2.verify(observer2, times(1)).onNext(1L);
    -
    -    sub1.unsubscribe();
    -    sub2.unsubscribe();
    -
    -    inOrder1.verify(observer, never()).onNext(anyLong());
    -    inOrder1.verify(observer, times(1)).onCompleted();
    -    verify(observer, never()).onError(any(Throwable.class));
    +        InOrder inOrder1 = inOrder(observer);
    +        InOrder inOrder2 = inOrder(observer2);
    +
    +        inOrder1.verify(observer, times(1)).onNext(0L);
    +        inOrder1.verify(observer, times(1)).onNext(1L);
    +        inOrder1.verify(observer, never()).onNext(2L);
    +        verify(observer, never()).onCompleted();
    +        verify(observer, never()).onError(any(Throwable.class));
    +
    +        inOrder2.verify(observer2, times(1)).onNext(0L);
    +        inOrder2.verify(observer2, times(1)).onNext(1L);
    +        inOrder2.verify(observer2, never()).onNext(2L);
    +        verify(observer2, never()).onCompleted();
    +        verify(observer2, never()).onError(any(Throwable.class));
    +
    +        sub1.unsubscribe();
    +        sub2.unsubscribe();
    +        scheduler.advanceTimeTo(4, TimeUnit.SECONDS);
    +
    +        verify(observer, never()).onNext(2L);
    +        verify(observer, times(1)).onCompleted();
    +        verify(observer, never()).onError(any(Throwable.class));
    +
    +        verify(observer2, never()).onNext(2L);
    +        verify(observer2, times(1)).onCompleted();
    +        verify(observer2, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testWithMultipleStaggeredSubscribers() {
    +        Observable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler));
    +        Subscription sub1 = w.subscribe(observer);
    +
    +        verify(observer, never()).onNext(anyLong());
    +
    +        scheduler.advanceTimeTo(2, TimeUnit.SECONDS);
    +        Subscription sub2 = w.subscribe(observer2);
    +
    +        InOrder inOrder1 = inOrder(observer);
    +        inOrder1.verify(observer, times(1)).onNext(0L);
    +        inOrder1.verify(observer, times(1)).onNext(1L);
    +        inOrder1.verify(observer, never()).onNext(2L);
    +
    +        verify(observer, never()).onCompleted();
    +        verify(observer, never()).onError(any(Throwable.class));
    +        verify(observer2, never()).onNext(anyLong());
    +
    +        scheduler.advanceTimeTo(4, TimeUnit.SECONDS);
    +
    +        inOrder1.verify(observer, times(1)).onNext(2L);
    +        inOrder1.verify(observer, times(1)).onNext(3L);
    +
    +        InOrder inOrder2 = inOrder(observer2);
    +        inOrder2.verify(observer2, times(1)).onNext(0L);
    +        inOrder2.verify(observer2, times(1)).onNext(1L);
    +
    +        sub1.unsubscribe();
    +        sub2.unsubscribe();
    +
    +        inOrder1.verify(observer, never()).onNext(anyLong());
    +        inOrder1.verify(observer, times(1)).onCompleted();
    +        verify(observer, never()).onError(any(Throwable.class));
     
    -    inOrder2.verify(observer2, never()).onNext(anyLong());
    -    inOrder2.verify(observer2, times(1)).onCompleted();
    -    verify(observer2, never()).onError(any(Throwable.class));
    -  }
    +        inOrder2.verify(observer2, never()).onNext(anyLong());
    +        inOrder2.verify(observer2, times(1)).onCompleted();
    +        verify(observer2, never()).onError(any(Throwable.class));
    +    }
     
    -  @Test
    -  public void testWithMultipleStaggeredSubscribersAndPublish() {
    -    ConnectableObservable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler)).publish();
    -    Subscription sub1 = w.subscribe(observer);
    -    w.connect();
    +    @Test
    +    public void testWithMultipleStaggeredSubscribersAndPublish() {
    +        ConnectableObservable w = Observable.create(OperationInterval.interval(1, TimeUnit.SECONDS, scheduler)).publish();
    +        Subscription sub1 = w.subscribe(observer);
    +        w.connect();
     
    -    verify(observer, never()).onNext(anyLong());
    -
    -    scheduler.advanceTimeTo(2, TimeUnit.SECONDS);
    -    Subscription sub2 = w.subscribe(observer2);
    +        verify(observer, never()).onNext(anyLong());
    +
    +        scheduler.advanceTimeTo(2, TimeUnit.SECONDS);
    +        Subscription sub2 = w.subscribe(observer2);
     
    -    InOrder inOrder1 = inOrder(observer);
    -    inOrder1.verify(observer, times(1)).onNext(0L);
    -    inOrder1.verify(observer, times(1)).onNext(1L);
    -    inOrder1.verify(observer, never()).onNext(2L);
    +        InOrder inOrder1 = inOrder(observer);
    +        inOrder1.verify(observer, times(1)).onNext(0L);
    +        inOrder1.verify(observer, times(1)).onNext(1L);
    +        inOrder1.verify(observer, never()).onNext(2L);
     
    -    verify(observer, never()).onCompleted();
    -    verify(observer, never()).onError(any(Throwable.class));
    -    verify(observer2, never()).onNext(anyLong());
    +        verify(observer, never()).onCompleted();
    +        verify(observer, never()).onError(any(Throwable.class));
    +        verify(observer2, never()).onNext(anyLong());
     
    -    scheduler.advanceTimeTo(4, TimeUnit.SECONDS);
    +        scheduler.advanceTimeTo(4, TimeUnit.SECONDS);
     
    -    inOrder1.verify(observer, times(1)).onNext(2L);
    -    inOrder1.verify(observer, times(1)).onNext(3L);
    +        inOrder1.verify(observer, times(1)).onNext(2L);
    +        inOrder1.verify(observer, times(1)).onNext(3L);
     
    -    InOrder inOrder2 = inOrder(observer2);
    -    inOrder2.verify(observer2, times(1)).onNext(2L);
    -    inOrder2.verify(observer2, times(1)).onNext(3L);
    +        InOrder inOrder2 = inOrder(observer2);
    +        inOrder2.verify(observer2, times(1)).onNext(2L);
    +        inOrder2.verify(observer2, times(1)).onNext(3L);
     
    -    sub1.unsubscribe();
    -    sub2.unsubscribe();
    +        sub1.unsubscribe();
    +        sub2.unsubscribe();
     
    -    inOrder1.verify(observer, never()).onNext(anyLong());
    -    inOrder1.verify(observer, never()).onCompleted();
    -    verify(observer, never()).onError(any(Throwable.class));
    +        inOrder1.verify(observer, never()).onNext(anyLong());
    +        inOrder1.verify(observer, never()).onCompleted();
    +        verify(observer, never()).onError(any(Throwable.class));
     
    -    inOrder2.verify(observer2, never()).onNext(anyLong());
    -    inOrder2.verify(observer2, never()).onCompleted();
    -    verify(observer2, never()).onError(any(Throwable.class));
    -  }
    +        inOrder2.verify(observer2, never()).onNext(anyLong());
    +        inOrder2.verify(observer2, never()).onCompleted();
    +        verify(observer2, never()).onError(any(Throwable.class));
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java
    index cfd081112e..279a40eb7f 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java
    @@ -1,276 +1,277 @@
     package rx.operators;
     
    +import static org.junit.Assert.*;
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationMap.*;
    +
    +import java.util.HashMap;
    +import java.util.Map;
    +import java.util.concurrent.CountDownLatch;
    +import java.util.concurrent.atomic.AtomicInteger;
    +
     import org.junit.Before;
     import org.junit.Test;
     import org.mockito.InOrder;
     import org.mockito.Mock;
     import org.mockito.MockitoAnnotations;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.concurrency.Schedulers;
     import rx.util.functions.Func1;
     import rx.util.functions.Func2;
     
    -import java.util.HashMap;
    -import java.util.Map;
    -import java.util.concurrent.CountDownLatch;
    -import java.util.concurrent.atomic.AtomicInteger;
    +public class OperationMapTest {
     
    -import static org.junit.Assert.assertEquals;
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationMap.*;
    +    @Mock
    +    Observer stringObserver;
    +    @Mock
    +    Observer stringObserver2;
     
    -public class OperationMapTest {
    +    final static Func2 APPEND_INDEX = new Func2() {
    +        @Override
    +        public String call(String value, Integer index) {
    +            return value + index;
    +        }
    +    };
    +
    +    @Before
    +    public void before() {
    +        MockitoAnnotations.initMocks(this);
    +    }
    +
    +    @Test
    +    public void testMap() {
    +        Map m1 = getMap("One");
    +        Map m2 = getMap("Two");
    +        Observable> observable = Observable.from(m1, m2);
     
    -  @Mock
    -  Observer stringObserver;
    -  @Mock
    -  Observer stringObserver2;
    +        Observable m = Observable.create(map(observable, new Func1, String>() {
     
    -  final static Func2 APPEND_INDEX = new Func2() {
    -    @Override
    -    public String call(String value, Integer index) {
    -      return value + index;
    +            @Override
    +            public String call(Map map) {
    +                return map.get("firstName");
    +            }
    +
    +        }));
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(1)).onNext("OneFirst");
    +        verify(stringObserver, times(1)).onNext("TwoFirst");
    +        verify(stringObserver, times(1)).onCompleted();
         }
    -  };
    -
    -  @Before
    -  public void before() {
    -    MockitoAnnotations.initMocks(this);
    -  }
    -
    -  @Test
    -  public void testMap() {
    -    Map m1 = getMap("One");
    -    Map m2 = getMap("Two");
    -    Observable> observable = Observable.from(m1, m2);
    -
    -    Observable m = Observable.create(map(observable, new Func1, String>() {
    -
    -      @Override
    -      public String call(Map map) {
    -        return map.get("firstName");
    -      }
    -
    -    }));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(1)).onNext("OneFirst");
    -    verify(stringObserver, times(1)).onNext("TwoFirst");
    -    verify(stringObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testMapWithIndex() {
    -    Observable w = Observable.from("a", "b", "c");
    -    Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX));
    -    m.subscribe(stringObserver);
    -    InOrder inOrder = inOrder(stringObserver);
    -    inOrder.verify(stringObserver, times(1)).onNext("a0");
    -    inOrder.verify(stringObserver, times(1)).onNext("b1");
    -    inOrder.verify(stringObserver, times(1)).onNext("c2");
    -    inOrder.verify(stringObserver, times(1)).onCompleted();
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testMapWithIndexAndMultipleSubscribers() {
    -    Observable w = Observable.from("a", "b", "c");
    -    Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX));
    -    m.subscribe(stringObserver);
    -    m.subscribe(stringObserver2);
    -    InOrder inOrder = inOrder(stringObserver);
    -    inOrder.verify(stringObserver, times(1)).onNext("a0");
    -    inOrder.verify(stringObserver, times(1)).onNext("b1");
    -    inOrder.verify(stringObserver, times(1)).onNext("c2");
    -    inOrder.verify(stringObserver, times(1)).onCompleted();
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -
    -    InOrder inOrder2 = inOrder(stringObserver2);
    -    inOrder2.verify(stringObserver2, times(1)).onNext("a0");
    -    inOrder2.verify(stringObserver2, times(1)).onNext("b1");
    -    inOrder2.verify(stringObserver2, times(1)).onNext("c2");
    -    inOrder2.verify(stringObserver2, times(1)).onCompleted();
    -    verify(stringObserver2, never()).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testMapMany() {
    -            /* simulate a top-level async call which returns IDs */
    -    Observable ids = Observable.from(1, 2);
    -
    -            /* now simulate the behavior to take those IDs and perform nested async calls based on them */
    -    Observable m = Observable.create(mapMany(ids, new Func1>() {
    -
    -      @Override
    -      public Observable call(Integer id) {
    -                    /* simulate making a nested async call which creates another Observable */
    -        Observable> subObservable = null;
    -        if (id == 1) {
    -          Map m1 = getMap("One");
    -          Map m2 = getMap("Two");
    -          subObservable = Observable.from(m1, m2);
    -        } else {
    -          Map m3 = getMap("Three");
    -          Map m4 = getMap("Four");
    -          subObservable = Observable.from(m3, m4);
    -        }
     
    -                    /* simulate kicking off the async call and performing a select on it to transform the data */
    -        return Observable.create(map(subObservable, new Func1, String>() {
    -          @Override
    -          public String call(Map map) {
    -            return map.get("firstName");
    -          }
    +    @Test
    +    public void testMapWithIndex() {
    +        Observable w = Observable.from("a", "b", "c");
    +        Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX));
    +        m.subscribe(stringObserver);
    +        InOrder inOrder = inOrder(stringObserver);
    +        inOrder.verify(stringObserver, times(1)).onNext("a0");
    +        inOrder.verify(stringObserver, times(1)).onNext("b1");
    +        inOrder.verify(stringObserver, times(1)).onNext("c2");
    +        inOrder.verify(stringObserver, times(1)).onCompleted();
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testMapWithIndexAndMultipleSubscribers() {
    +        Observable w = Observable.from("a", "b", "c");
    +        Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX));
    +        m.subscribe(stringObserver);
    +        m.subscribe(stringObserver2);
    +        InOrder inOrder = inOrder(stringObserver);
    +        inOrder.verify(stringObserver, times(1)).onNext("a0");
    +        inOrder.verify(stringObserver, times(1)).onNext("b1");
    +        inOrder.verify(stringObserver, times(1)).onNext("c2");
    +        inOrder.verify(stringObserver, times(1)).onCompleted();
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +
    +        InOrder inOrder2 = inOrder(stringObserver2);
    +        inOrder2.verify(stringObserver2, times(1)).onNext("a0");
    +        inOrder2.verify(stringObserver2, times(1)).onNext("b1");
    +        inOrder2.verify(stringObserver2, times(1)).onNext("c2");
    +        inOrder2.verify(stringObserver2, times(1)).onCompleted();
    +        verify(stringObserver2, never()).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testMapMany() {
    +        /* simulate a top-level async call which returns IDs */
    +        Observable ids = Observable.from(1, 2);
    +
    +        /* now simulate the behavior to take those IDs and perform nested async calls based on them */
    +        Observable m = Observable.create(mapMany(ids, new Func1>() {
    +
    +            @Override
    +            public Observable call(Integer id) {
    +                /* simulate making a nested async call which creates another Observable */
    +                Observable> subObservable = null;
    +                if (id == 1) {
    +                    Map m1 = getMap("One");
    +                    Map m2 = getMap("Two");
    +                    subObservable = Observable.from(m1, m2);
    +                } else {
    +                    Map m3 = getMap("Three");
    +                    Map m4 = getMap("Four");
    +                    subObservable = Observable.from(m3, m4);
    +                }
    +
    +                /* simulate kicking off the async call and performing a select on it to transform the data */
    +                return Observable.create(map(subObservable, new Func1, String>() {
    +                    @Override
    +                    public String call(Map map) {
    +                        return map.get("firstName");
    +                    }
    +                }));
    +            }
    +
             }));
    -      }
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(1)).onNext("OneFirst");
    +        verify(stringObserver, times(1)).onNext("TwoFirst");
    +        verify(stringObserver, times(1)).onNext("ThreeFirst");
    +        verify(stringObserver, times(1)).onNext("FourFirst");
    +        verify(stringObserver, times(1)).onCompleted();
    +    }
     
    -    }));
    -    m.subscribe(stringObserver);
    +    @Test
    +    public void testMapMany2() {
    +        Map m1 = getMap("One");
    +        Map m2 = getMap("Two");
    +        Observable> observable1 = Observable.from(m1, m2);
     
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(1)).onNext("OneFirst");
    -    verify(stringObserver, times(1)).onNext("TwoFirst");
    -    verify(stringObserver, times(1)).onNext("ThreeFirst");
    -    verify(stringObserver, times(1)).onNext("FourFirst");
    -    verify(stringObserver, times(1)).onCompleted();
    -  }
    +        Map m3 = getMap("Three");
    +        Map m4 = getMap("Four");
    +        Observable> observable2 = Observable.from(m3, m4);
     
    -  @Test
    -  public void testMapMany2() {
    -    Map m1 = getMap("One");
    -    Map m2 = getMap("Two");
    -    Observable> observable1 = Observable.from(m1, m2);
    +        Observable>> observable = Observable.from(observable1, observable2);
     
    -    Map m3 = getMap("Three");
    -    Map m4 = getMap("Four");
    -    Observable> observable2 = Observable.from(m3, m4);
    +        Observable m = Observable.create(mapMany(observable, new Func1>, Observable>() {
     
    -    Observable>> observable = Observable.from(observable1, observable2);
    +            @Override
    +            public Observable call(Observable> o) {
    +                return Observable.create(map(o, new Func1, String>() {
     
    -    Observable m = Observable.create(mapMany(observable, new Func1>, Observable>() {
    +                    @Override
    +                    public String call(Map map) {
    +                        return map.get("firstName");
    +                    }
    +                }));
    +            }
     
    -      @Override
    -      public Observable call(Observable> o) {
    -        return Observable.create(map(o, new Func1, String>() {
    +        }));
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(1)).onNext("OneFirst");
    +        verify(stringObserver, times(1)).onNext("TwoFirst");
    +        verify(stringObserver, times(1)).onNext("ThreeFirst");
    +        verify(stringObserver, times(1)).onNext("FourFirst");
    +        verify(stringObserver, times(1)).onCompleted();
    +
    +    }
     
    -          @Override
    -          public String call(Map map) {
    -            return map.get("firstName");
    -          }
    +    @Test
    +    public void testMapWithError() {
    +        Observable w = Observable.from("one", "fail", "two", "three", "fail");
    +        Observable m = Observable.create(map(w, new Func1() {
    +            @Override
    +            public String call(String s) {
    +                if ("fail".equals(s)) {
    +                    throw new RuntimeException("Forced Failure");
    +                }
    +                return s;
    +            }
             }));
    -      }
    -
    -    }));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(1)).onNext("OneFirst");
    -    verify(stringObserver, times(1)).onNext("TwoFirst");
    -    verify(stringObserver, times(1)).onNext("ThreeFirst");
    -    verify(stringObserver, times(1)).onNext("FourFirst");
    -    verify(stringObserver, times(1)).onCompleted();
    -
    -  }
    -
    -  @Test
    -  public void testMapWithError() {
    -    Observable w = Observable.from("one", "fail", "two", "three", "fail");
    -    Observable m = Observable.create(map(w, new Func1() {
    -      @Override
    -      public String call(String s) {
    -        if ("fail".equals(s)) {
    -          throw new RuntimeException("Forced Failure");
    -        }
    -        return s;
    -      }
    -    }));
    -
    -    m.subscribe(stringObserver);
    -    verify(stringObserver, times(1)).onNext("one");
    -    verify(stringObserver, never()).onNext("two");
    -    verify(stringObserver, never()).onNext("three");
    -    verify(stringObserver, never()).onCompleted();
    -    verify(stringObserver, times(1)).onError(any(Throwable.class));
    -  }
    -
    -  @Test
    -  public void testMapWithSynchronousObservableContainingError() {
    -    Observable w = Observable.from("one", "fail", "two", "three", "fail");
    -    final AtomicInteger c1 = new AtomicInteger();
    -    final AtomicInteger c2 = new AtomicInteger();
    -    Observable m = Observable.create(map(w, new Func1() {
    -      @Override
    -      public String call(String s) {
    -        if ("fail".equals(s))
    -          throw new RuntimeException("Forced Failure");
    -        System.out.println("BadMapper:" + s);
    -        c1.incrementAndGet();
    -        return s;
    -      }
    -    })).map(new Func1() {
    -      @Override
    -      public String call(String s) {
    -        System.out.println("SecondMapper:" + s);
    -        c2.incrementAndGet();
    -        return s;
    -      }
    -    });
    -
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, times(1)).onNext("one");
    -    verify(stringObserver, never()).onNext("two");
    -    verify(stringObserver, never()).onNext("three");
    -    verify(stringObserver, never()).onCompleted();
    -    verify(stringObserver, times(1)).onError(any(Throwable.class));
    -
    -    // we should have only returned 1 value: "one"
    -    assertEquals(1, c1.get());
    -    assertEquals(1, c2.get());
    -  }
    -
    -  @Test(expected = IllegalArgumentException.class)
    -  public void testMapWithIssue417() {
    -    Observable.from(1).observeOn(Schedulers.threadPoolForComputation())
    -        .map(new Func1() {
    -          public Integer call(Integer arg0) {
    -            throw new IllegalArgumentException("any error");
    -          }
    -        }).toBlockingObservable().single();
    -  }
    -
    -  @Test
    -  public void testMapWithErrorInFuncAndThreadPoolScheduler() throws InterruptedException {
    -    // The error will throw in one of threads in the thread pool.
    -    // If map does not handle it, the error will disappear.
    -    // so map needs to handle the error by itself.
    -    final CountDownLatch latch = new CountDownLatch(1);
    -    Observable m = Observable.from("one")
    -        .observeOn(Schedulers.threadPoolForComputation())
    -        .map(new Func1() {
    -          public String call(String arg0) {
    -            try {
    -              throw new IllegalArgumentException("any error");
    -            } finally {
    -              latch.countDown();
    +
    +        m.subscribe(stringObserver);
    +        verify(stringObserver, times(1)).onNext("one");
    +        verify(stringObserver, never()).onNext("two");
    +        verify(stringObserver, never()).onNext("three");
    +        verify(stringObserver, never()).onCompleted();
    +        verify(stringObserver, times(1)).onError(any(Throwable.class));
    +    }
    +
    +    @Test
    +    public void testMapWithSynchronousObservableContainingError() {
    +        Observable w = Observable.from("one", "fail", "two", "three", "fail");
    +        final AtomicInteger c1 = new AtomicInteger();
    +        final AtomicInteger c2 = new AtomicInteger();
    +        Observable m = Observable.create(map(w, new Func1() {
    +            @Override
    +            public String call(String s) {
    +                if ("fail".equals(s))
    +                    throw new RuntimeException("Forced Failure");
    +                System.out.println("BadMapper:" + s);
    +                c1.incrementAndGet();
    +                return s;
    +            }
    +        })).map(new Func1() {
    +            @Override
    +            public String call(String s) {
    +                System.out.println("SecondMapper:" + s);
    +                c2.incrementAndGet();
    +                return s;
                 }
    -          }
             });
     
    -    m.subscribe(stringObserver);
    -    latch.await();
    -    InOrder inorder = inOrder(stringObserver);
    -    inorder.verify(stringObserver, times(1)).onError(any(IllegalArgumentException.class));
    -    inorder.verifyNoMoreInteractions();
    -  }
    -
    -  private static Map getMap(String prefix) {
    -    Map m = new HashMap();
    -    m.put("firstName", prefix + "First");
    -    m.put("lastName", prefix + "Last");
    -    return m;
    -  }
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, times(1)).onNext("one");
    +        verify(stringObserver, never()).onNext("two");
    +        verify(stringObserver, never()).onNext("three");
    +        verify(stringObserver, never()).onCompleted();
    +        verify(stringObserver, times(1)).onError(any(Throwable.class));
    +
    +        // we should have only returned 1 value: "one"
    +        assertEquals(1, c1.get());
    +        assertEquals(1, c2.get());
    +    }
    +
    +    @Test(expected = IllegalArgumentException.class)
    +    public void testMapWithIssue417() {
    +        Observable.from(1).observeOn(Schedulers.threadPoolForComputation())
    +                .map(new Func1() {
    +                    public Integer call(Integer arg0) {
    +                        throw new IllegalArgumentException("any error");
    +                    }
    +                }).toBlockingObservable().single();
    +    }
    +
    +    @Test
    +    public void testMapWithErrorInFuncAndThreadPoolScheduler() throws InterruptedException {
    +        // The error will throw in one of threads in the thread pool.
    +        // If map does not handle it, the error will disappear.
    +        // so map needs to handle the error by itself.
    +        final CountDownLatch latch = new CountDownLatch(1);
    +        Observable m = Observable.from("one")
    +                .observeOn(Schedulers.threadPoolForComputation())
    +                .map(new Func1() {
    +                    public String call(String arg0) {
    +                        try {
    +                            throw new IllegalArgumentException("any error");
    +                        } finally {
    +                            latch.countDown();
    +                        }
    +                    }
    +                });
    +
    +        m.subscribe(stringObserver);
    +        latch.await();
    +        InOrder inorder = inOrder(stringObserver);
    +        inorder.verify(stringObserver, times(1)).onError(any(IllegalArgumentException.class));
    +        inorder.verifyNoMoreInteractions();
    +    }
    +
    +    private static Map getMap(String prefix) {
    +        Map m = new HashMap();
    +        m.put("firstName", prefix + "First");
    +        m.put("lastName", prefix + "Last");
    +        return m;
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java b/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java
    index f7f4142672..0795ec03ed 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java
    @@ -1,150 +1,151 @@
     package rx.operators;
     
    -import org.junit.Test;
    -import rx.Notification;
    -import rx.Observable;
    -import rx.Observer;
    -import rx.Subscription;
    +import static org.junit.Assert.*;
    +import static rx.operators.OperationMaterialize.*;
     
     import java.util.List;
     import java.util.Vector;
     import java.util.concurrent.ExecutionException;
     
    -import static org.junit.Assert.*;
    -import static rx.operators.OperationMaterialize.materialize;
    +import org.junit.Test;
    +
    +import rx.Notification;
    +import rx.Observable;
    +import rx.Observer;
    +import rx.Subscription;
     
     public class OperationMaterializeTest {
     
    -  @Test
    -  public void testMaterialize1() {
    -    // null will cause onError to be triggered before "three" can be returned
    -    final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", null, "three");
    +    @Test
    +    public void testMaterialize1() {
    +        // null will cause onError to be triggered before "three" can be returned
    +        final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", null, "three");
     
    -    TestObserver Observer = new TestObserver();
    -    Observable> m = Observable.create(materialize(Observable.create(o1)));
    -    m.subscribe(Observer);
    +        TestObserver Observer = new TestObserver();
    +        Observable> m = Observable.create(materialize(Observable.create(o1)));
    +        m.subscribe(Observer);
     
    -    try {
    -      o1.t.join();
    -    } catch (InterruptedException e) {
    -      throw new RuntimeException(e);
    -    }
    +        try {
    +            o1.t.join();
    +        } catch (InterruptedException e) {
    +            throw new RuntimeException(e);
    +        }
     
    -    assertFalse(Observer.onError);
    -    assertTrue(Observer.onCompleted);
    -    assertEquals(3, Observer.notifications.size());
    -    assertEquals("one", Observer.notifications.get(0).getValue());
    -    assertTrue(Observer.notifications.get(0).isOnNext());
    -    assertEquals("two", Observer.notifications.get(1).getValue());
    -    assertTrue(Observer.notifications.get(1).isOnNext());
    -    assertEquals(NullPointerException.class, Observer.notifications.get(2).getThrowable().getClass());
    -    assertTrue(Observer.notifications.get(2).isOnError());
    -  }
    -
    -  @Test
    -  public void testMaterialize2() {
    -    final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", "three");
    -
    -    TestObserver Observer = new TestObserver();
    -    Observable> m = Observable.create(materialize(Observable.create(o1)));
    -    m.subscribe(Observer);
    -
    -    try {
    -      o1.t.join();
    -    } catch (InterruptedException e) {
    -      throw new RuntimeException(e);
    +        assertFalse(Observer.onError);
    +        assertTrue(Observer.onCompleted);
    +        assertEquals(3, Observer.notifications.size());
    +        assertEquals("one", Observer.notifications.get(0).getValue());
    +        assertTrue(Observer.notifications.get(0).isOnNext());
    +        assertEquals("two", Observer.notifications.get(1).getValue());
    +        assertTrue(Observer.notifications.get(1).isOnNext());
    +        assertEquals(NullPointerException.class, Observer.notifications.get(2).getThrowable().getClass());
    +        assertTrue(Observer.notifications.get(2).isOnError());
         }
     
    -    assertFalse(Observer.onError);
    -    assertTrue(Observer.onCompleted);
    -    assertEquals(4, Observer.notifications.size());
    -    assertEquals("one", Observer.notifications.get(0).getValue());
    -    assertTrue(Observer.notifications.get(0).isOnNext());
    -    assertEquals("two", Observer.notifications.get(1).getValue());
    -    assertTrue(Observer.notifications.get(1).isOnNext());
    -    assertEquals("three", Observer.notifications.get(2).getValue());
    -    assertTrue(Observer.notifications.get(2).isOnNext());
    -    assertTrue(Observer.notifications.get(3).isOnCompleted());
    -  }
    -
    -  @Test
    -  public void testMultipleSubscribes() throws InterruptedException, ExecutionException {
    -    final TestAsyncErrorObservable o = new TestAsyncErrorObservable("one", "two", null, "three");
    -
    -    Observable> m = Observable.create(materialize(Observable.create(o)));
    -
    -    assertEquals(3, m.toList().toBlockingObservable().toFuture().get().size());
    -    assertEquals(3, m.toList().toBlockingObservable().toFuture().get().size());
    -  }
    -
    -  private static class TestObserver implements Observer> {
    -
    -    boolean onCompleted = false;
    -    boolean onError = false;
    -    List> notifications = new Vector>();
    -
    -    @Override
    -    public void onCompleted() {
    -      this.onCompleted = true;
    -    }
    +    @Test
    +    public void testMaterialize2() {
    +        final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", "three");
     
    -    @Override
    -    public void onError(Throwable e) {
    -      this.onError = true;
    -    }
    +        TestObserver Observer = new TestObserver();
    +        Observable> m = Observable.create(materialize(Observable.create(o1)));
    +        m.subscribe(Observer);
     
    -    @Override
    -    public void onNext(Notification value) {
    -      this.notifications.add(value);
    -    }
    +        try {
    +            o1.t.join();
    +        } catch (InterruptedException e) {
    +            throw new RuntimeException(e);
    +        }
     
    -  }
    +        assertFalse(Observer.onError);
    +        assertTrue(Observer.onCompleted);
    +        assertEquals(4, Observer.notifications.size());
    +        assertEquals("one", Observer.notifications.get(0).getValue());
    +        assertTrue(Observer.notifications.get(0).isOnNext());
    +        assertEquals("two", Observer.notifications.get(1).getValue());
    +        assertTrue(Observer.notifications.get(1).isOnNext());
    +        assertEquals("three", Observer.notifications.get(2).getValue());
    +        assertTrue(Observer.notifications.get(2).isOnNext());
    +        assertTrue(Observer.notifications.get(3).isOnCompleted());
    +    }
     
    -  private static class TestAsyncErrorObservable implements Observable.OnSubscribeFunc {
    +    @Test
    +    public void testMultipleSubscribes() throws InterruptedException, ExecutionException {
    +        final TestAsyncErrorObservable o = new TestAsyncErrorObservable("one", "two", null, "three");
     
    -    String[] valuesToReturn;
    +        Observable> m = Observable.create(materialize(Observable.create(o)));
     
    -    TestAsyncErrorObservable(String... values) {
    -      valuesToReturn = values;
    +        assertEquals(3, m.toList().toBlockingObservable().toFuture().get().size());
    +        assertEquals(3, m.toList().toBlockingObservable().toFuture().get().size());
         }
     
    -    volatile Thread t;
    +    private static class TestObserver implements Observer> {
     
    -    @Override
    -    public Subscription onSubscribe(final Observer observer) {
    -      t = new Thread(new Runnable() {
    +        boolean onCompleted = false;
    +        boolean onError = false;
    +        List> notifications = new Vector>();
     
             @Override
    -        public void run() {
    -          for (String s : valuesToReturn) {
    -            if (s == null) {
    -              System.out.println("throwing exception");
    -              try {
    -                Thread.sleep(100);
    -              } catch (Throwable e) {
    -
    -              }
    -              observer.onError(new NullPointerException());
    -              return;
    -            } else {
    -              observer.onNext(s);
    -            }
    -          }
    -          System.out.println("subscription complete");
    -          observer.onCompleted();
    +        public void onCompleted() {
    +            this.onCompleted = true;
             }
     
    -      });
    -      t.start();
    -
    -      return new Subscription() {
    +        @Override
    +        public void onError(Throwable e) {
    +            this.onError = true;
    +        }
     
             @Override
    -        public void unsubscribe() {
    +        public void onNext(Notification value) {
    +            this.notifications.add(value);
    +        }
     
    +    }
    +
    +    private static class TestAsyncErrorObservable implements Observable.OnSubscribeFunc {
    +
    +        String[] valuesToReturn;
    +
    +        TestAsyncErrorObservable(String... values) {
    +            valuesToReturn = values;
             }
     
    -      };
    +        volatile Thread t;
    +
    +        @Override
    +        public Subscription onSubscribe(final Observer observer) {
    +            t = new Thread(new Runnable() {
    +
    +                @Override
    +                public void run() {
    +                    for (String s : valuesToReturn) {
    +                        if (s == null) {
    +                            System.out.println("throwing exception");
    +                            try {
    +                                Thread.sleep(100);
    +                            } catch (Throwable e) {
    +
    +                            }
    +                            observer.onError(new NullPointerException());
    +                            return;
    +                        } else {
    +                            observer.onNext(s);
    +                        }
    +                    }
    +                    System.out.println("subscription complete");
    +                    observer.onCompleted();
    +                }
    +
    +            });
    +            t.start();
    +
    +            return new Subscription() {
    +
    +                @Override
    +                public void unsubscribe() {
    +
    +                }
    +
    +            };
    +        }
         }
    -  }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java b/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java
    index 03dbcd7044..50ebda0bb3 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java
    @@ -1,500 +1,500 @@
     package rx.operators;
     
    +import static org.junit.Assert.*;
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationMergeDelayError.*;
    +
    +import java.util.ArrayList;
    +import java.util.List;
    +
     import org.junit.Before;
     import org.junit.Test;
     import org.mockito.Mock;
     import org.mockito.MockitoAnnotations;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.Subscription;
     import rx.util.CompositeException;
     
    -import java.util.ArrayList;
    -import java.util.List;
    +public class OperationMergeDelayErrorTest {
     
    -import static org.junit.Assert.*;
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationMergeDelayError.mergeDelayError;
    +    @Mock
    +    Observer stringObserver;
     
    -public class OperationMergeDelayErrorTest {
    +    @Before
    +    public void before() {
    +        MockitoAnnotations.initMocks(this);
    +    }
     
    +    @Test
    +    public void testErrorDelayed1() {
    +        final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called
    +        final Observable o2 = Observable.create(new TestErrorObservable("one", "two", "three"));
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(mergeDelayError(o1, o2));
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    +        verify(stringObserver, never()).onCompleted();
    +        verify(stringObserver, times(1)).onNext("one");
    +        verify(stringObserver, times(1)).onNext("two");
    +        verify(stringObserver, times(1)).onNext("three");
    +        verify(stringObserver, times(1)).onNext("four");
    +        verify(stringObserver, times(0)).onNext("five");
    +        verify(stringObserver, times(0)).onNext("six");
    +    }
     
    -  @Mock
    -  Observer stringObserver;
    -
    -  @Before
    -  public void before() {
    -    MockitoAnnotations.initMocks(this);
    -  }
    -
    -  @Test
    -  public void testErrorDelayed1() {
    -    final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called
    -    final Observable o2 = Observable.create(new TestErrorObservable("one", "two", "three"));
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(mergeDelayError(o1, o2));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    -    verify(stringObserver, never()).onCompleted();
    -    verify(stringObserver, times(1)).onNext("one");
    -    verify(stringObserver, times(1)).onNext("two");
    -    verify(stringObserver, times(1)).onNext("three");
    -    verify(stringObserver, times(1)).onNext("four");
    -    verify(stringObserver, times(0)).onNext("five");
    -    verify(stringObserver, times(0)).onNext("six");
    -  }
    -
    -  @Test
    -  public void testErrorDelayed2() {
    -    final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three"));
    -    final Observable o2 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called
    -    final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null));
    -    final Observable o4 = Observable.create(new TestErrorObservable("nine"));
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    -    verify(stringObserver, never()).onCompleted();
    -    verify(stringObserver, times(1)).onNext("one");
    -    verify(stringObserver, times(1)).onNext("two");
    -    verify(stringObserver, times(1)).onNext("three");
    -    verify(stringObserver, times(1)).onNext("four");
    -    verify(stringObserver, times(0)).onNext("five");
    -    verify(stringObserver, times(0)).onNext("six");
    -    verify(stringObserver, times(1)).onNext("seven");
    -    verify(stringObserver, times(1)).onNext("eight");
    -    verify(stringObserver, times(1)).onNext("nine");
    -  }
    -
    -  @Test
    -  public void testErrorDelayed3() {
    -    final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three"));
    -    final Observable o2 = Observable.create(new TestErrorObservable("four", "five", "six"));
    -    final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null));
    -    final Observable o4 = Observable.create(new TestErrorObservable("nine"));
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    -    verify(stringObserver, never()).onCompleted();
    -    verify(stringObserver, times(1)).onNext("one");
    -    verify(stringObserver, times(1)).onNext("two");
    -    verify(stringObserver, times(1)).onNext("three");
    -    verify(stringObserver, times(1)).onNext("four");
    -    verify(stringObserver, times(1)).onNext("five");
    -    verify(stringObserver, times(1)).onNext("six");
    -    verify(stringObserver, times(1)).onNext("seven");
    -    verify(stringObserver, times(1)).onNext("eight");
    -    verify(stringObserver, times(1)).onNext("nine");
    -  }
    -
    -  @Test
    -  public void testErrorDelayed4() {
    -    final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three"));
    -    final Observable o2 = Observable.create(new TestErrorObservable("four", "five", "six"));
    -    final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight"));
    -    final Observable o4 = Observable.create(new TestErrorObservable("nine", null));
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    -    verify(stringObserver, never()).onCompleted();
    -    verify(stringObserver, times(1)).onNext("one");
    -    verify(stringObserver, times(1)).onNext("two");
    -    verify(stringObserver, times(1)).onNext("three");
    -    verify(stringObserver, times(1)).onNext("four");
    -    verify(stringObserver, times(1)).onNext("five");
    -    verify(stringObserver, times(1)).onNext("six");
    -    verify(stringObserver, times(1)).onNext("seven");
    -    verify(stringObserver, times(1)).onNext("eight");
    -    verify(stringObserver, times(1)).onNext("nine");
    -  }
    -
    -  @Test
    -  public void testErrorDelayed4WithThreading() {
    -    final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", "three");
    -    final TestAsyncErrorObservable o2 = new TestAsyncErrorObservable("four", "five", "six");
    -    final TestAsyncErrorObservable o3 = new TestAsyncErrorObservable("seven", "eight");
    -    // throw the error at the very end so no onComplete will be called after it
    -    final TestAsyncErrorObservable o4 = new TestAsyncErrorObservable("nine", null);
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(mergeDelayError(Observable.create(o1), Observable.create(o2), Observable.create(o3), Observable.create(o4)));
    -    m.subscribe(stringObserver);
    -
    -    try {
    -      o1.t.join();
    -      o2.t.join();
    -      o3.t.join();
    -      o4.t.join();
    -    } catch (InterruptedException e) {
    -      throw new RuntimeException(e);
    +    @Test
    +    public void testErrorDelayed2() {
    +        final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three"));
    +        final Observable o2 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called
    +        final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null));
    +        final Observable o4 = Observable.create(new TestErrorObservable("nine"));
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4));
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    +        verify(stringObserver, never()).onCompleted();
    +        verify(stringObserver, times(1)).onNext("one");
    +        verify(stringObserver, times(1)).onNext("two");
    +        verify(stringObserver, times(1)).onNext("three");
    +        verify(stringObserver, times(1)).onNext("four");
    +        verify(stringObserver, times(0)).onNext("five");
    +        verify(stringObserver, times(0)).onNext("six");
    +        verify(stringObserver, times(1)).onNext("seven");
    +        verify(stringObserver, times(1)).onNext("eight");
    +        verify(stringObserver, times(1)).onNext("nine");
         }
     
    -    verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    -    verify(stringObserver, never()).onCompleted();
    -    verify(stringObserver, times(1)).onNext("one");
    -    verify(stringObserver, times(1)).onNext("two");
    -    verify(stringObserver, times(1)).onNext("three");
    -    verify(stringObserver, times(1)).onNext("four");
    -    verify(stringObserver, times(1)).onNext("five");
    -    verify(stringObserver, times(1)).onNext("six");
    -    verify(stringObserver, times(1)).onNext("seven");
    -    verify(stringObserver, times(1)).onNext("eight");
    -    verify(stringObserver, times(1)).onNext("nine");
    -  }
    -
    -  @Test
    -  public void testCompositeErrorDelayed1() {
    -    final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called
    -    final Observable o2 = Observable.create(new TestErrorObservable("one", "two", null));
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(mergeDelayError(o1, o2));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, times(1)).onError(any(CompositeException.class));
    -    verify(stringObserver, never()).onCompleted();
    -    verify(stringObserver, times(1)).onNext("one");
    -    verify(stringObserver, times(1)).onNext("two");
    -    verify(stringObserver, times(0)).onNext("three");
    -    verify(stringObserver, times(1)).onNext("four");
    -    verify(stringObserver, times(0)).onNext("five");
    -    verify(stringObserver, times(0)).onNext("six");
    -  }
    -
    -  @Test
    -  public void testCompositeErrorDelayed2() {
    -    final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called
    -    final Observable o2 = Observable.create(new TestErrorObservable("one", "two", null));
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(mergeDelayError(o1, o2));
    -    CaptureObserver w = new CaptureObserver();
    -    m.subscribe(w);
    -
    -    assertNotNull(w.e);
    -    if (w.e instanceof CompositeException) {
    -      assertEquals(2, ((CompositeException) w.e).getExceptions().size());
    -      w.e.printStackTrace();
    -    } else {
    -      fail("Expecting CompositeException");
    +    @Test
    +    public void testErrorDelayed3() {
    +        final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three"));
    +        final Observable o2 = Observable.create(new TestErrorObservable("four", "five", "six"));
    +        final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null));
    +        final Observable o4 = Observable.create(new TestErrorObservable("nine"));
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4));
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    +        verify(stringObserver, never()).onCompleted();
    +        verify(stringObserver, times(1)).onNext("one");
    +        verify(stringObserver, times(1)).onNext("two");
    +        verify(stringObserver, times(1)).onNext("three");
    +        verify(stringObserver, times(1)).onNext("four");
    +        verify(stringObserver, times(1)).onNext("five");
    +        verify(stringObserver, times(1)).onNext("six");
    +        verify(stringObserver, times(1)).onNext("seven");
    +        verify(stringObserver, times(1)).onNext("eight");
    +        verify(stringObserver, times(1)).onNext("nine");
         }
     
    -  }
    +    @Test
    +    public void testErrorDelayed4() {
    +        final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three"));
    +        final Observable o2 = Observable.create(new TestErrorObservable("four", "five", "six"));
    +        final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight"));
    +        final Observable o4 = Observable.create(new TestErrorObservable("nine", null));
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(mergeDelayError(o1, o2, o3, o4));
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    +        verify(stringObserver, never()).onCompleted();
    +        verify(stringObserver, times(1)).onNext("one");
    +        verify(stringObserver, times(1)).onNext("two");
    +        verify(stringObserver, times(1)).onNext("three");
    +        verify(stringObserver, times(1)).onNext("four");
    +        verify(stringObserver, times(1)).onNext("five");
    +        verify(stringObserver, times(1)).onNext("six");
    +        verify(stringObserver, times(1)).onNext("seven");
    +        verify(stringObserver, times(1)).onNext("eight");
    +        verify(stringObserver, times(1)).onNext("nine");
    +    }
     
    -  /**
    -   * The unit tests below are from OperationMerge and should ensure the normal merge functionality is correct.
    -   */
    +    @Test
    +    public void testErrorDelayed4WithThreading() {
    +        final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", "three");
    +        final TestAsyncErrorObservable o2 = new TestAsyncErrorObservable("four", "five", "six");
    +        final TestAsyncErrorObservable o3 = new TestAsyncErrorObservable("seven", "eight");
    +        // throw the error at the very end so no onComplete will be called after it
    +        final TestAsyncErrorObservable o4 = new TestAsyncErrorObservable("nine", null);
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(mergeDelayError(Observable.create(o1), Observable.create(o2), Observable.create(o3), Observable.create(o4)));
    +        m.subscribe(stringObserver);
    +
    +        try {
    +            o1.t.join();
    +            o2.t.join();
    +            o3.t.join();
    +            o4.t.join();
    +        } catch (InterruptedException e) {
    +            throw new RuntimeException(e);
    +        }
     
    -  @Test
    -  public void testMergeObservableOfObservables() {
    -    final Observable o1 = Observable.create(new TestSynchronousObservable());
    -    final Observable o2 = Observable.create(new TestSynchronousObservable());
    +        verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    +        verify(stringObserver, never()).onCompleted();
    +        verify(stringObserver, times(1)).onNext("one");
    +        verify(stringObserver, times(1)).onNext("two");
    +        verify(stringObserver, times(1)).onNext("three");
    +        verify(stringObserver, times(1)).onNext("four");
    +        verify(stringObserver, times(1)).onNext("five");
    +        verify(stringObserver, times(1)).onNext("six");
    +        verify(stringObserver, times(1)).onNext("seven");
    +        verify(stringObserver, times(1)).onNext("eight");
    +        verify(stringObserver, times(1)).onNext("nine");
    +    }
     
    -    Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() {
    +    @Test
    +    public void testCompositeErrorDelayed1() {
    +        final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called
    +        final Observable o2 = Observable.create(new TestErrorObservable("one", "two", null));
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(mergeDelayError(o1, o2));
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, times(1)).onError(any(CompositeException.class));
    +        verify(stringObserver, never()).onCompleted();
    +        verify(stringObserver, times(1)).onNext("one");
    +        verify(stringObserver, times(1)).onNext("two");
    +        verify(stringObserver, times(0)).onNext("three");
    +        verify(stringObserver, times(1)).onNext("four");
    +        verify(stringObserver, times(0)).onNext("five");
    +        verify(stringObserver, times(0)).onNext("six");
    +    }
     
    -      @Override
    -      public Subscription onSubscribe(Observer> observer) {
    -        // simulate what would happen in an observable
    -        observer.onNext(o1);
    -        observer.onNext(o2);
    -        observer.onCompleted();
    +    @Test
    +    public void testCompositeErrorDelayed2() {
    +        final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called
    +        final Observable o2 = Observable.create(new TestErrorObservable("one", "two", null));
     
    -        return new Subscription() {
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(mergeDelayError(o1, o2));
    +        CaptureObserver w = new CaptureObserver();
    +        m.subscribe(w);
     
    -          @Override
    -          public void unsubscribe() {
    -            // unregister ... will never be called here since we are executing synchronously
    -          }
    +        assertNotNull(w.e);
    +        if (w.e instanceof CompositeException) {
    +            assertEquals(2, ((CompositeException) w.e).getExceptions().size());
    +            w.e.printStackTrace();
    +        } else {
    +            fail("Expecting CompositeException");
    +        }
     
    -        };
    -      }
    -
    -    });
    -    Observable m = Observable.create(mergeDelayError(observableOfObservables));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(1)).onCompleted();
    -    verify(stringObserver, times(2)).onNext("hello");
    -  }
    -
    -  @Test
    -  public void testMergeArray() {
    -    final Observable o1 = Observable.create(new TestSynchronousObservable());
    -    final Observable o2 = Observable.create(new TestSynchronousObservable());
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(mergeDelayError(o1, o2));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(2)).onNext("hello");
    -    verify(stringObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testMergeList() {
    -    final Observable o1 = Observable.create(new TestSynchronousObservable());
    -    final Observable o2 = Observable.create(new TestSynchronousObservable());
    -    List> listOfObservables = new ArrayList>();
    -    listOfObservables.add(o1);
    -    listOfObservables.add(o2);
    -
    -    Observable m = Observable.create(mergeDelayError(listOfObservables));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(1)).onCompleted();
    -    verify(stringObserver, times(2)).onNext("hello");
    -  }
    -
    -  @Test
    -  public void testUnSubscribe() {
    -    TestObservable tA = new TestObservable();
    -    TestObservable tB = new TestObservable();
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(mergeDelayError(Observable.create(tA), Observable.create(tB)));
    -    Subscription s = m.subscribe(stringObserver);
    -
    -    tA.sendOnNext("Aone");
    -    tB.sendOnNext("Bone");
    -    s.unsubscribe();
    -    tA.sendOnNext("Atwo");
    -    tB.sendOnNext("Btwo");
    -    tA.sendOnCompleted();
    -    tB.sendOnCompleted();
    -
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(1)).onNext("Aone");
    -    verify(stringObserver, times(1)).onNext("Bone");
    -    assertTrue(tA.unsubscribed);
    -    assertTrue(tB.unsubscribed);
    -    verify(stringObserver, never()).onNext("Atwo");
    -    verify(stringObserver, never()).onNext("Btwo");
    -    verify(stringObserver, never()).onCompleted();
    -  }
    -
    -  @Test
    -  public void testMergeArrayWithThreading() {
    -    final TestASynchronousObservable o1 = new TestASynchronousObservable();
    -    final TestASynchronousObservable o2 = new TestASynchronousObservable();
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(mergeDelayError(Observable.create(o1), Observable.create(o2)));
    -    m.subscribe(stringObserver);
    -
    -    try {
    -      o1.t.join();
    -      o2.t.join();
    -    } catch (InterruptedException e) {
    -      throw new RuntimeException(e);
         }
     
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(2)).onNext("hello");
    -    verify(stringObserver, times(1)).onCompleted();
    -  }
    +    /**
    +     * The unit tests below are from OperationMerge and should ensure the normal merge functionality is correct.
    +     */
     
    -  private static class TestSynchronousObservable implements Observable.OnSubscribeFunc {
    +    @Test
    +    public void testMergeObservableOfObservables() {
    +        final Observable o1 = Observable.create(new TestSynchronousObservable());
    +        final Observable o2 = Observable.create(new TestSynchronousObservable());
     
    -    @Override
    -    public Subscription onSubscribe(Observer observer) {
    +        Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() {
     
    -      observer.onNext("hello");
    -      observer.onCompleted();
    +            @Override
    +            public Subscription onSubscribe(Observer> observer) {
    +                // simulate what would happen in an observable
    +                observer.onNext(o1);
    +                observer.onNext(o2);
    +                observer.onCompleted();
     
    -      return new Subscription() {
    +                return new Subscription() {
     
    -        @Override
    -        public void unsubscribe() {
    -          // unregister ... will never be called here since we are executing synchronously
    -        }
    +                    @Override
    +                    public void unsubscribe() {
    +                        // unregister ... will never be called here since we are executing synchronously
    +                    }
    +
    +                };
    +            }
     
    -      };
    +        });
    +        Observable m = Observable.create(mergeDelayError(observableOfObservables));
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(1)).onCompleted();
    +        verify(stringObserver, times(2)).onNext("hello");
         }
    -  }
     
    -  private static class TestASynchronousObservable implements Observable.OnSubscribeFunc {
    -    Thread t;
    +    @Test
    +    public void testMergeArray() {
    +        final Observable o1 = Observable.create(new TestSynchronousObservable());
    +        final Observable o2 = Observable.create(new TestSynchronousObservable());
     
    -    @Override
    -    public Subscription onSubscribe(final Observer observer) {
    -      t = new Thread(new Runnable() {
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(mergeDelayError(o1, o2));
    +        m.subscribe(stringObserver);
     
    -        @Override
    -        public void run() {
    -          observer.onNext("hello");
    -          observer.onCompleted();
    -        }
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(2)).onNext("hello");
    +        verify(stringObserver, times(1)).onCompleted();
    +    }
     
    -      });
    -      t.start();
    +    @Test
    +    public void testMergeList() {
    +        final Observable o1 = Observable.create(new TestSynchronousObservable());
    +        final Observable o2 = Observable.create(new TestSynchronousObservable());
    +        List> listOfObservables = new ArrayList>();
    +        listOfObservables.add(o1);
    +        listOfObservables.add(o2);
     
    -      return new Subscription() {
    +        Observable m = Observable.create(mergeDelayError(listOfObservables));
    +        m.subscribe(stringObserver);
     
    -        @Override
    -        public void unsubscribe() {
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(1)).onCompleted();
    +        verify(stringObserver, times(2)).onNext("hello");
    +    }
    +
    +    @Test
    +    public void testUnSubscribe() {
    +        TestObservable tA = new TestObservable();
    +        TestObservable tB = new TestObservable();
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(mergeDelayError(Observable.create(tA), Observable.create(tB)));
    +        Subscription s = m.subscribe(stringObserver);
    +
    +        tA.sendOnNext("Aone");
    +        tB.sendOnNext("Bone");
    +        s.unsubscribe();
    +        tA.sendOnNext("Atwo");
    +        tB.sendOnNext("Btwo");
    +        tA.sendOnCompleted();
    +        tB.sendOnCompleted();
    +
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(1)).onNext("Aone");
    +        verify(stringObserver, times(1)).onNext("Bone");
    +        assertTrue(tA.unsubscribed);
    +        assertTrue(tB.unsubscribed);
    +        verify(stringObserver, never()).onNext("Atwo");
    +        verify(stringObserver, never()).onNext("Btwo");
    +        verify(stringObserver, never()).onCompleted();
    +    }
    +
    +    @Test
    +    public void testMergeArrayWithThreading() {
    +        final TestASynchronousObservable o1 = new TestASynchronousObservable();
    +        final TestASynchronousObservable o2 = new TestASynchronousObservable();
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(mergeDelayError(Observable.create(o1), Observable.create(o2)));
    +        m.subscribe(stringObserver);
     
    +        try {
    +            o1.t.join();
    +            o2.t.join();
    +        } catch (InterruptedException e) {
    +            throw new RuntimeException(e);
             }
     
    -      };
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(2)).onNext("hello");
    +        verify(stringObserver, times(1)).onCompleted();
         }
    -  }
     
    -  /**
    -   * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens.
    -   */
    -  private static class TestObservable implements Observable.OnSubscribeFunc {
    +    private static class TestSynchronousObservable implements Observable.OnSubscribeFunc {
     
    -    Observer observer = null;
    -    volatile boolean unsubscribed = false;
    -    Subscription s = new Subscription() {
    +        @Override
    +        public Subscription onSubscribe(Observer observer) {
     
    -      @Override
    -      public void unsubscribe() {
    -        unsubscribed = true;
    +            observer.onNext("hello");
    +            observer.onCompleted();
     
    -      }
    +            return new Subscription() {
     
    -    };
    +                @Override
    +                public void unsubscribe() {
    +                    // unregister ... will never be called here since we are executing synchronously
    +                }
     
    -    /* used to simulate subscription */
    -    public void sendOnCompleted() {
    -      observer.onCompleted();
    +            };
    +        }
         }
     
    -    /* used to simulate subscription */
    -    public void sendOnNext(String value) {
    -      observer.onNext(value);
    -    }
    +    private static class TestASynchronousObservable implements Observable.OnSubscribeFunc {
    +        Thread t;
     
    -    /* used to simulate subscription */
    -    @SuppressWarnings("unused")
    -    public void sendOnError(Throwable e) {
    -      observer.onError(e);
    -    }
    +        @Override
    +        public Subscription onSubscribe(final Observer observer) {
    +            t = new Thread(new Runnable() {
     
    -    @Override
    -    public Subscription onSubscribe(final Observer observer) {
    -      this.observer = observer;
    -      return s;
    -    }
    -  }
    +                @Override
    +                public void run() {
    +                    observer.onNext("hello");
    +                    observer.onCompleted();
    +                }
     
    -  private static class TestErrorObservable implements Observable.OnSubscribeFunc {
    +            });
    +            t.start();
     
    -    String[] valuesToReturn;
    +            return new Subscription() {
     
    -    TestErrorObservable(String... values) {
    -      valuesToReturn = values;
    -    }
    +                @Override
    +                public void unsubscribe() {
     
    -    @Override
    -    public Subscription onSubscribe(Observer observer) {
    -      boolean errorThrown = false;
    -      for (String s : valuesToReturn) {
    -        if (s == null) {
    -          System.out.println("throwing exception");
    -          observer.onError(new NullPointerException());
    -          errorThrown = true;
    -          // purposefully not returning here so it will continue calling onNext
    -          // so that we also test that we handle bad sequences like this
    -        } else {
    -          observer.onNext(s);
    +                }
    +
    +            };
             }
    -      }
    -      if (!errorThrown) {
    -        observer.onCompleted();
    -      }
    +    }
     
    -      return new Subscription() {
    +    /**
    +     * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens.
    +     */
    +    private static class TestObservable implements Observable.OnSubscribeFunc {
     
    -        @Override
    -        public void unsubscribe() {
    -          // unregister ... will never be called here since we are executing synchronously
    -        }
    +        Observer observer = null;
    +        volatile boolean unsubscribed = false;
    +        Subscription s = new Subscription() {
     
    -      };
    -    }
    -  }
    +            @Override
    +            public void unsubscribe() {
    +                unsubscribed = true;
     
    -  private static class TestAsyncErrorObservable implements Observable.OnSubscribeFunc {
    +            }
     
    -    String[] valuesToReturn;
    +        };
     
    -    TestAsyncErrorObservable(String... values) {
    -      valuesToReturn = values;
    -    }
    +        /* used to simulate subscription */
    +        public void sendOnCompleted() {
    +            observer.onCompleted();
    +        }
     
    -    Thread t;
    +        /* used to simulate subscription */
    +        public void sendOnNext(String value) {
    +            observer.onNext(value);
    +        }
     
    -    @Override
    -    public Subscription onSubscribe(final Observer observer) {
    -      t = new Thread(new Runnable() {
    +        /* used to simulate subscription */
    +        @SuppressWarnings("unused")
    +        public void sendOnError(Throwable e) {
    +            observer.onError(e);
    +        }
     
             @Override
    -        public void run() {
    -          for (String s : valuesToReturn) {
    -            if (s == null) {
    -              System.out.println("throwing exception");
    -              try {
    -                Thread.sleep(100);
    -              } catch (Throwable e) {
    -
    -              }
    -              observer.onError(new NullPointerException());
    -              return;
    -            } else {
    -              observer.onNext(s);
    -            }
    -          }
    -          System.out.println("subscription complete");
    -          observer.onCompleted();
    +        public Subscription onSubscribe(final Observer observer) {
    +            this.observer = observer;
    +            return s;
             }
    +    }
    +
    +    private static class TestErrorObservable implements Observable.OnSubscribeFunc {
     
    -      });
    -      t.start();
    +        String[] valuesToReturn;
     
    -      return new Subscription() {
    +        TestErrorObservable(String... values) {
    +            valuesToReturn = values;
    +        }
     
             @Override
    -        public void unsubscribe() {
    +        public Subscription onSubscribe(Observer observer) {
    +            boolean errorThrown = false;
    +            for (String s : valuesToReturn) {
    +                if (s == null) {
    +                    System.out.println("throwing exception");
    +                    observer.onError(new NullPointerException());
    +                    errorThrown = true;
    +                    // purposefully not returning here so it will continue calling onNext
    +                    // so that we also test that we handle bad sequences like this
    +                } else {
    +                    observer.onNext(s);
    +                }
    +            }
    +            if (!errorThrown) {
    +                observer.onCompleted();
    +            }
     
    -        }
    +            return new Subscription() {
    +
    +                @Override
    +                public void unsubscribe() {
    +                    // unregister ... will never be called here since we are executing synchronously
    +                }
     
    -      };
    +            };
    +        }
         }
    -  }
     
    -  private static class CaptureObserver implements Observer {
    -    volatile Throwable e;
    +    private static class TestAsyncErrorObservable implements Observable.OnSubscribeFunc {
     
    -    @Override
    -    public void onCompleted() {
    +        String[] valuesToReturn;
     
    -    }
    +        TestAsyncErrorObservable(String... values) {
    +            valuesToReturn = values;
    +        }
     
    -    @Override
    -    public void onError(Throwable e) {
    -      this.e = e;
    +        Thread t;
    +
    +        @Override
    +        public Subscription onSubscribe(final Observer observer) {
    +            t = new Thread(new Runnable() {
    +
    +                @Override
    +                public void run() {
    +                    for (String s : valuesToReturn) {
    +                        if (s == null) {
    +                            System.out.println("throwing exception");
    +                            try {
    +                                Thread.sleep(100);
    +                            } catch (Throwable e) {
    +
    +                            }
    +                            observer.onError(new NullPointerException());
    +                            return;
    +                        } else {
    +                            observer.onNext(s);
    +                        }
    +                    }
    +                    System.out.println("subscription complete");
    +                    observer.onCompleted();
    +                }
    +
    +            });
    +            t.start();
    +
    +            return new Subscription() {
    +
    +                @Override
    +                public void unsubscribe() {
    +
    +                }
    +
    +            };
    +        }
         }
     
    -    @Override
    -    public void onNext(String args) {
    +    private static class CaptureObserver implements Observer {
    +        volatile Throwable e;
     
    -    }
    +        @Override
    +        public void onCompleted() {
     
    -  }
    +        }
    +
    +        @Override
    +        public void onError(Throwable e) {
    +            this.e = e;
    +        }
    +
    +        @Override
    +        public void onNext(String args) {
    +
    +        }
    +
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java b/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java
    index b3bdf1423c..d7d399486e 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java
    @@ -1,9 +1,22 @@
     package rx.operators;
     
    +import static org.junit.Assert.*;
    +import static org.mockito.Matchers.*;
    +import static org.mockito.Mockito.*;
    +import static rx.operators.OperationMerge.*;
    +
    +import java.util.ArrayList;
    +import java.util.List;
    +import java.util.concurrent.CountDownLatch;
    +import java.util.concurrent.TimeUnit;
    +import java.util.concurrent.atomic.AtomicBoolean;
    +import java.util.concurrent.atomic.AtomicInteger;
    +
     import org.junit.Before;
     import org.junit.Test;
     import org.mockito.Mock;
     import org.mockito.MockitoAnnotations;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.Subscription;
    @@ -11,448 +24,436 @@
     import rx.util.functions.Action0;
     import rx.util.functions.Action1;
     
    -import java.util.ArrayList;
    -import java.util.List;
    -import java.util.concurrent.CountDownLatch;
    -import java.util.concurrent.TimeUnit;
    -import java.util.concurrent.atomic.AtomicBoolean;
    -import java.util.concurrent.atomic.AtomicInteger;
    -
    -import static org.junit.Assert.*;
    -import static org.mockito.Matchers.any;
    -import static org.mockito.Mockito.*;
    -import static rx.operators.OperationMerge.merge;
    -
     public class OperationMergeTest {
     
    -  @Mock
    -  Observer stringObserver;
    +    @Mock
    +    Observer stringObserver;
     
    -  @Before
    -  public void before() {
    -    MockitoAnnotations.initMocks(this);
    -  }
    +    @Before
    +    public void before() {
    +        MockitoAnnotations.initMocks(this);
    +    }
     
    -  @Test
    -  public void testMergeObservableOfObservables() {
    -    final Observable o1 = Observable.create(new TestSynchronousObservable());
    -    final Observable o2 = Observable.create(new TestSynchronousObservable());
    +    @Test
    +    public void testMergeObservableOfObservables() {
    +        final Observable o1 = Observable.create(new TestSynchronousObservable());
    +        final Observable o2 = Observable.create(new TestSynchronousObservable());
     
    -    Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() {
    +        Observable> observableOfObservables = Observable.create(new Observable.OnSubscribeFunc>() {
     
    -      @Override
    -      public Subscription onSubscribe(Observer> observer) {
    -        // simulate what would happen in an observable
    -        observer.onNext(o1);
    -        observer.onNext(o2);
    -        observer.onCompleted();
    +            @Override
    +            public Subscription onSubscribe(Observer> observer) {
    +                // simulate what would happen in an observable
    +                observer.onNext(o1);
    +                observer.onNext(o2);
    +                observer.onCompleted();
     
    -        return new Subscription() {
    +                return new Subscription() {
     
    -          @Override
    -          public void unsubscribe() {
    -            // unregister ... will never be called here since we are executing synchronously
    -          }
    +                    @Override
    +                    public void unsubscribe() {
    +                        // unregister ... will never be called here since we are executing synchronously
    +                    }
     
    -        };
    -      }
    -
    -    });
    -    Observable m = Observable.create(merge(observableOfObservables));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(1)).onCompleted();
    -    verify(stringObserver, times(2)).onNext("hello");
    -  }
    -
    -  @Test
    -  public void testMergeArray() {
    -    final Observable o1 = Observable.create(new TestSynchronousObservable());
    -    final Observable o2 = Observable.create(new TestSynchronousObservable());
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(merge(o1, o2));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(2)).onNext("hello");
    -    verify(stringObserver, times(1)).onCompleted();
    -  }
    -
    -  @Test
    -  public void testMergeList() {
    -    final Observable o1 = Observable.create(new TestSynchronousObservable());
    -    final Observable o2 = Observable.create(new TestSynchronousObservable());
    -    List> listOfObservables = new ArrayList>();
    -    listOfObservables.add(o1);
    -    listOfObservables.add(o2);
    -
    -    Observable m = Observable.create(merge(listOfObservables));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(1)).onCompleted();
    -    verify(stringObserver, times(2)).onNext("hello");
    -  }
    -
    -  @Test
    -  public void testUnSubscribe() {
    -    TestObservable tA = new TestObservable();
    -    TestObservable tB = new TestObservable();
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(merge(Observable.create(tA), Observable.create(tB)));
    -    Subscription s = m.subscribe(stringObserver);
    -
    -    tA.sendOnNext("Aone");
    -    tB.sendOnNext("Bone");
    -    s.unsubscribe();
    -    tA.sendOnNext("Atwo");
    -    tB.sendOnNext("Btwo");
    -    tA.sendOnCompleted();
    -    tB.sendOnCompleted();
    -
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(1)).onNext("Aone");
    -    verify(stringObserver, times(1)).onNext("Bone");
    -    assertTrue(tA.unsubscribed);
    -    assertTrue(tB.unsubscribed);
    -    verify(stringObserver, never()).onNext("Atwo");
    -    verify(stringObserver, never()).onNext("Btwo");
    -    verify(stringObserver, never()).onCompleted();
    -  }
    -
    -  @Test
    -  public void testUnSubscribeObservableOfObservables() throws InterruptedException {
    -
    -    final AtomicBoolean unsubscribed = new AtomicBoolean();
    -    final CountDownLatch latch = new CountDownLatch(1);
    -
    -    Observable> source = Observable.create(new Observable.OnSubscribeFunc>() {
    -
    -      @Override
    -      public Subscription onSubscribe(final Observer> observer) {
    -        // verbose on purpose so I can track the inside of it
    -        final Subscription s = Subscriptions.create(new Action0() {
    -
    -          @Override
    -          public void call() {
    -            System.out.println("*** unsubscribed");
    -            unsubscribed.set(true);
    -          }
    +                };
    +            }
     
             });
    +        Observable m = Observable.create(merge(observableOfObservables));
    +        m.subscribe(stringObserver);
     
    -        new Thread(new Runnable() {
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(1)).onCompleted();
    +        verify(stringObserver, times(2)).onNext("hello");
    +    }
     
    -          @Override
    -          public void run() {
    +    @Test
    +    public void testMergeArray() {
    +        final Observable o1 = Observable.create(new TestSynchronousObservable());
    +        final Observable o2 = Observable.create(new TestSynchronousObservable());
     
    -            while (!unsubscribed.get()) {
    -              observer.onNext(Observable.from(1L, 2L));
    -            }
    -            System.out.println("Done looping after unsubscribe: " + unsubscribed.get());
    -            observer.onCompleted();
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(merge(o1, o2));
    +        m.subscribe(stringObserver);
     
    -            // mark that the thread is finished
    -            latch.countDown();
    -          }
    -        }).start();
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(2)).onNext("hello");
    +        verify(stringObserver, times(1)).onCompleted();
    +    }
     
    -        return s;
    -      }
    +    @Test
    +    public void testMergeList() {
    +        final Observable o1 = Observable.create(new TestSynchronousObservable());
    +        final Observable o2 = Observable.create(new TestSynchronousObservable());
    +        List> listOfObservables = new ArrayList>();
    +        listOfObservables.add(o1);
    +        listOfObservables.add(o2);
     
    -      ;
    +        Observable m = Observable.create(merge(listOfObservables));
    +        m.subscribe(stringObserver);
     
    -    });
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(1)).onCompleted();
    +        verify(stringObserver, times(2)).onNext("hello");
    +    }
     
    -    final AtomicInteger count = new AtomicInteger();
    -    Observable.create(merge(source)).take(6).toBlockingObservable().forEach(new Action1() {
    +    @Test
    +    public void testUnSubscribe() {
    +        TestObservable tA = new TestObservable();
    +        TestObservable tB = new TestObservable();
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(merge(Observable.create(tA), Observable.create(tB)));
    +        Subscription s = m.subscribe(stringObserver);
    +
    +        tA.sendOnNext("Aone");
    +        tB.sendOnNext("Bone");
    +        s.unsubscribe();
    +        tA.sendOnNext("Atwo");
    +        tB.sendOnNext("Btwo");
    +        tA.sendOnCompleted();
    +        tB.sendOnCompleted();
    +
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(1)).onNext("Aone");
    +        verify(stringObserver, times(1)).onNext("Bone");
    +        assertTrue(tA.unsubscribed);
    +        assertTrue(tB.unsubscribed);
    +        verify(stringObserver, never()).onNext("Atwo");
    +        verify(stringObserver, never()).onNext("Btwo");
    +        verify(stringObserver, never()).onCompleted();
    +    }
     
    -      @Override
    -      public void call(Long v) {
    -        System.out.println("Value: " + v);
    -        int c = count.incrementAndGet();
    -        if (c > 6) {
    -          fail("Should be only 6");
    -        }
    +    @Test
    +    public void testUnSubscribeObservableOfObservables() throws InterruptedException {
     
    -      }
    -    });
    +        final AtomicBoolean unsubscribed = new AtomicBoolean();
    +        final CountDownLatch latch = new CountDownLatch(1);
     
    -    latch.await(1000, TimeUnit.MILLISECONDS);
    +        Observable> source = Observable.create(new Observable.OnSubscribeFunc>() {
     
    -    System.out.println("unsubscribed: " + unsubscribed.get());
    +            @Override
    +            public Subscription onSubscribe(final Observer> observer) {
    +                // verbose on purpose so I can track the inside of it
    +                final Subscription s = Subscriptions.create(new Action0() {
     
    -    assertTrue(unsubscribed.get());
    +                    @Override
    +                    public void call() {
    +                        System.out.println("*** unsubscribed");
    +                        unsubscribed.set(true);
    +                    }
     
    -  }
    +                });
     
    -  @Test
    -  public void testMergeArrayWithThreading() {
    -    final TestASynchronousObservable o1 = new TestASynchronousObservable();
    -    final TestASynchronousObservable o2 = new TestASynchronousObservable();
    +                new Thread(new Runnable() {
     
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(merge(Observable.create(o1), Observable.create(o2)));
    -    m.subscribe(stringObserver);
    +                    @Override
    +                    public void run() {
     
    -    try {
    -      o1.t.join();
    -      o2.t.join();
    -    } catch (InterruptedException e) {
    -      throw new RuntimeException(e);
    -    }
    +                        while (!unsubscribed.get()) {
    +                            observer.onNext(Observable.from(1L, 2L));
    +                        }
    +                        System.out.println("Done looping after unsubscribe: " + unsubscribed.get());
    +                        observer.onCompleted();
     
    -    verify(stringObserver, never()).onError(any(Throwable.class));
    -    verify(stringObserver, times(2)).onNext("hello");
    -    verify(stringObserver, times(1)).onCompleted();
    -  }
    +                        // mark that the thread is finished
    +                        latch.countDown();
    +                    }
    +                }).start();
     
    -  @Test
    -  public void testSynchronizationOfMultipleSequences() throws Throwable {
    -    final TestASynchronousObservable o1 = new TestASynchronousObservable();
    -    final TestASynchronousObservable o2 = new TestASynchronousObservable();
    +                return s;
    +            }
     
    -    // use this latch to cause onNext to wait until we're ready to let it go
    -    final CountDownLatch endLatch = new CountDownLatch(1);
    +            ;
     
    -    final AtomicInteger concurrentCounter = new AtomicInteger();
    -    final AtomicInteger totalCounter = new AtomicInteger();
    +        });
     
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(merge(Observable.create(o1), Observable.create(o2)));
    -    m.subscribe(new Observer() {
    +        final AtomicInteger count = new AtomicInteger();
    +        Observable.create(merge(source)).take(6).toBlockingObservable().forEach(new Action1() {
     
    -      @Override
    -      public void onCompleted() {
    +            @Override
    +            public void call(Long v) {
    +                System.out.println("Value: " + v);
    +                int c = count.incrementAndGet();
    +                if (c > 6) {
    +                    fail("Should be only 6");
    +                }
     
    -      }
    +            }
    +        });
     
    -      @Override
    -      public void onError(Throwable e) {
    -        throw new RuntimeException("failed", e);
    -      }
    +        latch.await(1000, TimeUnit.MILLISECONDS);
    +
    +        System.out.println("unsubscribed: " + unsubscribed.get());
    +
    +        assertTrue(unsubscribed.get());
    +
    +    }
    +
    +    @Test
    +    public void testMergeArrayWithThreading() {
    +        final TestASynchronousObservable o1 = new TestASynchronousObservable();
    +        final TestASynchronousObservable o2 = new TestASynchronousObservable();
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(merge(Observable.create(o1), Observable.create(o2)));
    +        m.subscribe(stringObserver);
     
    -      @Override
    -      public void onNext(String v) {
    -        totalCounter.incrementAndGet();
    -        concurrentCounter.incrementAndGet();
             try {
    -          // wait here until we're done asserting
    -          endLatch.await();
    +            o1.t.join();
    +            o2.t.join();
             } catch (InterruptedException e) {
    -          e.printStackTrace();
    -          throw new RuntimeException("failed", e);
    -        } finally {
    -          concurrentCounter.decrementAndGet();
    +            throw new RuntimeException(e);
             }
    -      }
     
    -    });
    +        verify(stringObserver, never()).onError(any(Throwable.class));
    +        verify(stringObserver, times(2)).onNext("hello");
    +        verify(stringObserver, times(1)).onCompleted();
    +    }
     
    -    // wait for both observables to send (one should be blocked)
    -    o1.onNextBeingSent.await();
    -    o2.onNextBeingSent.await();
    +    @Test
    +    public void testSynchronizationOfMultipleSequences() throws Throwable {
    +        final TestASynchronousObservable o1 = new TestASynchronousObservable();
    +        final TestASynchronousObservable o2 = new TestASynchronousObservable();
     
    -    // I can't think of a way to know for sure that both threads have or are trying to send onNext
    -    // since I can't use a CountDownLatch for "after" onNext since I want to catch during it
    -    // but I can't know for sure onNext is invoked
    -    // so I'm unfortunately reverting to using a Thread.sleep to allow the process scheduler time
    -    // to make sure after o1.onNextBeingSent and o2.onNextBeingSent are hit that the following
    -    // onNext is invoked.
    +        // use this latch to cause onNext to wait until we're ready to let it go
    +        final CountDownLatch endLatch = new CountDownLatch(1);
     
    -    Thread.sleep(300);
    +        final AtomicInteger concurrentCounter = new AtomicInteger();
    +        final AtomicInteger totalCounter = new AtomicInteger();
     
    -    try { // in try/finally so threads are released via latch countDown even if assertion fails
    -      assertEquals(1, concurrentCounter.get());
    -    } finally {
    -      // release so it can finish
    -      endLatch.countDown();
    -    }
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(merge(Observable.create(o1), Observable.create(o2)));
    +        m.subscribe(new Observer() {
     
    -    try {
    -      o1.t.join();
    -      o2.t.join();
    -    } catch (InterruptedException e) {
    -      throw new RuntimeException(e);
    -    }
    +            @Override
    +            public void onCompleted() {
     
    -    assertEquals(2, totalCounter.get());
    -    assertEquals(0, concurrentCounter.get());
    -  }
    -
    -  /**
    -   * unit test from OperationMergeDelayError backported here to show how these use cases work with normal merge
    -   */
    -  @Test
    -  public void testError1() {
    -    // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior
    -    final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six"
    -    final Observable o2 = Observable.create(new TestErrorObservable("one", "two", "three")); // we expect to lose all of these since o1 is done first and fails
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(merge(o1, o2));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    -    verify(stringObserver, never()).onCompleted();
    -    verify(stringObserver, times(0)).onNext("one");
    -    verify(stringObserver, times(0)).onNext("two");
    -    verify(stringObserver, times(0)).onNext("three");
    -    verify(stringObserver, times(1)).onNext("four");
    -    verify(stringObserver, times(0)).onNext("five");
    -    verify(stringObserver, times(0)).onNext("six");
    -  }
    -
    -  /**
    -   * unit test from OperationMergeDelayError backported here to show how these use cases work with normal merge
    -   */
    -  @Test
    -  public void testError2() {
    -    // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior
    -    final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three"));
    -    final Observable o2 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six"
    -    final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null));// we expect to lose all of these since o2 is done first and fails
    -    final Observable o4 = Observable.create(new TestErrorObservable("nine"));// we expect to lose all of these since o2 is done first and fails
    -
    -    @SuppressWarnings("unchecked")
    -    Observable m = Observable.create(merge(o1, o2, o3, o4));
    -    m.subscribe(stringObserver);
    -
    -    verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    -    verify(stringObserver, never()).onCompleted();
    -    verify(stringObserver, times(1)).onNext("one");
    -    verify(stringObserver, times(1)).onNext("two");
    -    verify(stringObserver, times(1)).onNext("three");
    -    verify(stringObserver, times(1)).onNext("four");
    -    verify(stringObserver, times(0)).onNext("five");
    -    verify(stringObserver, times(0)).onNext("six");
    -    verify(stringObserver, times(0)).onNext("seven");
    -    verify(stringObserver, times(0)).onNext("eight");
    -    verify(stringObserver, times(0)).onNext("nine");
    -  }
    -
    -  private static class TestSynchronousObservable implements Observable.OnSubscribeFunc {
    -
    -    @Override
    -    public Subscription onSubscribe(Observer observer) {
    -
    -      observer.onNext("hello");
    -      observer.onCompleted();
    -
    -      return new Subscription() {
    +            }
     
    -        @Override
    -        public void unsubscribe() {
    -          // unregister ... will never be called here since we are executing synchronously
    +            @Override
    +            public void onError(Throwable e) {
    +                throw new RuntimeException("failed", e);
    +            }
    +
    +            @Override
    +            public void onNext(String v) {
    +                totalCounter.incrementAndGet();
    +                concurrentCounter.incrementAndGet();
    +                try {
    +                    // wait here until we're done asserting
    +                    endLatch.await();
    +                } catch (InterruptedException e) {
    +                    e.printStackTrace();
    +                    throw new RuntimeException("failed", e);
    +                } finally {
    +                    concurrentCounter.decrementAndGet();
    +                }
    +            }
    +
    +        });
    +
    +        // wait for both observables to send (one should be blocked)
    +        o1.onNextBeingSent.await();
    +        o2.onNextBeingSent.await();
    +
    +        // I can't think of a way to know for sure that both threads have or are trying to send onNext
    +        // since I can't use a CountDownLatch for "after" onNext since I want to catch during it
    +        // but I can't know for sure onNext is invoked
    +        // so I'm unfortunately reverting to using a Thread.sleep to allow the process scheduler time
    +        // to make sure after o1.onNextBeingSent and o2.onNextBeingSent are hit that the following
    +        // onNext is invoked.
    +
    +        Thread.sleep(300);
    +
    +        try { // in try/finally so threads are released via latch countDown even if assertion fails
    +            assertEquals(1, concurrentCounter.get());
    +        } finally {
    +            // release so it can finish
    +            endLatch.countDown();
             }
     
    -      };
    +        try {
    +            o1.t.join();
    +            o2.t.join();
    +        } catch (InterruptedException e) {
    +            throw new RuntimeException(e);
    +        }
    +
    +        assertEquals(2, totalCounter.get());
    +        assertEquals(0, concurrentCounter.get());
         }
    -  }
     
    -  private static class TestASynchronousObservable implements Observable.OnSubscribeFunc {
    -    Thread t;
    -    final CountDownLatch onNextBeingSent = new CountDownLatch(1);
    +    /**
    +     * unit test from OperationMergeDelayError backported here to show how these use cases work with normal merge
    +     */
    +    @Test
    +    public void testError1() {
    +        // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior
    +        final Observable o1 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six"
    +        final Observable o2 = Observable.create(new TestErrorObservable("one", "two", "three")); // we expect to lose all of these since o1 is done first and fails
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(merge(o1, o2));
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    +        verify(stringObserver, never()).onCompleted();
    +        verify(stringObserver, times(0)).onNext("one");
    +        verify(stringObserver, times(0)).onNext("two");
    +        verify(stringObserver, times(0)).onNext("three");
    +        verify(stringObserver, times(1)).onNext("four");
    +        verify(stringObserver, times(0)).onNext("five");
    +        verify(stringObserver, times(0)).onNext("six");
    +    }
    +
    +    /**
    +     * unit test from OperationMergeDelayError backported here to show how these use cases work with normal merge
    +     */
    +    @Test
    +    public void testError2() {
    +        // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior
    +        final Observable o1 = Observable.create(new TestErrorObservable("one", "two", "three"));
    +        final Observable o2 = Observable.create(new TestErrorObservable("four", null, "six")); // we expect to lose "six"
    +        final Observable o3 = Observable.create(new TestErrorObservable("seven", "eight", null));// we expect to lose all of these since o2 is done first and fails
    +        final Observable o4 = Observable.create(new TestErrorObservable("nine"));// we expect to lose all of these since o2 is done first and fails
    +
    +        @SuppressWarnings("unchecked")
    +        Observable m = Observable.create(merge(o1, o2, o3, o4));
    +        m.subscribe(stringObserver);
    +
    +        verify(stringObserver, times(1)).onError(any(NullPointerException.class));
    +        verify(stringObserver, never()).onCompleted();
    +        verify(stringObserver, times(1)).onNext("one");
    +        verify(stringObserver, times(1)).onNext("two");
    +        verify(stringObserver, times(1)).onNext("three");
    +        verify(stringObserver, times(1)).onNext("four");
    +        verify(stringObserver, times(0)).onNext("five");
    +        verify(stringObserver, times(0)).onNext("six");
    +        verify(stringObserver, times(0)).onNext("seven");
    +        verify(stringObserver, times(0)).onNext("eight");
    +        verify(stringObserver, times(0)).onNext("nine");
    +    }
     
    -    @Override
    -    public Subscription onSubscribe(final Observer observer) {
    -      t = new Thread(new Runnable() {
    +    private static class TestSynchronousObservable implements Observable.OnSubscribeFunc {
     
             @Override
    -        public void run() {
    -          onNextBeingSent.countDown();
    -          observer.onNext("hello");
    -          // I can't use a countDownLatch to prove we are actually sending 'onNext'
    -          // since it will block if synchronized and I'll deadlock
    -          observer.onCompleted();
    -        }
    +        public Subscription onSubscribe(Observer observer) {
     
    -      });
    -      t.start();
    +            observer.onNext("hello");
    +            observer.onCompleted();
     
    -      return new Subscription() {
    +            return new Subscription() {
     
    -        @Override
    -        public void unsubscribe() {
    +                @Override
    +                public void unsubscribe() {
    +                    // unregister ... will never be called here since we are executing synchronously
    +                }
     
    +            };
             }
    -
    -      };
         }
    -  }
     
    -  /**
    -   * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens.
    -   */
    -  private static class TestObservable implements Observable.OnSubscribeFunc {
    +    private static class TestASynchronousObservable implements Observable.OnSubscribeFunc {
    +        Thread t;
    +        final CountDownLatch onNextBeingSent = new CountDownLatch(1);
    +
    +        @Override
    +        public Subscription onSubscribe(final Observer observer) {
    +            t = new Thread(new Runnable() {
     
    -    Observer observer = null;
    -    volatile boolean unsubscribed = false;
    -    Subscription s = new Subscription() {
    +                @Override
    +                public void run() {
    +                    onNextBeingSent.countDown();
    +                    observer.onNext("hello");
    +                    // I can't use a countDownLatch to prove we are actually sending 'onNext'
    +                    // since it will block if synchronized and I'll deadlock
    +                    observer.onCompleted();
    +                }
     
    -      @Override
    -      public void unsubscribe() {
    -        unsubscribed = true;
    +            });
    +            t.start();
     
    -      }
    +            return new Subscription() {
     
    -    };
    +                @Override
    +                public void unsubscribe() {
     
    -    /* used to simulate subscription */
    -    public void sendOnCompleted() {
    -      observer.onCompleted();
    -    }
    +                }
     
    -    /* used to simulate subscription */
    -    public void sendOnNext(String value) {
    -      observer.onNext(value);
    +            };
    +        }
         }
     
    -    /* used to simulate subscription */
    -    @SuppressWarnings("unused")
    -    public void sendOnError(Throwable e) {
    -      observer.onError(e);
    -    }
    +    /**
    +     * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens.
    +     */
    +    private static class TestObservable implements Observable.OnSubscribeFunc {
     
    -    @Override
    -    public Subscription onSubscribe(final Observer observer) {
    -      this.observer = observer;
    -      return s;
    -    }
    -  }
    +        Observer observer = null;
    +        volatile boolean unsubscribed = false;
    +        Subscription s = new Subscription() {
     
    -  private static class TestErrorObservable implements Observable.OnSubscribeFunc {
    +            @Override
    +            public void unsubscribe() {
    +                unsubscribed = true;
     
    -    String[] valuesToReturn;
    +            }
     
    -    TestErrorObservable(String... values) {
    -      valuesToReturn = values;
    -    }
    +        };
     
    -    @Override
    -    public Subscription onSubscribe(Observer observer) {
    +        /* used to simulate subscription */
    +        public void sendOnCompleted() {
    +            observer.onCompleted();
    +        }
     
    -      for (String s : valuesToReturn) {
    -        if (s == null) {
    -          System.out.println("throwing exception");
    -          observer.onError(new NullPointerException());
    -        } else {
    -          observer.onNext(s);
    +        /* used to simulate subscription */
    +        public void sendOnNext(String value) {
    +            observer.onNext(value);
             }
    -      }
    -      observer.onCompleted();
     
    -      return new Subscription() {
    +        /* used to simulate subscription */
    +        @SuppressWarnings("unused")
    +        public void sendOnError(Throwable e) {
    +            observer.onError(e);
    +        }
     
             @Override
    -        public void unsubscribe() {
    -          // unregister ... will never be called here since we are executing synchronously
    +        public Subscription onSubscribe(final Observer observer) {
    +            this.observer = observer;
    +            return s;
             }
    +    }
     
    -      };
    +    private static class TestErrorObservable implements Observable.OnSubscribeFunc {
    +
    +        String[] valuesToReturn;
    +
    +        TestErrorObservable(String... values) {
    +            valuesToReturn = values;
    +        }
    +
    +        @Override
    +        public Subscription onSubscribe(Observer observer) {
    +
    +            for (String s : valuesToReturn) {
    +                if (s == null) {
    +                    System.out.println("throwing exception");
    +                    observer.onError(new NullPointerException());
    +                } else {
    +                    observer.onNext(s);
    +                }
    +            }
    +            observer.onCompleted();
    +
    +            return new Subscription() {
    +
    +                @Override
    +                public void unsubscribe() {
    +                    // unregister ... will never be called here since we are executing synchronously
    +                }
    +
    +            };
    +        }
         }
    -  }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java b/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java
    index 35c6c9fadf..9dfb1ef9c1 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java
    @@ -1,59 +1,59 @@
     package rx.operators;
     
    -import org.junit.Test;
    -import rx.subjects.PublishSubject;
    -import rx.subjects.Subject;
    +import static org.junit.Assert.*;
    +import static rx.operators.OperationMostRecent.*;
     
     import java.util.Iterator;
     
    -import static org.junit.Assert.*;
    -import static rx.operators.OperationMostRecent.mostRecent;
    +import org.junit.Test;
     
    -public class OperationMostRecentTest {
    +import rx.subjects.PublishSubject;
    +import rx.subjects.Subject;
     
    +public class OperationMostRecentTest {
     
    -  @Test
    -  public void testMostRecent() {
    -    Subject observable = PublishSubject.create();
    +    @Test
    +    public void testMostRecent() {
    +        Subject observable = PublishSubject.create();
     
    -    Iterator it = mostRecent(observable, "default").iterator();
    +        Iterator it = mostRecent(observable, "default").iterator();
     
    -    assertTrue(it.hasNext());
    -    assertEquals("default", it.next());
    -    assertEquals("default", it.next());
    +        assertTrue(it.hasNext());
    +        assertEquals("default", it.next());
    +        assertEquals("default", it.next());
     
    -    observable.onNext("one");
    -    assertTrue(it.hasNext());
    -    assertEquals("one", it.next());
    -    assertEquals("one", it.next());
    +        observable.onNext("one");
    +        assertTrue(it.hasNext());
    +        assertEquals("one", it.next());
    +        assertEquals("one", it.next());
     
    -    observable.onNext("two");
    -    assertTrue(it.hasNext());
    -    assertEquals("two", it.next());
    -    assertEquals("two", it.next());
    +        observable.onNext("two");
    +        assertTrue(it.hasNext());
    +        assertEquals("two", it.next());
    +        assertEquals("two", it.next());
     
    -    observable.onCompleted();
    -    assertFalse(it.hasNext());
    +        observable.onCompleted();
    +        assertFalse(it.hasNext());
     
    -  }
    +    }
     
    -  @Test(expected = TestException.class)
    -  public void testMostRecentWithException() {
    -    Subject observable = PublishSubject.create();
    +    @Test(expected = TestException.class)
    +    public void testMostRecentWithException() {
    +        Subject observable = PublishSubject.create();
     
    -    Iterator it = mostRecent(observable, "default").iterator();
    +        Iterator it = mostRecent(observable, "default").iterator();
     
    -    assertTrue(it.hasNext());
    -    assertEquals("default", it.next());
    -    assertEquals("default", it.next());
    +        assertTrue(it.hasNext());
    +        assertEquals("default", it.next());
    +        assertEquals("default", it.next());
     
    -    observable.onError(new TestException());
    -    assertTrue(it.hasNext());
    +        observable.onError(new TestException());
    +        assertTrue(it.hasNext());
     
    -    it.next();
    -  }
    +        it.next();
    +    }
     
    -  private static class TestException extends RuntimeException {
    -    private static final long serialVersionUID = 1L;
    -  }
    +    private static class TestException extends RuntimeException {
    +        private static final long serialVersionUID = 1L;
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java b/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java
    index e33ee00de3..9e0189941d 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java
    @@ -1,97 +1,98 @@
     package rx.operators;
     
    +import static org.mockito.Mockito.*;
    +
     import org.junit.Test;
    +
     import rx.Observer;
     import rx.Subscription;
     import rx.observables.ConnectableObservable;
     import rx.subjects.PublishSubject;
     import rx.subjects.Subject;
     
    -import static org.mockito.Mockito.*;
    -
     public class OperationMulticastTest {
     
    -  @Test
    -  public void testMulticast() {
    -    Subject source = PublishSubject.create();
    +    @Test
    +    public void testMulticast() {
    +        Subject source = PublishSubject.create();
     
    -    ConnectableObservable multicasted = OperationMulticast.multicast(source,
    -        PublishSubject.create());
    +        ConnectableObservable multicasted = OperationMulticast.multicast(source,
    +                PublishSubject. create());
     
    -    @SuppressWarnings("unchecked")
    -    Observer observer = mock(Observer.class);
    -    multicasted.subscribe(observer);
    +        @SuppressWarnings("unchecked")
    +        Observer observer = mock(Observer.class);
    +        multicasted.subscribe(observer);
     
    -    source.onNext("one");
    -    source.onNext("two");
    +        source.onNext("one");
    +        source.onNext("two");
     
    -    multicasted.connect();
    +        multicasted.connect();
     
    -    source.onNext("three");
    -    source.onNext("four");
    -    source.onCompleted();
    +        source.onNext("three");
    +        source.onNext("four");
    +        source.onCompleted();
     
    -    verify(observer, never()).onNext("one");
    -    verify(observer, never()).onNext("two");
    -    verify(observer, times(1)).onNext("three");
    -    verify(observer, times(1)).onNext("four");
    -    verify(observer, times(1)).onCompleted();
    +        verify(observer, never()).onNext("one");
    +        verify(observer, never()).onNext("two");
    +        verify(observer, times(1)).onNext("three");
    +        verify(observer, times(1)).onNext("four");
    +        verify(observer, times(1)).onCompleted();
     
    -  }
    +    }
     
    -  @Test
    -  public void testMulticastConnectTwice() {
    -    Subject source = PublishSubject.create();
    +    @Test
    +    public void testMulticastConnectTwice() {
    +        Subject source = PublishSubject.create();
     
    -    ConnectableObservable multicasted = OperationMulticast.multicast(source,
    -        PublishSubject.create());
    +        ConnectableObservable multicasted = OperationMulticast.multicast(source,
    +                PublishSubject. create());
     
    -    @SuppressWarnings("unchecked")
    -    Observer observer = mock(Observer.class);
    -    multicasted.subscribe(observer);
    +        @SuppressWarnings("unchecked")
    +        Observer observer = mock(Observer.class);
    +        multicasted.subscribe(observer);
     
    -    source.onNext("one");
    +        source.onNext("one");
     
    -    multicasted.connect();
    -    multicasted.connect();
    +        multicasted.connect();
    +        multicasted.connect();
     
    -    source.onNext("two");
    -    source.onCompleted();
    +        source.onNext("two");
    +        source.onCompleted();
     
    -    verify(observer, never()).onNext("one");
    -    verify(observer, times(1)).onNext("two");
    -    verify(observer, times(1)).onCompleted();
    +        verify(observer, never()).onNext("one");
    +        verify(observer, times(1)).onNext("two");
    +        verify(observer, times(1)).onCompleted();
     
    -  }
    +    }
     
    -  @Test
    -  public void testMulticastDisconnect() {
    -    Subject source = PublishSubject.create();
    +    @Test
    +    public void testMulticastDisconnect() {
    +        Subject source = PublishSubject.create();
     
    -    ConnectableObservable multicasted = OperationMulticast.multicast(source,
    -        PublishSubject.create());
    +        ConnectableObservable multicasted = OperationMulticast.multicast(source,
    +                PublishSubject. create());
     
    -    @SuppressWarnings("unchecked")
    -    Observer observer = mock(Observer.class);
    -    multicasted.subscribe(observer);
    +        @SuppressWarnings("unchecked")
    +        Observer observer = mock(Observer.class);
    +        multicasted.subscribe(observer);
     
    -    source.onNext("one");
    +        source.onNext("one");
     
    -    Subscription connection = multicasted.connect();
    -    source.onNext("two");
    +        Subscription connection = multicasted.connect();
    +        source.onNext("two");
     
    -    connection.unsubscribe();
    -    source.onNext("three");
    +        connection.unsubscribe();
    +        source.onNext("three");
     
    -    multicasted.connect();
    -    source.onNext("four");
    -    source.onCompleted();
    +        multicasted.connect();
    +        source.onNext("four");
    +        source.onCompleted();
     
    -    verify(observer, never()).onNext("one");
    -    verify(observer, times(1)).onNext("two");
    -    verify(observer, never()).onNext("three");
    -    verify(observer, times(1)).onNext("four");
    -    verify(observer, times(1)).onCompleted();
    +        verify(observer, never()).onNext("one");
    +        verify(observer, times(1)).onNext("two");
    +        verify(observer, never()).onNext("three");
    +        verify(observer, times(1)).onNext("four");
    +        verify(observer, times(1)).onCompleted();
     
    -  }
    +    }
     }
    diff --git a/rxjava-core/src/test/java/rx/operators/OperationNextTest.java b/rxjava-core/src/test/java/rx/operators/OperationNextTest.java
    index c696a64c27..4b16458c4e 100644
    --- a/rxjava-core/src/test/java/rx/operators/OperationNextTest.java
    +++ b/rxjava-core/src/test/java/rx/operators/OperationNextTest.java
    @@ -1,6 +1,17 @@
     package rx.operators;
     
    +import static org.junit.Assert.*;
    +import static rx.operators.OperationNext.*;
    +
    +import java.util.Iterator;
    +import java.util.NoSuchElementException;
    +import java.util.concurrent.CountDownLatch;
    +import java.util.concurrent.TimeUnit;
    +import java.util.concurrent.atomic.AtomicBoolean;
    +import java.util.concurrent.atomic.AtomicInteger;
    +
     import org.junit.Test;
    +
     import rx.Observable;
     import rx.Observer;
     import rx.Subscription;
    @@ -9,273 +20,262 @@
     import rx.subjects.Subject;
     import rx.subscriptions.Subscriptions;
     
    -import java.util.Iterator;
    -import java.util.NoSuchElementException;
    -import java.util.concurrent.CountDownLatch;
    -import java.util.concurrent.TimeUnit;
    -import java.util.concurrent.atomic.AtomicBoolean;
    -import java.util.concurrent.atomic.AtomicInteger;
    +public class OperationNextTest {
     
    -import static org.junit.Assert.*;
    -import static rx.operators.OperationNext.next;
    +    private void fireOnNextInNewThread(final Subject o, final String value) {
    +        new Thread() {
    +            @Override
    +            public void run() {
    +                try {
    +                    Thread.sleep(500);
    +                } catch (InterruptedException e) {
    +                    // ignore
    +                }
    +                o.onNext(value);
    +            }
    +        }.start();
    +    }
     
    -public class OperationNextTest {
    +    private void fireOnErrorInNewThread(final Subject o) {
    +        new Thread() {
    +            @Override
    +            public void run() {
    +                try {
    +                    Thread.sleep(500);
    +                } catch (InterruptedException e) {
    +                    // ignore
    +                }
    +                o.onError(new TestException());
    +            }
    +        }.start();
    +    }
     
    -  private void fireOnNextInNewThread(final Subject o, final String value) {
    -    new Thread() {
    -      @Override
    -      public void run() {
    +    @Test
    +    public void testNext() {
    +        Subject obs = PublishSubject.create();
    +        Iterator it = next(obs).iterator();
    +        fireOnNextInNewThread(obs, "one");
    +        assertTrue(it.hasNext());
    +        assertEquals("one", it.next());
    +
    +        fireOnNextInNewThread(obs, "two");
    +        assertTrue(it.hasNext());
    +        assertEquals("two", it.next());
    +
    +        obs.onCompleted();
    +        assertFalse(it.hasNext());
             try {
    -          Thread.sleep(500);
    -        } catch (InterruptedException e) {
    -          // ignore
    +            it.next();
    +            fail("At the end of an iterator should throw a NoSuchElementException");
    +        } catch (NoSuchElementException e) {
             }
    -        o.onNext(value);
    -      }
    -    }.start();
    -  }
    -
    -  private void fireOnErrorInNewThread(final Subject o) {
    -    new Thread() {
    -      @Override
    -      public void run() {
    +
    +        // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException.
    +        assertFalse(it.hasNext());
             try {
    -          Thread.sleep(500);
    -        } catch (InterruptedException e) {
    -          // ignore
    +            it.next();
    +            fail("At the end of an iterator should throw a NoSuchElementException");
    +        } catch (NoSuchElementException e) {
             }
    -        o.onError(new TestException());
    -      }
    -    }.start();
    -  }
    -
    -
    -  @Test
    -  public void testNext() {
    -    Subject obs = PublishSubject.create();
    -    Iterator it = next(obs).iterator();
    -    fireOnNextInNewThread(obs, "one");
    -    assertTrue(it.hasNext());
    -    assertEquals("one", it.next());
    -
    -    fireOnNextInNewThread(obs, "two");
    -    assertTrue(it.hasNext());
    -    assertEquals("two", it.next());
    -
    -    obs.onCompleted();
    -    assertFalse(it.hasNext());
    -    try {
    -      it.next();
    -      fail("At the end of an iterator should throw a NoSuchElementException");
    -    } catch (NoSuchElementException e) {
         }
     
    -    // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException.
    -    assertFalse(it.hasNext());
    -    try {
    -      it.next();
    -      fail("At the end of an iterator should throw a NoSuchElementException");
    -    } catch (NoSuchElementException e) {
    -    }
    -  }
    -
    -  @Test
    -  public void testNextWithError() {
    -    Subject obs = PublishSubject.create();
    -    Iterator it = next(obs).iterator();
    -    fireOnNextInNewThread(obs, "one");
    -    assertTrue(it.hasNext());
    -    assertEquals("one", it.next());
    -
    -    fireOnErrorInNewThread(obs);
    -    try {
    -      it.hasNext();
    -      fail("Expected an TestException");
    -    } catch (TestException e) {
    +    @Test
    +    public void testNextWithError() {
    +        Subject obs = PublishSubject.create();
    +        Iterator it = next(obs).iterator();
    +        fireOnNextInNewThread(obs, "one");
    +        assertTrue(it.hasNext());
    +        assertEquals("one", it.next());
    +
    +        fireOnErrorInNewThread(obs);
    +        try {
    +            it.hasNext();
    +            fail("Expected an TestException");
    +        } catch (TestException e) {
    +        }
    +
    +        assertErrorAfterObservableFail(it);
         }
     
    -    assertErrorAfterObservableFail(it);
    -  }
    +    @Test
    +    public void testNextWithEmpty() {
    +        Observable obs = Observable. empty().observeOn(Schedulers.newThread());
    +        Iterator it = next(obs).iterator();
     
    -  @Test
    -  public void testNextWithEmpty() {
    -    Observable obs = Observable.empty().observeOn(Schedulers.newThread());
    -    Iterator it = next(obs).iterator();
    +        assertFalse(it.hasNext());
    +        try {
    +            it.next();
    +            fail("At the end of an iterator should throw a NoSuchElementException");
    +        } catch (NoSuchElementException e) {
    +        }
     
    -    assertFalse(it.hasNext());
    -    try {
    -      it.next();
    -      fail("At the end of an iterator should throw a NoSuchElementException");
    -    } catch (NoSuchElementException e) {
    +        // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException.
    +        assertFalse(it.hasNext());
    +        try {
    +            it.next();
    +            fail("At the end of an iterator should throw a NoSuchElementException");
    +        } catch (NoSuchElementException e) {
    +        }
         }
     
    -    // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException.
    -    assertFalse(it.hasNext());
    -    try {
    -      it.next();
    -      fail("At the end of an iterator should throw a NoSuchElementException");
    -    } catch (NoSuchElementException e) {
    -    }
    -  }
    -
    -  @Test
    -  public void testOnError() throws Throwable {
    -    Subject obs = PublishSubject.create();
    -    Iterator it = next(obs).iterator();
    -
    -    obs.onError(new TestException());
    -    try {
    -      it.hasNext();
    -      fail("Expected an TestException");
    -    } catch (TestException e) {
    -      // successful
    +    @Test
    +    public void testOnError() throws Throwable {
    +        Subject obs = PublishSubject.create();
    +        Iterator it = next(obs).iterator();
    +
    +        obs.onError(new TestException());
    +        try {
    +            it.hasNext();
    +            fail("Expected an TestException");
    +        } catch (TestException e) {
    +            // successful
    +        }
    +
    +        assertErrorAfterObservableFail(it);
         }
     
    -    assertErrorAfterObservableFail(it);
    -  }
    +    @Test
    +    public void testOnErrorInNewThread() {
    +        Subject obs = PublishSubject.create();
    +        Iterator it = next(obs).iterator();
     
    -  @Test
    -  public void testOnErrorInNewThread() {
    -    Subject obs = PublishSubject.create();
    -    Iterator it = next(obs).iterator();
    +        fireOnErrorInNewThread(obs);
     
    -    fireOnErrorInNewThread(obs);
    +        try {
    +            it.hasNext();
    +            fail("Expected an TestException");
    +        } catch (TestException e) {
    +            // successful
    +        }
     
    -    try {
    -      it.hasNext();
    -      fail("Expected an TestException");
    -    } catch (TestException e) {
    -      // successful
    +        assertErrorAfterObservableFail(it);
         }
     
    -    assertErrorAfterObservableFail(it);
    -  }
    -
    -  private void assertErrorAfterObservableFail(Iterator it) {
    -    // After the observable fails, hasNext and next always throw the exception.
    -    try {
    -      it.hasNext();
    -      fail("hasNext should throw a TestException");
    -    } catch (TestException e) {
    +    private void assertErrorAfterObservableFail(Iterator it) {
    +        // After the observable fails, hasNext and next always throw the exception.
    +        try {
    +            it.hasNext();
    +            fail("hasNext should throw a TestException");
    +        } catch (TestException e) {
    +        }
    +        try {
    +            it.next();
    +            fail("next should throw a TestException");
    +        } catch (TestException e) {
    +        }
         }
    -    try {
    -      it.next();
    -      fail("next should throw a TestException");
    -    } catch (TestException e) {
    +
    +    @Test
    +    public void testNextWithOnlyUsingNextMethod() {
    +        Subject obs = PublishSubject.create();
    +        Iterator it = next(obs).iterator();
    +        fireOnNextInNewThread(obs, "one");
    +        assertEquals("one", it.next());
    +
    +        fireOnNextInNewThread(obs, "two");
    +        assertEquals("two", it.next());
    +
    +        obs.onCompleted();
    +        try {
    +            it.next();
    +            fail("At the end of an iterator should throw a NoSuchElementException");
    +        } catch (NoSuchElementException e) {
    +        }
         }
    -  }
    -
    -  @Test
    -  public void testNextWithOnlyUsingNextMethod() {
    -    Subject obs = PublishSubject.create();
    -    Iterator it = next(obs).iterator();
    -    fireOnNextInNewThread(obs, "one");
    -    assertEquals("one", it.next());
    -
    -    fireOnNextInNewThread(obs, "two");
    -    assertEquals("two", it.next());
    -
    -    obs.onCompleted();
    -    try {
    -      it.next();
    -      fail("At the end of an iterator should throw a NoSuchElementException");
    -    } catch (NoSuchElementException e) {
    +
    +    @Test
    +    public void testNextWithCallingHasNextMultipleTimes() {
    +        Subject obs = PublishSubject.create();
    +        Iterator it = next(obs).iterator();
    +        fireOnNextInNewThread(obs, "one");
    +        assertTrue(it.hasNext());
    +        assertTrue(it.hasNext());
    +        assertTrue(it.hasNext());
    +        assertTrue(it.hasNext());
    +        assertEquals("one", it.next());
    +
    +        obs.onCompleted();
    +        try {
    +            it.next();
    +            fail("At the end of an iterator should throw a NoSuchElementException");
    +        } catch (NoSuchElementException e) {
    +        }
         }
    -  }
    -
    -  @Test
    -  public void testNextWithCallingHasNextMultipleTimes() {
    -    Subject obs = PublishSubject.create();
    -    Iterator it = next(obs).iterator();
    -    fireOnNextInNewThread(obs, "one");
    -    assertTrue(it.hasNext());
    -    assertTrue(it.hasNext());
    -    assertTrue(it.hasNext());
    -    assertTrue(it.hasNext());
    -    assertEquals("one", it.next());
    -
    -    obs.onCompleted();
    -    try {
    -      it.next();
    -      fail("At the end of an iterator should throw a NoSuchElementException");
    -    } catch (NoSuchElementException e) {
    +
    +    @SuppressWarnings("serial")
    +    private static class TestException extends RuntimeException {
    +
         }
    -  }
    -
    -  @SuppressWarnings("serial")
    -  private static class TestException extends RuntimeException {
    -
    -  }
    -
    -  /**
    -   * Confirm that no buffering or blocking of the Observable onNext calls occurs and it just grabs the next emitted value.
    -   * 

    - * This results in output such as => a: 1 b: 2 c: 89 - * - * @throws Throwable - */ - @Test - public void testNoBufferingOrBlockingOfSequence() throws Throwable { - final CountDownLatch finished = new CountDownLatch(1); - final int COUNT = 30; - final CountDownLatch timeHasPassed = new CountDownLatch(COUNT); - final AtomicBoolean running = new AtomicBoolean(true); - final AtomicInteger count = new AtomicInteger(0); - final Observable obs = Observable.create(new Observable.OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(final Observer o) { - new Thread(new Runnable() { - - @Override - public void run() { - try { - while (running.get()) { - o.onNext(count.incrementAndGet()); - timeHasPassed.countDown(); - } - o.onCompleted(); - } catch (Throwable e) { - o.onError(e); - } finally { - finished.countDown(); + + /** + * Confirm that no buffering or blocking of the Observable onNext calls occurs and it just grabs the next emitted value. + *

    + * This results in output such as => a: 1 b: 2 c: 89 + * + * @throws Throwable + */ + @Test + public void testNoBufferingOrBlockingOfSequence() throws Throwable { + final CountDownLatch finished = new CountDownLatch(1); + final int COUNT = 30; + final CountDownLatch timeHasPassed = new CountDownLatch(COUNT); + final AtomicBoolean running = new AtomicBoolean(true); + final AtomicInteger count = new AtomicInteger(0); + final Observable obs = Observable.create(new Observable.OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(final Observer o) { + new Thread(new Runnable() { + + @Override + public void run() { + try { + while (running.get()) { + o.onNext(count.incrementAndGet()); + timeHasPassed.countDown(); + } + o.onCompleted(); + } catch (Throwable e) { + o.onError(e); + } finally { + finished.countDown(); + } + } + }).start(); + return Subscriptions.empty(); } - } - }).start(); - return Subscriptions.empty(); - } - }); + }); - Iterator it = next(obs).iterator(); + Iterator it = next(obs).iterator(); - assertTrue(it.hasNext()); - int a = it.next(); - assertTrue(it.hasNext()); - int b = it.next(); - // we should have a different value - assertTrue("a and b should be different", a != b); + assertTrue(it.hasNext()); + int a = it.next(); + assertTrue(it.hasNext()); + int b = it.next(); + // we should have a different value + assertTrue("a and b should be different", a != b); - // wait for some time (if times out we are blocked somewhere so fail ... set very high for very slow, constrained machines) - timeHasPassed.await(8000, TimeUnit.MILLISECONDS); + // wait for some time (if times out we are blocked somewhere so fail ... set very high for very slow, constrained machines) + timeHasPassed.await(8000, TimeUnit.MILLISECONDS); - assertTrue(it.hasNext()); - int c = it.next(); + assertTrue(it.hasNext()); + int c = it.next(); - assertTrue("c should not just be the next in sequence", c != (b + 1)); - assertTrue("expected that c [" + c + "] is higher than or equal to " + COUNT, c >= COUNT); + assertTrue("c should not just be the next in sequence", c != (b + 1)); + assertTrue("expected that c [" + c + "] is higher than or equal to " + COUNT, c >= COUNT); - assertTrue(it.hasNext()); - int d = it.next(); - assertTrue(d > c); + assertTrue(it.hasNext()); + int d = it.next(); + assertTrue(d > c); - // shut down the thread - running.set(false); + // shut down the thread + running.set(false); - finished.await(); + finished.await(); - assertFalse(it.hasNext()); + assertFalse(it.hasNext()); - System.out.println("a: " + a + " b: " + b + " c: " + c); - } + System.out.println("a: " + a + " b: " + b + " c: " + c); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java b/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java index 17b5292130..75f2603df7 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java @@ -1,68 +1,69 @@ package rx.operators; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationObserveOn.*; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + import org.junit.Test; import org.mockito.InOrder; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; + import rx.Observable; import rx.Observer; import rx.concurrency.Schedulers; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.fail; -import static org.mockito.Mockito.*; -import static rx.operators.OperationObserveOn.observeOn; - public class OperationObserveOnTest { - /** - * This is testing a no-op path since it uses Schedulers.immediate() which will not do scheduling. - */ - @Test - @SuppressWarnings("unchecked") - public void testObserveOn() { - Observer observer = mock(Observer.class); - Observable.create(observeOn(Observable.from(1, 2, 3), Schedulers.immediate())).subscribe(observer); + /** + * This is testing a no-op path since it uses Schedulers.immediate() which will not do scheduling. + */ + @Test + @SuppressWarnings("unchecked") + public void testObserveOn() { + Observer observer = mock(Observer.class); + Observable.create(observeOn(Observable.from(1, 2, 3), Schedulers.immediate())).subscribe(observer); - verify(observer, times(1)).onNext(1); - verify(observer, times(1)).onNext(2); - verify(observer, times(1)).onNext(3); - verify(observer, times(1)).onCompleted(); - } + verify(observer, times(1)).onNext(1); + verify(observer, times(1)).onNext(2); + verify(observer, times(1)).onNext(3); + verify(observer, times(1)).onCompleted(); + } - @Test - @SuppressWarnings("unchecked") - public void testOrdering() throws InterruptedException { - Observable obs = Observable.from("one", null, "two", "three", "four"); + @Test + @SuppressWarnings("unchecked") + public void testOrdering() throws InterruptedException { + Observable obs = Observable.from("one", null, "two", "three", "four"); - Observer observer = mock(Observer.class); + Observer observer = mock(Observer.class); - InOrder inOrder = inOrder(observer); + InOrder inOrder = inOrder(observer); - final CountDownLatch completedLatch = new CountDownLatch(1); - doAnswer(new Answer() { + final CountDownLatch completedLatch = new CountDownLatch(1); + doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - completedLatch.countDown(); - return null; - } - }).when(observer).onCompleted(); + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + completedLatch.countDown(); + return null; + } + }).when(observer).onCompleted(); - obs.observeOn(Schedulers.threadPoolForComputation()).subscribe(observer); + obs.observeOn(Schedulers.threadPoolForComputation()).subscribe(observer); - if (!completedLatch.await(1000, TimeUnit.MILLISECONDS)) { - fail("timed out waiting"); - } + if (!completedLatch.await(1000, TimeUnit.MILLISECONDS)) { + fail("timed out waiting"); + } - inOrder.verify(observer, times(1)).onNext("one"); - inOrder.verify(observer, times(1)).onNext(null); - 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)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } + inOrder.verify(observer, times(1)).onNext("one"); + inOrder.verify(observer, times(1)).onNext(null); + 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)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java index 3de89d6a49..ba81230ccf 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java @@ -1,168 +1,168 @@ package rx.operators; +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationOnErrorResumeNextViaFunction.*; + +import java.util.concurrent.atomic.AtomicReference; + import org.junit.Test; import org.mockito.Mockito; + import rx.Observable; import rx.Observer; import rx.Subscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Func1; -import java.util.concurrent.atomic.AtomicReference; +public class OperationOnErrorResumeNextViaFunctionTest { -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction; + @Test + public void testResumeNextWithSynchronousExecution() { + final AtomicReference receivedException = new AtomicReference(); + Observable w = Observable.create(new Observable.OnSubscribeFunc() { -public class OperationOnErrorResumeNextViaFunctionTest { + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onError(new Throwable("injected failure")); + return Subscriptions.empty(); + } + }); + + Func1> resume = new Func1>() { - @Test - public void testResumeNextWithSynchronousExecution() { - final AtomicReference receivedException = new AtomicReference(); - Observable w = Observable.create(new Observable.OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(Observer observer) { - observer.onNext("one"); - observer.onError(new Throwable("injected failure")); - return Subscriptions.empty(); - } - }); - - Func1> resume = new Func1>() { - - @Override - public Observable call(Throwable t1) { - receivedException.set(t1); - return Observable.from("twoResume", "threeResume"); - } - - }; - Observable observable = Observable.create(onErrorResumeNextViaFunction(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - assertNotNull(receivedException.get()); - } - - @Test - public void testResumeNextWithAsyncExecution() { - final AtomicReference receivedException = new AtomicReference(); - Subscription s = mock(Subscription.class); - TestObservable w = new TestObservable(s, "one"); - Func1> resume = new Func1>() { - - @Override - public Observable call(Throwable t1) { - receivedException.set(t1); - return Observable.from("twoResume", "threeResume"); - } - - }; - Observable observable = Observable.create(onErrorResumeNextViaFunction(Observable.create(w), resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - w.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); + @Override + public Observable call(Throwable t1) { + receivedException.set(t1); + return Observable.from("twoResume", "threeResume"); + } + + }; + Observable observable = Observable.create(onErrorResumeNextViaFunction(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + assertNotNull(receivedException.get()); } - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - assertNotNull(receivedException.get()); - } - - /** - * Test that when a function throws an exception this is propagated through onError - */ - @Test - public void testFunctionThrowsError() { - Subscription s = mock(Subscription.class); - TestObservable w = new TestObservable(s, "one"); - Func1> resume = new Func1>() { - - @Override - public Observable call(Throwable t1) { - throw new RuntimeException("exception from function"); - } - - }; - Observable observable = Observable.create(onErrorResumeNextViaFunction(Observable.create(w), resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - w.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); + @Test + public void testResumeNextWithAsyncExecution() { + final AtomicReference receivedException = new AtomicReference(); + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one"); + Func1> resume = new Func1>() { + + @Override + public Observable call(Throwable t1) { + receivedException.set(t1); + return Observable.from("twoResume", "threeResume"); + } + + }; + Observable observable = Observable.create(onErrorResumeNextViaFunction(Observable.create(w), resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + w.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + assertNotNull(receivedException.get()); } - // we should get the "one" value before the error - verify(aObserver, times(1)).onNext("one"); + /** + * Test that when a function throws an exception this is propagated through onError + */ + @Test + public void testFunctionThrowsError() { + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one"); + Func1> resume = new Func1>() { + + @Override + public Observable call(Throwable t1) { + throw new RuntimeException("exception from function"); + } - // we should have received an onError call on the Observer since the resume function threw an exception - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, times(0)).onCompleted(); - } + }; + Observable observable = Observable.create(onErrorResumeNextViaFunction(Observable.create(w), resume)); - private static class TestObservable implements Observable.OnSubscribeFunc { + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); - final Subscription s; - final String[] values; - Thread t = null; + try { + w.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } - public TestObservable(Subscription s, String... values) { - this.s = s; - this.values = values; + // we should get the "one" value before the error + verify(aObserver, times(1)).onNext("one"); + + // we should have received an onError call on the Observer since the resume function threw an exception + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, times(0)).onCompleted(); } - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { + private static class TestObservable implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservable(Subscription s, String... values) { + this.s = s; + this.values = values; + } @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); - } - throw new RuntimeException("Forced Failure"); - } catch (Throwable e) { - observer.onError(e); - } + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + throw new RuntimeException("Forced Failure"); + } catch (Throwable e) { + observer.onError(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; } - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; } - - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java index 69b62ec495..48ef273932 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java @@ -1,127 +1,128 @@ package rx.operators; +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationOnErrorResumeNextViaObservable.*; + import org.junit.Test; import org.mockito.Mockito; + import rx.Observable; import rx.Observer; import rx.Subscription; import rx.util.functions.Func1; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable; - public class OperationOnErrorResumeNextViaObservableTest { - @Test - public void testResumeNext() { - Subscription s = mock(Subscription.class); - // Trigger failure on second element - TestObservable f = new TestObservable(s, "one", "fail", "two", "three"); - Observable w = Observable.create(f); - Observable resume = Observable.from("twoResume", "threeResume"); - Observable observable = Observable.create(onErrorResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } + @Test + public void testResumeNext() { + Subscription s = mock(Subscription.class); + // Trigger failure on second element + TestObservable f = new TestObservable(s, "one", "fail", "two", "three"); + Observable w = Observable.create(f); + Observable resume = Observable.from("twoResume", "threeResume"); + Observable observable = Observable.create(onErrorResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - } - - @Test - public void testMapResumeAsyncNext() { - Subscription sr = mock(Subscription.class); - // Trigger multiple failures - Observable w = Observable.from("one", "fail", "two", "three", "fail"); - // Resume Observable is async - TestObservable f = new TestObservable(sr, "twoResume", "threeResume"); - Observable resume = Observable.create(f); - - // Introduce map function that fails intermittently (Map does not prevent this when the observer is a - // rx.operator incl onErrorResumeNextViaObservable) - w = w.map(new Func1() { - public String call(String s) { - if ("fail".equals(s)) - throw new RuntimeException("Forced Failure"); - System.out.println("BadMapper:" + s); - return s; - } - }); - - Observable observable = Observable.create(onErrorResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); } - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - } + @Test + public void testMapResumeAsyncNext() { + Subscription sr = mock(Subscription.class); + // Trigger multiple failures + Observable w = Observable.from("one", "fail", "two", "three", "fail"); + // Resume Observable is async + TestObservable f = new TestObservable(sr, "twoResume", "threeResume"); + Observable resume = Observable.create(f); + + // Introduce map function that fails intermittently (Map does not prevent this when the observer is a + // rx.operator incl onErrorResumeNextViaObservable) + w = w.map(new Func1() { + public String call(String s) { + if ("fail".equals(s)) + throw new RuntimeException("Forced Failure"); + System.out.println("BadMapper:" + s); + return s; + } + }); - private static class TestObservable implements Observable.OnSubscribeFunc { + Observable observable = Observable.create(onErrorResumeNextViaObservable(w, resume)); - final Subscription s; - final String[] values; - Thread t = null; + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); - public TestObservable(Subscription s, String... values) { - this.s = s; - this.values = values; + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); } - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { + private static class TestObservable implements Observable.OnSubscribeFunc { - @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - if ("fail".equals(s)) - throw new RuntimeException("Forced Failure"); - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); - } - System.out.println("TestObservable onCompleted"); - observer.onCompleted(); - } catch (Throwable e) { - System.out.println("TestObservable onError: " + e); - observer.onError(e); - } + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservable(Subscription s, String... values) { + this.s = s; + this.values = values; } - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + if ("fail".equals(s)) + throw new RuntimeException("Forced Failure"); + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + System.out.println("TestObservable onCompleted"); + observer.onCompleted(); + } catch (Throwable e) { + System.out.println("TestObservable onError: " + e); + observer.onError(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } } - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java index b19e60598f..288f4f4995 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java @@ -1,130 +1,130 @@ package rx.operators; +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationOnErrorReturn.*; + +import java.util.concurrent.atomic.AtomicReference; + import org.junit.Test; import org.mockito.Mockito; + import rx.Observable; import rx.Observer; import rx.Subscription; import rx.util.functions.Func1; -import java.util.concurrent.atomic.AtomicReference; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationOnErrorReturn.onErrorReturn; - public class OperationOnErrorReturnTest { - @Test - public void testResumeNext() { - Subscription s = mock(Subscription.class); - TestObservable f = new TestObservable(s, "one"); - Observable w = Observable.create(f); - final AtomicReference capturedException = new AtomicReference(); + @Test + public void testResumeNext() { + Subscription s = mock(Subscription.class); + TestObservable f = new TestObservable(s, "one"); + Observable w = Observable.create(f); + final AtomicReference capturedException = new AtomicReference(); + + Observable observable = Observable.create(onErrorReturn(w, new Func1() { - Observable observable = Observable.create(onErrorReturn(w, new Func1() { + @Override + public String call(Throwable e) { + capturedException.set(e); + return "failure"; + } - @Override - public String call(Throwable e) { - capturedException.set(e); - return "failure"; - } + })); - })); + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("failure"); + assertNotNull(capturedException.get()); } - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("failure"); - assertNotNull(capturedException.get()); - } - - /** - * Test that when a function throws an exception this is propagated through onError - */ - @Test - public void testFunctionThrowsError() { - Subscription s = mock(Subscription.class); - TestObservable f = new TestObservable(s, "one"); - Observable w = Observable.create(f); - final AtomicReference capturedException = new AtomicReference(); - - Observable observable = Observable.create(onErrorReturn(w, new Func1() { - - @Override - public String call(Throwable e) { - capturedException.set(e); - throw new RuntimeException("exception from function"); - } - - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } + /** + * Test that when a function throws an exception this is propagated through onError + */ + @Test + public void testFunctionThrowsError() { + Subscription s = mock(Subscription.class); + TestObservable f = new TestObservable(s, "one"); + Observable w = Observable.create(f); + final AtomicReference capturedException = new AtomicReference(); + + Observable observable = Observable.create(onErrorReturn(w, new Func1() { + + @Override + public String call(Throwable e) { + capturedException.set(e); + throw new RuntimeException("exception from function"); + } - // we should get the "one" value before the error - verify(aObserver, times(1)).onNext("one"); + })); - // we should have received an onError call on the Observer since the resume function threw an exception - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, times(0)).onCompleted(); - assertNotNull(capturedException.get()); - } + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); - private static class TestObservable implements Observable.OnSubscribeFunc { + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } - final Subscription s; - final String[] values; - Thread t = null; + // we should get the "one" value before the error + verify(aObserver, times(1)).onNext("one"); - public TestObservable(Subscription s, String... values) { - this.s = s; - this.values = values; + // we should have received an onError call on the Observer since the resume function threw an exception + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, times(0)).onCompleted(); + assertNotNull(capturedException.get()); } - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { + private static class TestObservable implements Observable.OnSubscribeFunc { - @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); - } - throw new RuntimeException("Forced Failure"); - } catch (Throwable e) { - observer.onError(e); - } + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservable(Subscription s, String... values) { + this.s = s; + this.values = values; } - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + throw new RuntimeException("Forced Failure"); + } catch (Throwable e) { + observer.onError(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } } - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java index ee85ed7517..18326a306b 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java @@ -1,224 +1,225 @@ package rx.operators; +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationOnExceptionResumeNextViaObservable.*; + import org.junit.Test; import org.mockito.Mockito; + import rx.Observable; import rx.Observer; import rx.Subscription; import rx.util.functions.Func1; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationOnExceptionResumeNextViaObservable.onExceptionResumeNextViaObservable; - public class OperationOnExceptionResumeNextViaObservableTest { - @Test - public void testResumeNextWithException() { - Subscription s = mock(Subscription.class); - // Trigger failure on second element - TestObservable f = new TestObservable(s, "one", "EXCEPTION", "two", "three"); - Observable w = Observable.create(f); - Observable resume = Observable.from("twoResume", "threeResume"); - Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } + @Test + public void testResumeNextWithException() { + Subscription s = mock(Subscription.class); + // Trigger failure on second element + TestObservable f = new TestObservable(s, "one", "EXCEPTION", "two", "three"); + Observable w = Observable.create(f); + Observable resume = Observable.from("twoResume", "threeResume"); + Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testResumeNextWithRuntimeException() { - Subscription s = mock(Subscription.class); - // Trigger failure on second element - TestObservable f = new TestObservable(s, "one", "RUNTIMEEXCEPTION", "two", "three"); - Observable w = Observable.create(f); - Observable resume = Observable.from("twoResume", "threeResume"); - Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verifyNoMoreInteractions(aObserver); } - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testThrowablePassesThru() { - Subscription s = mock(Subscription.class); - // Trigger failure on second element - TestObservable f = new TestObservable(s, "one", "THROWABLE", "two", "three"); - Observable w = Observable.create(f); - Observable resume = Observable.from("twoResume", "threeResume"); - Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); + @Test + public void testResumeNextWithRuntimeException() { + Subscription s = mock(Subscription.class); + // Trigger failure on second element + TestObservable f = new TestObservable(s, "one", "RUNTIMEEXCEPTION", "two", "three"); + Observable w = Observable.create(f); + Observable resume = Observable.from("twoResume", "threeResume"); + Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verifyNoMoreInteractions(aObserver); } - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, never()).onNext("twoResume"); - verify(aObserver, never()).onNext("threeResume"); - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testErrorPassesThru() { - Subscription s = mock(Subscription.class); - // Trigger failure on second element - TestObservable f = new TestObservable(s, "one", "ERROR", "two", "three"); - Observable w = Observable.create(f); - Observable resume = Observable.from("twoResume", "threeResume"); - Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - f.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); + @Test + public void testThrowablePassesThru() { + Subscription s = mock(Subscription.class); + // Trigger failure on second element + TestObservable f = new TestObservable(s, "one", "THROWABLE", "two", "three"); + Observable w = Observable.create(f); + Observable resume = Observable.from("twoResume", "threeResume"); + Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onNext("twoResume"); + verify(aObserver, never()).onNext("threeResume"); + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verifyNoMoreInteractions(aObserver); } - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, never()).onNext("twoResume"); - verify(aObserver, never()).onNext("threeResume"); - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testMapResumeAsyncNext() { - Subscription sr = mock(Subscription.class); - // Trigger multiple failures - Observable w = Observable.from("one", "fail", "two", "three", "fail"); - // Resume Observable is async - TestObservable f = new TestObservable(sr, "twoResume", "threeResume"); - Observable resume = Observable.create(f); - - // Introduce map function that fails intermittently (Map does not prevent this when the observer is a - // rx.operator incl onErrorResumeNextViaObservable) - w = w.map(new Func1() { - public String call(String s) { - if ("fail".equals(s)) - throw new RuntimeException("Forced Failure"); - System.out.println("BadMapper:" + s); - return s; - } - }); - - Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - - try { - // if the thread gets started (which it shouldn't if it's working correctly) - if (f.t != null) { - f.t.join(); - } - } catch (InterruptedException e) { - fail(e.getMessage()); + @Test + public void testErrorPassesThru() { + Subscription s = mock(Subscription.class); + // Trigger failure on second element + TestObservable f = new TestObservable(s, "one", "ERROR", "two", "three"); + Observable w = Observable.create(f); + Observable resume = Observable.from("twoResume", "threeResume"); + Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + try { + f.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onNext("twoResume"); + verify(aObserver, never()).onNext("threeResume"); + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verifyNoMoreInteractions(aObserver); } - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, times(1)).onNext("twoResume"); - verify(aObserver, times(1)).onNext("threeResume"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } + @Test + public void testMapResumeAsyncNext() { + Subscription sr = mock(Subscription.class); + // Trigger multiple failures + Observable w = Observable.from("one", "fail", "two", "three", "fail"); + // Resume Observable is async + TestObservable f = new TestObservable(sr, "twoResume", "threeResume"); + Observable resume = Observable.create(f); + + // Introduce map function that fails intermittently (Map does not prevent this when the observer is a + // rx.operator incl onErrorResumeNextViaObservable) + w = w.map(new Func1() { + public String call(String s) { + if ("fail".equals(s)) + throw new RuntimeException("Forced Failure"); + System.out.println("BadMapper:" + s); + return s; + } + }); + + Observable observable = Observable.create(onExceptionResumeNextViaObservable(w, resume)); - private static class TestObservable implements Observable.OnSubscribeFunc { + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); - final Subscription s; - final String[] values; - Thread t = null; + try { + // if the thread gets started (which it shouldn't if it's working correctly) + if (f.t != null) { + f.t.join(); + } + } catch (InterruptedException e) { + fail(e.getMessage()); + } - public TestObservable(Subscription s, String... values) { - this.s = s; - this.values = values; + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); } - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { + private static class TestObservable implements Observable.OnSubscribeFunc { - @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - if ("EXCEPTION".equals(s)) - throw new Exception("Forced Exception"); - else if ("RUNTIMEEXCEPTION".equals(s)) - throw new RuntimeException("Forced RuntimeException"); - else if ("ERROR".equals(s)) - throw new Error("Forced Error"); - else if ("THROWABLE".equals(s)) - throw new Throwable("Forced Throwable"); - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); - } - System.out.println("TestObservable onCompleted"); - observer.onCompleted(); - } catch (Throwable e) { - System.out.println("TestObservable onError: " + e); - observer.onError(e); - } + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservable(Subscription s, String... values) { + this.s = s; + this.values = values; } - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + if ("EXCEPTION".equals(s)) + throw new Exception("Forced Exception"); + else if ("RUNTIMEEXCEPTION".equals(s)) + throw new RuntimeException("Forced RuntimeException"); + else if ("ERROR".equals(s)) + throw new Error("Forced Error"); + else if ("THROWABLE".equals(s)) + throw new Throwable("Forced Throwable"); + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + System.out.println("TestObservable onCompleted"); + observer.onCompleted(); + } catch (Throwable e) { + System.out.println("TestObservable onError: " + e); + observer.onError(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } } - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java b/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java index fd79f9e9fb..2a0959a14f 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java @@ -1,45 +1,46 @@ package rx.operators; +import static org.junit.Assert.*; + +import java.util.concurrent.atomic.AtomicInteger; + import org.junit.Test; + import rx.Observable; import rx.util.functions.Action1; import rx.util.functions.Func1; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.Assert.assertEquals; - public class OperationParallelTest { - @Test - public void testParallel() { - int NUM = 1000; - final AtomicInteger count = new AtomicInteger(); - Observable.range(1, NUM).parallel( - new Func1, Observable>() { - - @Override - public Observable call(Observable o) { - return o.map(new Func1() { - - @Override - public Integer[] call(Integer t) { - return new Integer[]{t, t * 99}; - } - - }); - } - }).toBlockingObservable().forEach(new Action1() { - - @Override - public void call(Integer[] v) { - count.incrementAndGet(); - System.out.println("V: " + v[0] + " R: " + v[1] + " Thread: " + Thread.currentThread()); - } - - }); - - // just making sure we finish and get the number we expect - assertEquals(NUM, count.get()); - } + @Test + public void testParallel() { + int NUM = 1000; + final AtomicInteger count = new AtomicInteger(); + Observable.range(1, NUM).parallel( + new Func1, Observable>() { + + @Override + public Observable call(Observable o) { + return o.map(new Func1() { + + @Override + public Integer[] call(Integer t) { + return new Integer[] { t, t * 99 }; + } + + }); + } + }).toBlockingObservable().forEach(new Action1() { + + @Override + public void call(Integer[] v) { + count.incrementAndGet(); + System.out.println("V: " + v[0] + " R: " + v[1] + " Thread: " + Thread.currentThread()); + } + + }); + + // just making sure we finish and get the number we expect + assertEquals(NUM, count.get()); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java b/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java index 5132b84219..40845167c4 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java @@ -1,114 +1,115 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationRetry.*; + +import java.util.concurrent.atomic.AtomicInteger; + import org.junit.Test; import org.mockito.InOrder; + import rx.Observable; import rx.Observer; import rx.Subscription; import rx.subscriptions.Subscriptions; -import java.util.concurrent.atomic.AtomicInteger; +public class OperationRetryTest { -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationRetry.retry; + @Test + public void testOriginFails() { + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Observable origin = Observable.create(new FuncWithErrors(2)); + origin.subscribe(observer); + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext("beginningEveryTime"); + inOrder.verify(observer, times(1)).onError(any(RuntimeException.class)); + inOrder.verify(observer, never()).onNext("onSuccessOnly"); + inOrder.verify(observer, never()).onCompleted(); + } -public class OperationRetryTest { + @Test + public void testRetryFail() { + int NUM_RETRIES = 1; + int NUM_FAILURES = 2; + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); + Observable.create(retry(origin, NUM_RETRIES)).subscribe(observer); + + InOrder inOrder = inOrder(observer); + // should show 2 attempts (first time fail, second time (1st retry) fail) + inOrder.verify(observer, times(1 + NUM_RETRIES)).onNext("beginningEveryTime"); + // should only retry once, fail again and emit onError + inOrder.verify(observer, times(1)).onError(any(RuntimeException.class)); + // no success + inOrder.verify(observer, never()).onNext("onSuccessOnly"); + inOrder.verify(observer, never()).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testRetrySuccess() { + int NUM_RETRIES = 3; + int NUM_FAILURES = 2; + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); + Observable.create(retry(origin, NUM_RETRIES)).subscribe(observer); + + InOrder inOrder = inOrder(observer); + // should show 3 attempts + inOrder.verify(observer, times(1 + NUM_FAILURES)).onNext("beginningEveryTime"); + // should have no errors + inOrder.verify(observer, never()).onError(any(Throwable.class)); + // should have a single success + inOrder.verify(observer, times(1)).onNext("onSuccessOnly"); + // should have a single successful onCompleted + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } - @Test - public void testOriginFails() { - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(2)); - origin.subscribe(observer); - - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext("beginningEveryTime"); - inOrder.verify(observer, times(1)).onError(any(RuntimeException.class)); - inOrder.verify(observer, never()).onNext("onSuccessOnly"); - inOrder.verify(observer, never()).onCompleted(); - } - - @Test - public void testRetryFail() { - int NUM_RETRIES = 1; - int NUM_FAILURES = 2; - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); - Observable.create(retry(origin, NUM_RETRIES)).subscribe(observer); - - InOrder inOrder = inOrder(observer); - // should show 2 attempts (first time fail, second time (1st retry) fail) - inOrder.verify(observer, times(1 + NUM_RETRIES)).onNext("beginningEveryTime"); - // should only retry once, fail again and emit onError - inOrder.verify(observer, times(1)).onError(any(RuntimeException.class)); - // no success - inOrder.verify(observer, never()).onNext("onSuccessOnly"); - inOrder.verify(observer, never()).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testRetrySuccess() { - int NUM_RETRIES = 3; - int NUM_FAILURES = 2; - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); - Observable.create(retry(origin, NUM_RETRIES)).subscribe(observer); - - InOrder inOrder = inOrder(observer); - // should show 3 attempts - inOrder.verify(observer, times(1 + NUM_FAILURES)).onNext("beginningEveryTime"); - // should have no errors - inOrder.verify(observer, never()).onError(any(Throwable.class)); - // should have a single success - inOrder.verify(observer, times(1)).onNext("onSuccessOnly"); - // should have a single successful onCompleted - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testInfiniteRetry() { - int NUM_FAILURES = 20; - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); - Observable.create(retry(origin)).subscribe(observer); - - InOrder inOrder = inOrder(observer); - // should show 3 attempts - inOrder.verify(observer, times(1 + NUM_FAILURES)).onNext("beginningEveryTime"); - // should have no errors - inOrder.verify(observer, never()).onError(any(Throwable.class)); - // should have a single success - inOrder.verify(observer, times(1)).onNext("onSuccessOnly"); - // should have a single successful onCompleted - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - public static class FuncWithErrors implements Observable.OnSubscribeFunc { - - private final int numFailures; - private final AtomicInteger count = new AtomicInteger(0); - - FuncWithErrors(int count) { - this.numFailures = count; + @Test + public void testInfiniteRetry() { + int NUM_FAILURES = 20; + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Observable origin = Observable.create(new FuncWithErrors(NUM_FAILURES)); + Observable.create(retry(origin)).subscribe(observer); + + InOrder inOrder = inOrder(observer); + // should show 3 attempts + inOrder.verify(observer, times(1 + NUM_FAILURES)).onNext("beginningEveryTime"); + // should have no errors + inOrder.verify(observer, never()).onError(any(Throwable.class)); + // should have a single success + inOrder.verify(observer, times(1)).onNext("onSuccessOnly"); + // should have a single successful onCompleted + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); } - @Override - public Subscription onSubscribe(Observer o) { - o.onNext("beginningEveryTime"); - if (count.incrementAndGet() <= numFailures) { - o.onError(new RuntimeException("forced failure: " + count.get())); - } else { - o.onNext("onSuccessOnly"); - o.onCompleted(); - } - return Subscriptions.empty(); + public static class FuncWithErrors implements Observable.OnSubscribeFunc { + + private final int numFailures; + private final AtomicInteger count = new AtomicInteger(0); + + FuncWithErrors(int count) { + this.numFailures = count; + } + + @Override + public Subscription onSubscribe(Observer o) { + o.onNext("beginningEveryTime"); + if (count.incrementAndGet() <= numFailures) { + o.onError(new RuntimeException("forced failure: " + count.get())); + } else { + o.onNext("onSuccessOnly"); + o.onCompleted(); + } + return Subscriptions.empty(); + } } - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java b/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java index 789fa9cf2f..7ef3906eaa 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java @@ -1,8 +1,14 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.util.concurrent.TimeUnit; + import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; + import rx.Observable; import rx.Observer; import rx.Subscription; @@ -10,82 +16,78 @@ import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; -import java.util.concurrent.TimeUnit; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - public class OperationSampleTest { - private TestScheduler scheduler; - private Observer observer; + private TestScheduler scheduler; + private Observer observer; - @Before - @SuppressWarnings("unchecked") // due to mocking - public void before() { - scheduler = new TestScheduler(); - observer = mock(Observer.class); - } + @Before + @SuppressWarnings("unchecked") + // due to mocking + public void before() { + scheduler = new TestScheduler(); + observer = mock(Observer.class); + } - @Test - public void testSample() { - Observable source = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(final Observer observer1) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer1.onNext(1L); - } - }, 1, TimeUnit.SECONDS); - scheduler.schedule(new Action0() { - @Override - public void call() { - observer1.onNext(2L); - } - }, 2, TimeUnit.SECONDS); - scheduler.schedule(new Action0() { - @Override - public void call() { - observer1.onCompleted(); - } - }, 3, TimeUnit.SECONDS); + @Test + public void testSample() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(final Observer observer1) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer1.onNext(1L); + } + }, 1, TimeUnit.SECONDS); + scheduler.schedule(new Action0() { + @Override + public void call() { + observer1.onNext(2L); + } + }, 2, TimeUnit.SECONDS); + scheduler.schedule(new Action0() { + @Override + public void call() { + observer1.onCompleted(); + } + }, 3, TimeUnit.SECONDS); - return Subscriptions.empty(); - } - }); + return Subscriptions.empty(); + } + }); - Observable sampled = Observable.create(OperationSample.sample(source, 400L, TimeUnit.MILLISECONDS, scheduler)); - sampled.subscribe(observer); + Observable sampled = Observable.create(OperationSample.sample(source, 400L, TimeUnit.MILLISECONDS, scheduler)); + sampled.subscribe(observer); - InOrder inOrder = inOrder(observer); + InOrder inOrder = inOrder(observer); - scheduler.advanceTimeTo(800L, TimeUnit.MILLISECONDS); - verify(observer, never()).onNext(any(Long.class)); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); + scheduler.advanceTimeTo(800L, TimeUnit.MILLISECONDS); + verify(observer, never()).onNext(any(Long.class)); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); - scheduler.advanceTimeTo(1200L, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext(1L); - verify(observer, never()).onNext(2L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); + scheduler.advanceTimeTo(1200L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(1L); + verify(observer, never()).onNext(2L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); - scheduler.advanceTimeTo(1600L, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext(1L); - verify(observer, never()).onNext(2L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); + scheduler.advanceTimeTo(1600L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(1L); + verify(observer, never()).onNext(2L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); - scheduler.advanceTimeTo(2000L, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(1L); - inOrder.verify(observer, times(1)).onNext(2L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); + scheduler.advanceTimeTo(2000L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(1L); + inOrder.verify(observer, times(1)).onNext(2L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); - scheduler.advanceTimeTo(3000L, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(1L); - inOrder.verify(observer, times(2)).onNext(2L); - verify(observer, times(1)).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - } + scheduler.advanceTimeTo(3000L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(1L); + inOrder.verify(observer, times(2)).onNext(2L); + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationScanTest.java b/rxjava-core/src/test/java/rx/operators/OperationScanTest.java index 691df4b0f1..5c28f9efdf 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationScanTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationScanTest.java @@ -1,102 +1,100 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationScan.*; + import org.junit.Before; import org.junit.Test; import org.mockito.MockitoAnnotations; + import rx.Observable; import rx.Observer; import rx.util.functions.Func2; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.*; -import static rx.operators.OperationScan.scan; - public class OperationScanTest { - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testScanIntegersWithInitialValue() { - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - - Observable observable = Observable.from(1, 2, 3); - - Observable m = Observable.create(scan(observable, "", new Func2() { - - @Override - public String call(String s, Integer n) { - return s + n.toString(); - } - - })); - m.subscribe(observer); - - verify(observer, never()).onError(any(Throwable.class)); - verify(observer, times(1)).onNext(""); - verify(observer, times(1)).onNext("1"); - verify(observer, times(1)).onNext("12"); - verify(observer, times(1)).onNext("123"); - verify(observer, times(4)).onNext(anyString()); - verify(observer, times(1)).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - } - - @Test - public void testScanIntegersWithoutInitialValue() { - @SuppressWarnings("unchecked") - Observer Observer = mock(Observer.class); - - Observable observable = Observable.from(1, 2, 3); - - Observable m = Observable.create(scan(observable, new Func2() { - - @Override - public Integer call(Integer t1, Integer t2) { - return t1 + t2; - } - - })); - m.subscribe(Observer); - - verify(Observer, never()).onError(any(Throwable.class)); - verify(Observer, never()).onNext(0); - verify(Observer, times(1)).onNext(1); - verify(Observer, times(1)).onNext(3); - verify(Observer, times(1)).onNext(6); - verify(Observer, times(3)).onNext(anyInt()); - verify(Observer, times(1)).onCompleted(); - verify(Observer, never()).onError(any(Throwable.class)); - } - - @Test - public void testScanIntegersWithoutInitialValueAndOnlyOneValue() { - @SuppressWarnings("unchecked") - Observer Observer = mock(Observer.class); - - Observable observable = Observable.from(1); - - Observable m = Observable.create(scan(observable, new Func2() { - - @Override - public Integer call(Integer t1, Integer t2) { - return t1 + t2; - } - - })); - m.subscribe(Observer); - - verify(Observer, never()).onError(any(Throwable.class)); - verify(Observer, never()).onNext(0); - verify(Observer, times(1)).onNext(1); - verify(Observer, times(1)).onNext(anyInt()); - verify(Observer, times(1)).onCompleted(); - verify(Observer, never()).onError(any(Throwable.class)); - } + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testScanIntegersWithInitialValue() { + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + + Observable observable = Observable.from(1, 2, 3); + + Observable m = Observable.create(scan(observable, "", new Func2() { + + @Override + public String call(String s, Integer n) { + return s + n.toString(); + } + + })); + m.subscribe(observer); + + verify(observer, never()).onError(any(Throwable.class)); + verify(observer, times(1)).onNext(""); + verify(observer, times(1)).onNext("1"); + verify(observer, times(1)).onNext("12"); + verify(observer, times(1)).onNext("123"); + verify(observer, times(4)).onNext(anyString()); + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + + @Test + public void testScanIntegersWithoutInitialValue() { + @SuppressWarnings("unchecked") + Observer Observer = mock(Observer.class); + + Observable observable = Observable.from(1, 2, 3); + + Observable m = Observable.create(scan(observable, new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + + })); + m.subscribe(Observer); + + verify(Observer, never()).onError(any(Throwable.class)); + verify(Observer, never()).onNext(0); + verify(Observer, times(1)).onNext(1); + verify(Observer, times(1)).onNext(3); + verify(Observer, times(1)).onNext(6); + verify(Observer, times(3)).onNext(anyInt()); + verify(Observer, times(1)).onCompleted(); + verify(Observer, never()).onError(any(Throwable.class)); + } + + @Test + public void testScanIntegersWithoutInitialValueAndOnlyOneValue() { + @SuppressWarnings("unchecked") + Observer Observer = mock(Observer.class); + + Observable observable = Observable.from(1); + + Observable m = Observable.create(scan(observable, new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + + })); + m.subscribe(Observer); + + verify(Observer, never()).onError(any(Throwable.class)); + verify(Observer, never()).onNext(0); + verify(Observer, times(1)).onNext(1); + verify(Observer, times(1)).onNext(anyInt()); + verify(Observer, times(1)).onCompleted(); + verify(Observer, never()).onError(any(Throwable.class)); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java index 8b1dd163fb..ed213f3d5d 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java @@ -1,98 +1,99 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationSkipLast.*; + import org.junit.Test; import org.mockito.InOrder; + import rx.Observable; import rx.Observer; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationSkipLast.skipLast; - public class OperationSkipLastTest { - @Test - public void testSkipLastEmpty() { - Observable w = Observable.empty(); - Observable observable = Observable.create(skipLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(any(String.class)); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkipLast1() { - Observable w = Observable.from("one", "two", "three"); - Observable observable = Observable.create(skipLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - InOrder inOrder = inOrder(aObserver); - observable.subscribe(aObserver); - inOrder.verify(aObserver, never()).onNext("two"); - inOrder.verify(aObserver, never()).onNext("three"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkipLast2() { - Observable w = Observable.from("one", "two"); - Observable observable = Observable.create(skipLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(any(String.class)); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkipLastWithZeroCount() { - Observable w = Observable.from("one", "two"); - Observable observable = Observable.create(skipLast(w, 0)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkipLastWithNull() { - Observable w = Observable.from("one", null, "two"); - Observable observable = Observable.create(skipLast(w, 1)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext(null); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkipLastWithNegativeCount() { - Observable w = Observable.from("one"); - Observable observable = Observable.create(skipLast(w, -1)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, never()).onNext(any(String.class)); - verify(aObserver, times(1)).onError( - any(IndexOutOfBoundsException.class)); - verify(aObserver, never()).onCompleted(); - } + @Test + public void testSkipLastEmpty() { + Observable w = Observable.empty(); + Observable observable = Observable.create(skipLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLast1() { + Observable w = Observable.from("one", "two", "three"); + Observable observable = Observable.create(skipLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + InOrder inOrder = inOrder(aObserver); + observable.subscribe(aObserver); + inOrder.verify(aObserver, never()).onNext("two"); + inOrder.verify(aObserver, never()).onNext("three"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLast2() { + Observable w = Observable.from("one", "two"); + Observable observable = Observable.create(skipLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLastWithZeroCount() { + Observable w = Observable.from("one", "two"); + Observable observable = Observable.create(skipLast(w, 0)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLastWithNull() { + Observable w = Observable.from("one", null, "two"); + Observable observable = Observable.create(skipLast(w, 1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext(null); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkipLastWithNegativeCount() { + Observable w = Observable.from("one"); + Observable observable = Observable.create(skipLast(w, -1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, times(1)).onError( + any(IndexOutOfBoundsException.class)); + verify(aObserver, never()).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java index acc351de34..9aa3985315 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java @@ -1,42 +1,43 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationSkip.*; + import org.junit.Test; + import rx.Observable; import rx.Observer; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationSkip.skip; - public class OperationSkipTest { - @Test - public void testSkip1() { - Observable w = Observable.from("one", "two", "three"); - Observable skip = Observable.create(skip(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - skip.subscribe(aObserver); - verify(aObserver, never()).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSkip2() { - Observable w = Observable.from("one", "two", "three"); - Observable skip = Observable.create(skip(w, 1)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - skip.subscribe(aObserver); - verify(aObserver, never()).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } + @Test + public void testSkip1() { + Observable w = Observable.from("one", "two", "three"); + Observable skip = Observable.create(skip(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + skip.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSkip2() { + Observable w = Observable.from("one", "two", "three"); + Observable skip = Observable.create(skip(w, 1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + skip.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java index 0c97f00c9b..17d7aced6a 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java @@ -14,89 +14,90 @@ public class OperationSkipWhileTest { - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - - private static final Func1 LESS_THAN_FIVE = new Func1() { - @Override - public Boolean call(Integer v) { - if (v == 42) throw new RuntimeException("that's not the answer to everything!"); - return v < 5; + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + + private static final Func1 LESS_THAN_FIVE = new Func1() { + @Override + public Boolean call(Integer v) { + if (v == 42) + throw new RuntimeException("that's not the answer to everything!"); + return v < 5; + } + }; + + private static final Func2 INDEX_LESS_THAN_THREE = new Func2() { + @Override + public Boolean call(Integer value, Integer index) { + return index < 3; + } + }; + + @Test + public void testSkipWithIndex() { + Observable src = Observable.from(1, 2, 3, 4, 5); + Observable.create(skipWhileWithIndex(src, INDEX_LESS_THAN_THREE)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext(4); + inOrder.verify(w, times(1)).onNext(5); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onError(any(Throwable.class)); } - }; - private static final Func2 INDEX_LESS_THAN_THREE = new Func2() { - @Override - public Boolean call(Integer value, Integer index) { - return index < 3; + @Test + public void testSkipEmpty() { + Observable src = Observable.empty(); + Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + verify(w, never()).onNext(anyInt()); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testSkipEverything() { + Observable src = Observable.from(1, 2, 3, 4, 3, 2, 1); + Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + verify(w, never()).onNext(anyInt()); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testSkipNothing() { + Observable src = Observable.from(5, 3, 1); + Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext(5); + inOrder.verify(w, times(1)).onNext(3); + inOrder.verify(w, times(1)).onNext(1); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testSkipSome() { + Observable src = Observable.from(1, 2, 3, 4, 5, 3, 1, 5); + Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, times(1)).onNext(5); + inOrder.verify(w, times(1)).onNext(3); + inOrder.verify(w, times(1)).onNext(1); + inOrder.verify(w, times(1)).onNext(5); + inOrder.verify(w, times(1)).onCompleted(); + inOrder.verify(w, never()).onError(any(Throwable.class)); + } + + @Test + public void testSkipError() { + Observable src = Observable.from(1, 2, 42, 5, 3, 1); + Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); + + InOrder inOrder = inOrder(w); + inOrder.verify(w, never()).onNext(anyInt()); + inOrder.verify(w, never()).onCompleted(); + inOrder.verify(w, times(1)).onError(any(RuntimeException.class)); } - }; - - @Test - public void testSkipWithIndex() { - Observable src = Observable.from(1, 2, 3, 4, 5); - Observable.create(skipWhileWithIndex(src, INDEX_LESS_THAN_THREE)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext(4); - inOrder.verify(w, times(1)).onNext(5); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testSkipEmpty() { - Observable src = Observable.empty(); - Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); - verify(w, never()).onNext(anyInt()); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testSkipEverything() { - Observable src = Observable.from(1, 2, 3, 4, 3, 2, 1); - Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); - verify(w, never()).onNext(anyInt()); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testSkipNothing() { - Observable src = Observable.from(5, 3, 1); - Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext(5); - inOrder.verify(w, times(1)).onNext(3); - inOrder.verify(w, times(1)).onNext(1); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testSkipSome() { - Observable src = Observable.from(1, 2, 3, 4, 5, 3, 1, 5); - Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, times(1)).onNext(5); - inOrder.verify(w, times(1)).onNext(3); - inOrder.verify(w, times(1)).onNext(1); - inOrder.verify(w, times(1)).onNext(5); - inOrder.verify(w, times(1)).onCompleted(); - inOrder.verify(w, never()).onError(any(Throwable.class)); - } - - @Test - public void testSkipError() { - Observable src = Observable.from(1, 2, 42, 5, 3, 1); - Observable.create(skipWhile(src, LESS_THAN_FIVE)).subscribe(w); - - InOrder inOrder = inOrder(w); - inOrder.verify(w, never()).onNext(anyInt()); - inOrder.verify(w, never()).onCompleted(); - inOrder.verify(w, times(1)).onError(any(RuntimeException.class)); - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java b/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java index 0c260cf8e8..d95bfdfc8a 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java @@ -1,6 +1,11 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationSubscribeOn.*; + import org.junit.Test; + import rx.Observable; import rx.Observer; import rx.Scheduler; @@ -10,31 +15,26 @@ import rx.util.functions.Action0; import rx.util.functions.Func2; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.isNull; -import static org.mockito.Mockito.*; -import static rx.operators.OperationSubscribeOn.subscribeOn; - public class OperationSubscribeOnTest { - @Test - @SuppressWarnings("unchecked") - public void testSubscribeOn() { - Observable w = Observable.from(1, 2, 3); + @Test + @SuppressWarnings("unchecked") + public void testSubscribeOn() { + Observable w = Observable.from(1, 2, 3); - Scheduler scheduler = spy(OperatorTester.forwardingScheduler(Schedulers.immediate())); + Scheduler scheduler = spy(OperatorTester.forwardingScheduler(Schedulers.immediate())); - Observer observer = mock(Observer.class); - Subscription subscription = Observable.create(subscribeOn(w, scheduler)).subscribe(observer); + Observer observer = mock(Observer.class); + Subscription subscription = Observable.create(subscribeOn(w, scheduler)).subscribe(observer); - verify(scheduler, times(1)).schedule(isNull(), any(Func2.class)); - subscription.unsubscribe(); - verify(scheduler, times(1)).schedule(any(Action0.class)); - verifyNoMoreInteractions(scheduler); + verify(scheduler, times(1)).schedule(isNull(), any(Func2.class)); + subscription.unsubscribe(); + verify(scheduler, times(1)).schedule(any(Action0.class)); + verifyNoMoreInteractions(scheduler); - verify(observer, times(1)).onNext(1); - verify(observer, times(1)).onNext(2); - verify(observer, times(1)).onNext(3); - verify(observer, times(1)).onCompleted(); - } + verify(observer, times(1)).onNext(1); + verify(observer, times(1)).onNext(2); + verify(observer, times(1)).onNext(3); + verify(observer, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSumTest.java b/rxjava-core/src/test/java/rx/operators/OperationSumTest.java index 9e826d8343..98a75fd836 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSumTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSumTest.java @@ -1,113 +1,110 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationSum.*; + import org.junit.Test; + import rx.Observable; import rx.Observer; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyDouble; -import static org.mockito.Matchers.anyFloat; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Mockito.*; -import static rx.operators.OperationSum.*; - public class OperationSumTest { - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - @SuppressWarnings("unchecked") - Observer wl = mock(Observer.class); - @SuppressWarnings("unchecked") - Observer wf = mock(Observer.class); - @SuppressWarnings("unchecked") - Observer wd = mock(Observer.class); - - @Test - public void testSumOfAFewInts() throws Throwable { - Observable src = Observable.from(1, 2, 3, 4, 5); - sum(src).subscribe(w); - - verify(w, times(1)).onNext(anyInt()); - verify(w).onNext(15); - verify(w, never()).onError(any(Throwable.class)); - verify(w, times(1)).onCompleted(); - } - - @Test - public void testEmptySum() throws Throwable { - Observable src = Observable.empty(); - sum(src).subscribe(w); - - 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 testSumOfAFewLongs() throws Throwable { - Observable src = Observable.from(1L, 2L, 3L, 4L, 5L); - sumLongs(src).subscribe(wl); - - verify(wl, times(1)).onNext(anyLong()); - verify(wl).onNext(15L); - verify(wl, never()).onError(any(Throwable.class)); - verify(wl, times(1)).onCompleted(); - } - - @Test - public void testEmptySumLongs() throws Throwable { - Observable src = Observable.empty(); - sumLongs(src).subscribe(wl); - - verify(wl, times(1)).onNext(anyLong()); - verify(wl).onNext(0L); - verify(wl, never()).onError(any(Throwable.class)); - verify(wl, times(1)).onCompleted(); - } - - @Test - public void testSumOfAFewFloats() throws Throwable { - Observable src = Observable.from(1.0f); - sumFloats(src).subscribe(wf); - - verify(wf, times(1)).onNext(anyFloat()); - verify(wf).onNext(1.0f); - verify(wf, never()).onError(any(Throwable.class)); - verify(wf, times(1)).onCompleted(); - } - - @Test - public void testEmptySumFloats() throws Throwable { - Observable src = Observable.empty(); - sumFloats(src).subscribe(wf); - - verify(wf, times(1)).onNext(anyFloat()); - verify(wf).onNext(0.0f); - verify(wf, never()).onError(any(Throwable.class)); - verify(wf, times(1)).onCompleted(); - } - - @Test - public void testSumOfAFewDoubles() throws Throwable { - Observable src = Observable.from(0.0d, 1.0d, 0.5d); - sumDoubles(src).subscribe(wd); - - verify(wd, times(1)).onNext(anyDouble()); - verify(wd).onNext(1.5d); - verify(wd, never()).onError(any(Throwable.class)); - verify(wd, times(1)).onCompleted(); - } - - @Test - public void testEmptySumDoubles() throws Throwable { - Observable src = Observable.empty(); - sumDoubles(src).subscribe(wd); - - verify(wd, times(1)).onNext(anyDouble()); - verify(wd).onNext(0.0d); - verify(wd, never()).onError(any(Throwable.class)); - verify(wd, times(1)).onCompleted(); - } + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer wl = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer wf = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer wd = mock(Observer.class); + + @Test + public void testSumOfAFewInts() throws Throwable { + Observable src = Observable.from(1, 2, 3, 4, 5); + sum(src).subscribe(w); + + verify(w, times(1)).onNext(anyInt()); + verify(w).onNext(15); + verify(w, never()).onError(any(Throwable.class)); + verify(w, times(1)).onCompleted(); + } + + @Test + public void testEmptySum() throws Throwable { + Observable src = Observable.empty(); + sum(src).subscribe(w); + + 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 testSumOfAFewLongs() throws Throwable { + Observable src = Observable.from(1L, 2L, 3L, 4L, 5L); + sumLongs(src).subscribe(wl); + + verify(wl, times(1)).onNext(anyLong()); + verify(wl).onNext(15L); + verify(wl, never()).onError(any(Throwable.class)); + verify(wl, times(1)).onCompleted(); + } + + @Test + public void testEmptySumLongs() throws Throwable { + Observable src = Observable.empty(); + sumLongs(src).subscribe(wl); + + verify(wl, times(1)).onNext(anyLong()); + verify(wl).onNext(0L); + verify(wl, never()).onError(any(Throwable.class)); + verify(wl, times(1)).onCompleted(); + } + + @Test + public void testSumOfAFewFloats() throws Throwable { + Observable src = Observable.from(1.0f); + sumFloats(src).subscribe(wf); + + verify(wf, times(1)).onNext(anyFloat()); + verify(wf).onNext(1.0f); + verify(wf, never()).onError(any(Throwable.class)); + verify(wf, times(1)).onCompleted(); + } + + @Test + public void testEmptySumFloats() throws Throwable { + Observable src = Observable.empty(); + sumFloats(src).subscribe(wf); + + verify(wf, times(1)).onNext(anyFloat()); + verify(wf).onNext(0.0f); + verify(wf, never()).onError(any(Throwable.class)); + verify(wf, times(1)).onCompleted(); + } + + @Test + public void testSumOfAFewDoubles() throws Throwable { + Observable src = Observable.from(0.0d, 1.0d, 0.5d); + sumDoubles(src).subscribe(wd); + + verify(wd, times(1)).onNext(anyDouble()); + verify(wd).onNext(1.5d); + verify(wd, never()).onError(any(Throwable.class)); + verify(wd, times(1)).onCompleted(); + } + + @Test + public void testEmptySumDoubles() throws Throwable { + Observable src = Observable.empty(); + sumDoubles(src).subscribe(wd); + + verify(wd, times(1)).onNext(anyDouble()); + verify(wd).onNext(0.0d); + verify(wd, never()).onError(any(Throwable.class)); + verify(wd, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java b/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java index 7c8c9ea21a..090e9e5d68 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java @@ -1,8 +1,14 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.util.concurrent.TimeUnit; + import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; + import rx.Observable; import rx.Observer; import rx.Subscription; @@ -10,359 +16,353 @@ import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; -import java.util.concurrent.TimeUnit; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.*; - public class OperationSwitchTest { - private TestScheduler scheduler; - private Observer observer; - - @Before - @SuppressWarnings("unchecked") - public void before() { - scheduler = new TestScheduler(); - observer = mock(Observer.class); - } - - @Test - public void testSwitchWhenOuterCompleteBeforeInner() { - Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 70, "one"); - publishNext(observer, 100, "two"); - publishCompleted(observer, 200); - return Subscriptions.empty(); - } - })); - publishCompleted(observer, 60); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(2)).onNext(anyString()); - inOrder.verify(observer, times(1)).onCompleted(); - } - - @Test - public void testSwitchWhenInnerCompleteBeforeOuter() { - Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 10, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 0, "one"); - publishNext(observer, 10, "two"); - publishCompleted(observer, 20); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 100, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 0, "three"); - publishNext(observer, 10, "four"); - publishCompleted(observer, 20); - return Subscriptions.empty(); - } - })); - publishCompleted(observer, 200); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(150, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onCompleted(); - 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"); - - scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - inOrder.verify(observer, times(1)).onCompleted(); - } - - @Test - public void testSwitchWithComplete() { - Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 60, "one"); - publishNext(observer, 100, "two"); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 200, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 0, "three"); - publishNext(observer, 100, "four"); - return Subscriptions.empty(); - } - })); - - publishCompleted(observer, 250); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("one"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(175, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("two"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(225, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("three"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("four"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - } - - @Test - public void testSwitchWithError() { - Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 50, "one"); - publishNext(observer, 100, "two"); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 200, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 0, "three"); - publishNext(observer, 100, "four"); - return Subscriptions.empty(); - } - })); - - publishError(observer, 250, new TestException()); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("one"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(175, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("two"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(225, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("three"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - verify(observer, never()).onCompleted(); - verify(observer, times(1)).onError(any(TestException.class)); - } - - @Test - public void testSwitchWithSubsequenceComplete() { - Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 50, "one"); - publishNext(observer, 100, "two"); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 130, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishCompleted(observer, 0); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 150, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 50, "three"); - return Subscriptions.empty(); - } - })); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("one"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("three"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - } - - @Test - public void testSwitchWithSubsequenceError() { - Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { - @Override - public Subscription onSubscribe(Observer> observer) { - publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 50, "one"); - publishNext(observer, 100, "two"); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 130, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishError(observer, 0, new TestException()); - return Subscriptions.empty(); - } - })); - - publishNext(observer, 150, Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 50, "three"); - return Subscriptions.empty(); - } - })); - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationSwitch.switchDo(source)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext(anyString()); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("one"); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); - inOrder.verify(observer, never()).onNext("three"); - verify(observer, never()).onCompleted(); - verify(observer, times(1)).onError(any(TestException.class)); - } - - private void publishCompleted(final Observer observer, long delay) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onCompleted(); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void publishError(final Observer observer, long delay, final Throwable error) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onError(error); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void publishNext(final Observer observer, long delay, final T value) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onNext(value); - } - }, delay, TimeUnit.MILLISECONDS); - } - - @SuppressWarnings("serial") - private class TestException extends Throwable { - } + private TestScheduler scheduler; + private Observer observer; + + @Before + @SuppressWarnings("unchecked") + public void before() { + scheduler = new TestScheduler(); + observer = mock(Observer.class); + } + + @Test + public void testSwitchWhenOuterCompleteBeforeInner() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 70, "one"); + publishNext(observer, 100, "two"); + publishCompleted(observer, 200); + return Subscriptions.empty(); + } + })); + publishCompleted(observer, 60); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(2)).onNext(anyString()); + inOrder.verify(observer, times(1)).onCompleted(); + } + + @Test + public void testSwitchWhenInnerCompleteBeforeOuter() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 10, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 0, "one"); + publishNext(observer, 10, "two"); + publishCompleted(observer, 20); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 100, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 0, "three"); + publishNext(observer, 10, "four"); + publishCompleted(observer, 20); + return Subscriptions.empty(); + } + })); + publishCompleted(observer, 200); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(150, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onCompleted(); + 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"); + + scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + inOrder.verify(observer, times(1)).onCompleted(); + } + + @Test + public void testSwitchWithComplete() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 60, "one"); + publishNext(observer, 100, "two"); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 200, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 0, "three"); + publishNext(observer, 100, "four"); + return Subscriptions.empty(); + } + })); + + publishCompleted(observer, 250); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(175, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("two"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(225, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("three"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("four"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + + @Test + public void testSwitchWithError() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 50, "one"); + publishNext(observer, 100, "two"); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 200, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 0, "three"); + publishNext(observer, 100, "four"); + return Subscriptions.empty(); + } + })); + + publishError(observer, 250, new TestException()); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(175, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("two"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(225, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("three"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(350, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + verify(observer, never()).onCompleted(); + verify(observer, times(1)).onError(any(TestException.class)); + } + + @Test + public void testSwitchWithSubsequenceComplete() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 50, "one"); + publishNext(observer, 100, "two"); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 130, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishCompleted(observer, 0); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 150, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 50, "three"); + return Subscriptions.empty(); + } + })); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("three"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + + @Test + public void testSwitchWithSubsequenceError() { + Observable> source = Observable.create(new Observable.OnSubscribeFunc>() { + @Override + public Subscription onSubscribe(Observer> observer) { + publishNext(observer, 50, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 50, "one"); + publishNext(observer, 100, "two"); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 130, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishError(observer, 0, new TestException()); + return Subscriptions.empty(); + } + })); + + publishNext(observer, 150, Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 50, "three"); + return Subscriptions.empty(); + } + })); + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationSwitch.switchDo(source)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(90, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyString()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(125, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(250, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext("three"); + verify(observer, never()).onCompleted(); + verify(observer, times(1)).onError(any(TestException.class)); + } + + private void publishCompleted(final Observer observer, long delay) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onCompleted(); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void publishError(final Observer observer, long delay, final Throwable error) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onError(error); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void publishNext(final Observer observer, long delay, final T value) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onNext(value); + } + }, delay, TimeUnit.MILLISECONDS); + } + + @SuppressWarnings("serial") + private class TestException extends Throwable { + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java b/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java index 28d7676c66..2815ea03f4 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java @@ -1,203 +1,204 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationSynchronize.*; + import org.junit.Test; import org.mockito.Mockito; + import rx.Observable; import rx.Observer; import rx.Subscription; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationSynchronize.synchronize; - public class OperationSynchronizeTest { - /** - * Ensure onCompleted can not be called after an Unsubscribe - */ - @Test - public void testOnCompletedAfterUnSubscribe() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - ws.unsubscribe(); - t.sendOnCompleted(); - - verify(w, times(1)).onNext("one"); - verify(w, Mockito.never()).onCompleted(); - } - - /** - * Ensure onNext can not be called after an Unsubscribe - */ - @Test - public void testOnNextAfterUnSubscribe() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - ws.unsubscribe(); - t.sendOnNext("two"); - - verify(w, times(1)).onNext("one"); - verify(w, Mockito.never()).onNext("two"); - } - - /** - * Ensure onError can not be called after an Unsubscribe - */ - @Test - public void testOnErrorAfterUnSubscribe() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - ws.unsubscribe(); - t.sendOnError(new RuntimeException("bad")); - - verify(w, times(1)).onNext("one"); - verify(w, Mockito.never()).onError(any(Throwable.class)); - } - - /** - * Ensure onNext can not be called after onError - */ - @Test - public void testOnNextAfterOnError() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - @SuppressWarnings("unused") - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - t.sendOnError(new RuntimeException("bad")); - t.sendOnNext("two"); - - verify(w, times(1)).onNext("one"); - verify(w, times(1)).onError(any(Throwable.class)); - verify(w, Mockito.never()).onNext("two"); - } - - /** - * Ensure onCompleted can not be called after onError - */ - @Test - public void testOnCompletedAfterOnError() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - @SuppressWarnings("unused") - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - t.sendOnError(new RuntimeException("bad")); - t.sendOnCompleted(); - - verify(w, times(1)).onNext("one"); - verify(w, times(1)).onError(any(Throwable.class)); - verify(w, Mockito.never()).onCompleted(); - } - - /** - * Ensure onNext can not be called after onCompleted - */ - @Test - public void testOnNextAfterOnCompleted() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - @SuppressWarnings("unused") - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - t.sendOnCompleted(); - t.sendOnNext("two"); - - verify(w, times(1)).onNext("one"); - verify(w, Mockito.never()).onNext("two"); - verify(w, times(1)).onCompleted(); - verify(w, Mockito.never()).onError(any(Throwable.class)); - } - - /** - * Ensure onError can not be called after onCompleted - */ - @Test - public void testOnErrorAfterOnCompleted() { - TestObservable t = new TestObservable(null); - Observable st = Observable.create(synchronize(Observable.create(t))); - - @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - @SuppressWarnings("unused") - Subscription ws = st.subscribe(w); - - t.sendOnNext("one"); - t.sendOnCompleted(); - t.sendOnError(new RuntimeException("bad")); - - verify(w, times(1)).onNext("one"); - verify(w, times(1)).onCompleted(); - verify(w, Mockito.never()).onError(any(Throwable.class)); - } - - /** - * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. - */ - private static class TestObservable implements Observable.OnSubscribeFunc { - - Observer observer = null; - - public TestObservable(Subscription s) { + /** + * Ensure onCompleted can not be called after an Unsubscribe + */ + @Test + public void testOnCompletedAfterUnSubscribe() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + ws.unsubscribe(); + t.sendOnCompleted(); + + verify(w, times(1)).onNext("one"); + verify(w, Mockito.never()).onCompleted(); + } + + /** + * Ensure onNext can not be called after an Unsubscribe + */ + @Test + public void testOnNextAfterUnSubscribe() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + ws.unsubscribe(); + t.sendOnNext("two"); + + verify(w, times(1)).onNext("one"); + verify(w, Mockito.never()).onNext("two"); + } + + /** + * Ensure onError can not be called after an Unsubscribe + */ + @Test + public void testOnErrorAfterUnSubscribe() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + ws.unsubscribe(); + t.sendOnError(new RuntimeException("bad")); + + verify(w, times(1)).onNext("one"); + verify(w, Mockito.never()).onError(any(Throwable.class)); + } + + /** + * Ensure onNext can not be called after onError + */ + @Test + public void testOnNextAfterOnError() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + @SuppressWarnings("unused") + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + t.sendOnError(new RuntimeException("bad")); + t.sendOnNext("two"); + + verify(w, times(1)).onNext("one"); + verify(w, times(1)).onError(any(Throwable.class)); + verify(w, Mockito.never()).onNext("two"); } - /* used to simulate subscription */ - public void sendOnCompleted() { - observer.onCompleted(); + /** + * Ensure onCompleted can not be called after onError + */ + @Test + public void testOnCompletedAfterOnError() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + @SuppressWarnings("unused") + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + t.sendOnError(new RuntimeException("bad")); + t.sendOnCompleted(); + + verify(w, times(1)).onNext("one"); + verify(w, times(1)).onError(any(Throwable.class)); + verify(w, Mockito.never()).onCompleted(); } - /* used to simulate subscription */ - public void sendOnNext(String value) { - observer.onNext(value); + /** + * Ensure onNext can not be called after onCompleted + */ + @Test + public void testOnNextAfterOnCompleted() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + @SuppressWarnings("unused") + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + t.sendOnCompleted(); + t.sendOnNext("two"); + + verify(w, times(1)).onNext("one"); + verify(w, Mockito.never()).onNext("two"); + verify(w, times(1)).onCompleted(); + verify(w, Mockito.never()).onError(any(Throwable.class)); } - /* used to simulate subscription */ - public void sendOnError(Throwable e) { - observer.onError(e); + /** + * Ensure onError can not be called after onCompleted + */ + @Test + public void testOnErrorAfterOnCompleted() { + TestObservable t = new TestObservable(null); + Observable st = Observable.create(synchronize(Observable.create(t))); + + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + @SuppressWarnings("unused") + Subscription ws = st.subscribe(w); + + t.sendOnNext("one"); + t.sendOnCompleted(); + t.sendOnError(new RuntimeException("bad")); + + verify(w, times(1)).onNext("one"); + verify(w, times(1)).onCompleted(); + verify(w, Mockito.never()).onError(any(Throwable.class)); } - @Override - public Subscription onSubscribe(final Observer observer) { - this.observer = observer; - return new Subscription() { + /** + * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. + */ + private static class TestObservable implements Observable.OnSubscribeFunc { + + Observer observer = null; + + public TestObservable(Subscription s) { + } + + /* used to simulate subscription */ + public void sendOnCompleted() { + observer.onCompleted(); + } + + /* used to simulate subscription */ + public void sendOnNext(String value) { + observer.onNext(value); + } + + /* used to simulate subscription */ + public void sendOnError(Throwable e) { + observer.onError(e); + } @Override - public void unsubscribe() { - // going to do nothing to pretend I'm a bad Observable that keeps allowing events to be sent + public Subscription onSubscribe(final Observer observer) { + this.observer = observer; + return new Subscription() { + + @Override + public void unsubscribe() { + // going to do nothing to pretend I'm a bad Observable that keeps allowing events to be sent + } + + }; } - }; } - - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java index 89fc932b51..2bc6ec2da8 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java @@ -1,97 +1,98 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationTakeLast.*; + import org.junit.Test; import org.mockito.InOrder; + import rx.Observable; import rx.Observer; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationTakeLast.takeLast; - public class OperationTakeLastTest { - @Test - public void testTakeLastEmpty() { - Observable w = Observable.empty(); - Observable take = Observable.create(takeLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, never()).onNext(any(String.class)); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeLast1() { - Observable w = Observable.from("one", "two", "three"); - Observable take = Observable.create(takeLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - InOrder inOrder = inOrder(aObserver); - take.subscribe(aObserver); - inOrder.verify(aObserver, times(1)).onNext("two"); - inOrder.verify(aObserver, times(1)).onNext("three"); - verify(aObserver, never()).onNext("one"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeLast2() { - Observable w = Observable.from("one"); - Observable take = Observable.create(takeLast(w, 10)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeLastWithZeroCount() { - Observable w = Observable.from("one"); - Observable take = Observable.create(takeLast(w, 0)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, never()).onNext("one"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeLastWithNull() { - Observable w = Observable.from("one", null, "three"); - Observable take = Observable.create(takeLast(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, never()).onNext("one"); - verify(aObserver, times(1)).onNext(null); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeLastWithNegativeCount() { - Observable w = Observable.from("one"); - Observable take = Observable.create(takeLast(w, -1)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, never()).onNext("one"); - verify(aObserver, times(1)).onError( - any(IndexOutOfBoundsException.class)); - verify(aObserver, never()).onCompleted(); - } + @Test + public void testTakeLastEmpty() { + Observable w = Observable.empty(); + Observable take = Observable.create(takeLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeLast1() { + Observable w = Observable.from("one", "two", "three"); + Observable take = Observable.create(takeLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + InOrder inOrder = inOrder(aObserver); + take.subscribe(aObserver); + inOrder.verify(aObserver, times(1)).onNext("two"); + inOrder.verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onNext("one"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeLast2() { + Observable w = Observable.from("one"); + Observable take = Observable.create(takeLast(w, 10)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeLastWithZeroCount() { + Observable w = Observable.from("one"); + Observable take = Observable.create(takeLast(w, 0)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeLastWithNull() { + Observable w = Observable.from("one", null, "three"); + Observable take = Observable.create(takeLast(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, times(1)).onNext(null); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testTakeLastWithNegativeCount() { + Observable w = Observable.from("one"); + Observable take = Observable.create(takeLast(w, -1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, times(1)).onError( + any(IndexOutOfBoundsException.class)); + verify(aObserver, never()).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java index b02c4925cd..57a549e752 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java @@ -1,213 +1,212 @@ package rx.operators; +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationTake.*; + +import java.util.concurrent.atomic.AtomicBoolean; + import org.junit.Test; import org.mockito.InOrder; + import rx.Observable; import rx.Observer; import rx.Subscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Func1; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.*; -import static rx.operators.OperationTake.take; - public class OperationTakeTest { - @Test - public void testTake1() { - Observable w = Observable.from("one", "two", "three"); - Observable take = Observable.create(take(w, 2)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTake2() { - Observable w = Observable.from("one", "two", "three"); - Observable take = Observable.create(take(w, 1)); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test(expected = IllegalArgumentException.class) - public void testTakeWithError() { - Observable.from(1, 2, 3).take(1).map(new Func1() { - public Integer call(Integer t1) { - throw new IllegalArgumentException("some error"); - } - }).toBlockingObservable().single(); - } - - @Test - public void testTakeWithErrorHappeningInOnNext() { - Observable w = Observable.from(1, 2, 3).take(2).map(new Func1() { - public Integer call(Integer t1) { - throw new IllegalArgumentException("some error"); - } - }); - - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - w.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError(any(IllegalArgumentException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testTakeWithErrorHappeningInTheLastOnNext() { - Observable w = Observable.from(1, 2, 3).take(1).map(new Func1() { - public Integer call(Integer t1) { - throw new IllegalArgumentException("some error"); - } - }); - - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - w.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError(any(IllegalArgumentException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testTakeDoesntLeakErrors() { - Observable source = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - observer.onNext("one"); - observer.onError(new Throwable("test failed")); - return Subscriptions.empty(); - } - }); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - - Observable.create(take(source, 1)).subscribe(aObserver); - - verify(aObserver, times(1)).onNext("one"); - // even though onError is called we take(1) so shouldn't see it - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testTakeZeroDoesntLeakError() { - final AtomicBoolean subscribed = new AtomicBoolean(false); - final AtomicBoolean unSubscribed = new AtomicBoolean(false); - Observable source = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - subscribed.set(true); - observer.onError(new Throwable("test failed")); - return new Subscription() { - @Override - public void unsubscribe() { - unSubscribed.set(true); - } - }; - } - }); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - - Observable.create(take(source, 0)).subscribe(aObserver); - assertTrue("source subscribed", subscribed.get()); - assertTrue("source unsubscribed", unSubscribed.get()); - - verify(aObserver, never()).onNext(anyString()); - // even though onError is called we take(0) so shouldn't see it - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verifyNoMoreInteractions(aObserver); - } - - @Test - public void testUnsubscribeAfterTake() { - final Subscription s = mock(Subscription.class); - TestObservableFunc f = new TestObservableFunc(s, "one", "two", "three"); - Observable w = Observable.create(f); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - Observable take = Observable.create(take(w, 1)); - take.subscribe(aObserver); - - // wait for the Observable to complete - try { - f.t.join(); - } catch (Throwable e) { - e.printStackTrace(); - fail(e.getMessage()); + @Test + public void testTake1() { + Observable w = Observable.from("one", "two", "three"); + Observable take = Observable.create(take(w, 2)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); } - System.out.println("TestObservable thread finished"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, times(1)).onCompleted(); - verify(s, times(1)).unsubscribe(); - verifyNoMoreInteractions(aObserver); - } + @Test + public void testTake2() { + Observable w = Observable.from("one", "two", "three"); + Observable take = Observable.create(take(w, 1)); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } - private static class TestObservableFunc implements Observable.OnSubscribeFunc { + @Test(expected = IllegalArgumentException.class) + public void testTakeWithError() { + Observable.from(1, 2, 3).take(1).map(new Func1() { + public Integer call(Integer t1) { + throw new IllegalArgumentException("some error"); + } + }).toBlockingObservable().single(); + } - final Subscription s; - final String[] values; - Thread t = null; + @Test + public void testTakeWithErrorHappeningInOnNext() { + Observable w = Observable.from(1, 2, 3).take(2).map(new Func1() { + public Integer call(Integer t1) { + throw new IllegalArgumentException("some error"); + } + }); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + w.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError(any(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); + } - public TestObservableFunc(Subscription s, String... values) { - this.s = s; - this.values = values; + @Test + public void testTakeWithErrorHappeningInTheLastOnNext() { + Observable w = Observable.from(1, 2, 3).take(1).map(new Func1() { + public Integer call(Integer t1) { + throw new IllegalArgumentException("some error"); + } + }); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + w.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError(any(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); } - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { + @Test + public void testTakeDoesntLeakErrors() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onError(new Throwable("test failed")); + return Subscriptions.empty(); + } + }); - @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + + Observable.create(take(source, 1)).subscribe(aObserver); + + verify(aObserver, times(1)).onNext("one"); + // even though onError is called we take(1) so shouldn't see it + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verifyNoMoreInteractions(aObserver); + } + + @Test + public void testTakeZeroDoesntLeakError() { + final AtomicBoolean subscribed = new AtomicBoolean(false); + final AtomicBoolean unSubscribed = new AtomicBoolean(false); + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + subscribed.set(true); + observer.onError(new Throwable("test failed")); + return new Subscription() { + @Override + public void unsubscribe() { + unSubscribed.set(true); + } + }; } - observer.onCompleted(); - } catch (Throwable e) { - throw new RuntimeException(e); - } + }); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + + Observable.create(take(source, 0)).subscribe(aObserver); + assertTrue("source subscribed", subscribed.get()); + assertTrue("source unsubscribed", unSubscribed.get()); + + verify(aObserver, never()).onNext(anyString()); + // even though onError is called we take(0) so shouldn't see it + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verifyNoMoreInteractions(aObserver); + } + + @Test + public void testUnsubscribeAfterTake() { + final Subscription s = mock(Subscription.class); + TestObservableFunc f = new TestObservableFunc(s, "one", "two", "three"); + Observable w = Observable.create(f); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Observable take = Observable.create(take(w, 1)); + take.subscribe(aObserver); + + // wait for the Observable to complete + try { + f.t.join(); + } catch (Throwable e) { + e.printStackTrace(); + fail(e.getMessage()); } - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; + System.out.println("TestObservable thread finished"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, times(1)).onCompleted(); + verify(s, times(1)).unsubscribe(); + verifyNoMoreInteractions(aObserver); + } + + private static class TestObservableFunc implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservableFunc(Subscription s, String... values) { + this.s = s; + this.values = values; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + observer.onCompleted(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } } - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java index 0e46af91e8..7033f9154a 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java @@ -1,164 +1,165 @@ package rx.operators; +import static org.mockito.Mockito.*; +import static rx.operators.OperationTakeUntil.*; + import org.junit.Test; + import rx.Observable; import rx.Observer; import rx.Subscription; -import static org.mockito.Mockito.*; -import static rx.operators.OperationTakeUntil.takeUntil; - public class OperationTakeUntilTest { - @Test - @SuppressWarnings("unchecked") - public void testTakeUntil() { - Subscription sSource = mock(Subscription.class); - Subscription sOther = mock(Subscription.class); - TestObservable source = new TestObservable(sSource); - TestObservable other = new TestObservable(sOther); - - Observer result = mock(Observer.class); - Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); - stringObservable.subscribe(result); - source.sendOnNext("one"); - source.sendOnNext("two"); - other.sendOnNext("three"); - source.sendOnNext("four"); - source.sendOnCompleted(); - other.sendOnCompleted(); - - verify(result, times(1)).onNext("one"); - verify(result, times(1)).onNext("two"); - verify(result, times(0)).onNext("three"); - verify(result, times(0)).onNext("four"); - verify(sSource, times(1)).unsubscribe(); - verify(sOther, times(1)).unsubscribe(); - - } - - @Test - @SuppressWarnings("unchecked") - public void testTakeUntilSourceCompleted() { - Subscription sSource = mock(Subscription.class); - Subscription sOther = mock(Subscription.class); - TestObservable source = new TestObservable(sSource); - TestObservable other = new TestObservable(sOther); - - Observer result = mock(Observer.class); - Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); - stringObservable.subscribe(result); - source.sendOnNext("one"); - source.sendOnNext("two"); - source.sendOnCompleted(); - - verify(result, times(1)).onNext("one"); - verify(result, times(1)).onNext("two"); - verify(sSource, times(1)).unsubscribe(); - verify(sOther, times(1)).unsubscribe(); - - } - - @Test - @SuppressWarnings("unchecked") - public void testTakeUntilSourceError() { - Subscription sSource = mock(Subscription.class); - Subscription sOther = mock(Subscription.class); - TestObservable source = new TestObservable(sSource); - TestObservable other = new TestObservable(sOther); - Throwable error = new Throwable(); - - Observer result = mock(Observer.class); - Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); - stringObservable.subscribe(result); - source.sendOnNext("one"); - source.sendOnNext("two"); - source.sendOnError(error); - - verify(result, times(1)).onNext("one"); - verify(result, times(1)).onNext("two"); - verify(result, times(1)).onError(error); - verify(sSource, times(1)).unsubscribe(); - verify(sOther, times(1)).unsubscribe(); - - } - - @Test - @SuppressWarnings("unchecked") - public void testTakeUntilOtherError() { - Subscription sSource = mock(Subscription.class); - Subscription sOther = mock(Subscription.class); - TestObservable source = new TestObservable(sSource); - TestObservable other = new TestObservable(sOther); - Throwable error = new Throwable(); - - Observer result = mock(Observer.class); - Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); - stringObservable.subscribe(result); - source.sendOnNext("one"); - source.sendOnNext("two"); - other.sendOnError(error); - - verify(result, times(1)).onNext("one"); - verify(result, times(1)).onNext("two"); - verify(result, times(1)).onError(error); - verify(result, times(0)).onCompleted(); - verify(sSource, times(1)).unsubscribe(); - verify(sOther, times(1)).unsubscribe(); - - } - - @Test - @SuppressWarnings("unchecked") - public void testTakeUntilOtherCompleted() { - Subscription sSource = mock(Subscription.class); - Subscription sOther = mock(Subscription.class); - TestObservable source = new TestObservable(sSource); - TestObservable other = new TestObservable(sOther); - - Observer result = mock(Observer.class); - Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); - stringObservable.subscribe(result); - source.sendOnNext("one"); - source.sendOnNext("two"); - other.sendOnCompleted(); - - verify(result, times(1)).onNext("one"); - verify(result, times(1)).onNext("two"); - verify(result, times(0)).onCompleted(); - verify(sSource, times(0)).unsubscribe(); - verify(sOther, times(0)).unsubscribe(); - - } - - private static class TestObservable implements Observable.OnSubscribeFunc { - - Observer observer = null; - Subscription s; - - public TestObservable(Subscription s) { - this.s = s; + @Test + @SuppressWarnings("unchecked") + public void testTakeUntil() { + Subscription sSource = mock(Subscription.class); + Subscription sOther = mock(Subscription.class); + TestObservable source = new TestObservable(sSource); + TestObservable other = new TestObservable(sOther); + + Observer result = mock(Observer.class); + Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); + stringObservable.subscribe(result); + source.sendOnNext("one"); + source.sendOnNext("two"); + other.sendOnNext("three"); + source.sendOnNext("four"); + source.sendOnCompleted(); + other.sendOnCompleted(); + + verify(result, times(1)).onNext("one"); + verify(result, times(1)).onNext("two"); + verify(result, times(0)).onNext("three"); + verify(result, times(0)).onNext("four"); + verify(sSource, times(1)).unsubscribe(); + verify(sOther, times(1)).unsubscribe(); + + } + + @Test + @SuppressWarnings("unchecked") + public void testTakeUntilSourceCompleted() { + Subscription sSource = mock(Subscription.class); + Subscription sOther = mock(Subscription.class); + TestObservable source = new TestObservable(sSource); + TestObservable other = new TestObservable(sOther); + + Observer result = mock(Observer.class); + Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); + stringObservable.subscribe(result); + source.sendOnNext("one"); + source.sendOnNext("two"); + source.sendOnCompleted(); + + verify(result, times(1)).onNext("one"); + verify(result, times(1)).onNext("two"); + verify(sSource, times(1)).unsubscribe(); + verify(sOther, times(1)).unsubscribe(); + } - /* used to simulate subscription */ - public void sendOnCompleted() { - observer.onCompleted(); + @Test + @SuppressWarnings("unchecked") + public void testTakeUntilSourceError() { + Subscription sSource = mock(Subscription.class); + Subscription sOther = mock(Subscription.class); + TestObservable source = new TestObservable(sSource); + TestObservable other = new TestObservable(sOther); + Throwable error = new Throwable(); + + Observer result = mock(Observer.class); + Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); + stringObservable.subscribe(result); + source.sendOnNext("one"); + source.sendOnNext("two"); + source.sendOnError(error); + + verify(result, times(1)).onNext("one"); + verify(result, times(1)).onNext("two"); + verify(result, times(1)).onError(error); + verify(sSource, times(1)).unsubscribe(); + verify(sOther, times(1)).unsubscribe(); + } - /* used to simulate subscription */ - public void sendOnNext(String value) { - observer.onNext(value); + @Test + @SuppressWarnings("unchecked") + public void testTakeUntilOtherError() { + Subscription sSource = mock(Subscription.class); + Subscription sOther = mock(Subscription.class); + TestObservable source = new TestObservable(sSource); + TestObservable other = new TestObservable(sOther); + Throwable error = new Throwable(); + + Observer result = mock(Observer.class); + Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); + stringObservable.subscribe(result); + source.sendOnNext("one"); + source.sendOnNext("two"); + other.sendOnError(error); + + verify(result, times(1)).onNext("one"); + verify(result, times(1)).onNext("two"); + verify(result, times(1)).onError(error); + verify(result, times(0)).onCompleted(); + verify(sSource, times(1)).unsubscribe(); + verify(sOther, times(1)).unsubscribe(); + } - /* used to simulate subscription */ - public void sendOnError(Throwable e) { - observer.onError(e); + @Test + @SuppressWarnings("unchecked") + public void testTakeUntilOtherCompleted() { + Subscription sSource = mock(Subscription.class); + Subscription sOther = mock(Subscription.class); + TestObservable source = new TestObservable(sSource); + TestObservable other = new TestObservable(sOther); + + Observer result = mock(Observer.class); + Observable stringObservable = takeUntil(Observable.create(source), Observable.create(other)); + stringObservable.subscribe(result); + source.sendOnNext("one"); + source.sendOnNext("two"); + other.sendOnCompleted(); + + verify(result, times(1)).onNext("one"); + verify(result, times(1)).onNext("two"); + verify(result, times(0)).onCompleted(); + verify(sSource, times(0)).unsubscribe(); + verify(sOther, times(0)).unsubscribe(); + } - @Override - public Subscription onSubscribe(final Observer observer) { - this.observer = observer; - return s; + private static class TestObservable implements Observable.OnSubscribeFunc { + + Observer observer = null; + Subscription s; + + public TestObservable(Subscription s) { + this.s = s; + } + + /* used to simulate subscription */ + public void sendOnCompleted() { + observer.onCompleted(); + } + + /* used to simulate subscription */ + public void sendOnNext(String value) { + observer.onNext(value); + } + + /* used to simulate subscription */ + public void sendOnError(Throwable e) { + observer.onError(e); + } + + @Override + public Subscription onSubscribe(final Observer observer) { + this.observer = observer; + return s; + } } - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java index 8109c818e0..dee73d2cdf 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java @@ -1,6 +1,12 @@ package rx.operators; +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationTakeWhile.*; + import org.junit.Test; + import rx.Observable; import rx.Observer; import rx.Subscription; @@ -10,195 +16,189 @@ import rx.util.functions.Func1; import rx.util.functions.Func2; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationTakeWhile.takeWhile; -import static rx.operators.OperationTakeWhile.takeWhileWithIndex; - public class OperationTakeWhileTest { - @Test - public void testTakeWhile1() { - Observable w = Observable.from(1, 2, 3); - Observable take = Observable.create(takeWhile(w, new Func1() { - @Override - public Boolean call(Integer input) { - return input < 3; - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, times(1)).onNext(1); - verify(aObserver, times(1)).onNext(2); - verify(aObserver, never()).onNext(3); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeWhileOnSubject1() { - Subject s = PublishSubject.create(); - Observable take = Observable.create(takeWhile(s, new Func1() { - @Override - public Boolean call(Integer input) { - return input < 3; - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - - s.onNext(1); - s.onNext(2); - s.onNext(3); - s.onNext(4); - s.onNext(5); - s.onCompleted(); - - verify(aObserver, times(1)).onNext(1); - verify(aObserver, times(1)).onNext(2); - verify(aObserver, never()).onNext(3); - verify(aObserver, never()).onNext(4); - verify(aObserver, never()).onNext(5); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeWhile2() { - Observable w = Observable.from("one", "two", "three"); - Observable take = Observable.create(takeWhileWithIndex(w, new Func2() { - @Override - public Boolean call(String input, Integer index) { - return index < 2; - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - take.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testTakeWhileDoesntLeakErrors() { - Observable source = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - observer.onNext("one"); - observer.onError(new Throwable("test failed")); - return Subscriptions.empty(); - } - }); - - Observable.create(takeWhile(source, new Func1() { - @Override - public Boolean call(String s) { - return false; - } - })).toBlockingObservable().last(); - } - - @Test - public void testTakeWhileProtectsPredicateCall() { - TestObservable source = new TestObservable(mock(Subscription.class), "one"); - final RuntimeException testException = new RuntimeException("test exception"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - Observable take = Observable.create(takeWhile(Observable.create(source), new Func1() { - @Override - public Boolean call(String s) { - throw testException; - } - })); - take.subscribe(aObserver); - - // wait for the Observable to complete - try { - source.t.join(); - } catch (Throwable e) { - e.printStackTrace(); - fail(e.getMessage()); + @Test + public void testTakeWhile1() { + Observable w = Observable.from(1, 2, 3); + Observable take = Observable.create(takeWhile(w, new Func1() { + @Override + public Boolean call(Integer input) { + return input < 3; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext(1); + verify(aObserver, times(1)).onNext(2); + verify(aObserver, never()).onNext(3); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); } - verify(aObserver, never()).onNext(any(String.class)); - verify(aObserver, times(1)).onError(testException); - } - - @Test - public void testUnsubscribeAfterTake() { - Subscription s = mock(Subscription.class); - TestObservable w = new TestObservable(s, "one", "two", "three"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - Observable take = Observable.create(takeWhileWithIndex(Observable.create(w), new Func2() { - @Override - public Boolean call(String s, Integer index) { - return index < 1; - } - })); - take.subscribe(aObserver); - - // wait for the Observable to complete - try { - w.t.join(); - } catch (Throwable e) { - e.printStackTrace(); - fail(e.getMessage()); + @Test + public void testTakeWhileOnSubject1() { + Subject s = PublishSubject.create(); + Observable take = Observable.create(takeWhile(s, new Func1() { + @Override + public Boolean call(Integer input) { + return input < 3; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + + s.onNext(1); + s.onNext(2); + s.onNext(3); + s.onNext(4); + s.onNext(5); + s.onCompleted(); + + verify(aObserver, times(1)).onNext(1); + verify(aObserver, times(1)).onNext(2); + verify(aObserver, never()).onNext(3); + verify(aObserver, never()).onNext(4); + verify(aObserver, never()).onNext(5); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); } - System.out.println("TestObservable thread finished"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, never()).onNext("two"); - verify(aObserver, never()).onNext("three"); - verify(s, times(1)).unsubscribe(); - } - - private static class TestObservable implements Observable.OnSubscribeFunc { + @Test + public void testTakeWhile2() { + Observable w = Observable.from("one", "two", "three"); + Observable take = Observable.create(takeWhileWithIndex(w, new Func2() { + @Override + public Boolean call(String input, Integer index) { + return index < 2; + } + })); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } - final Subscription s; - final String[] values; - Thread t = null; + @Test + public void testTakeWhileDoesntLeakErrors() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onError(new Throwable("test failed")); + return Subscriptions.empty(); + } + }); - public TestObservable(Subscription s, String... values) { - this.s = s; - this.values = values; + Observable.create(takeWhile(source, new Func1() { + @Override + public Boolean call(String s) { + return false; + } + })).toBlockingObservable().last(); } - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestObservable subscribed to ..."); - t = new Thread(new Runnable() { + @Test + public void testTakeWhileProtectsPredicateCall() { + TestObservable source = new TestObservable(mock(Subscription.class), "one"); + final RuntimeException testException = new RuntimeException("test exception"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Observable take = Observable.create(takeWhile(Observable.create(source), new Func1() { + @Override + public Boolean call(String s) { + throw testException; + } + })); + take.subscribe(aObserver); + + // wait for the Observable to complete + try { + source.t.join(); + } catch (Throwable e) { + e.printStackTrace(); + fail(e.getMessage()); + } - @Override - public void run() { - try { - System.out.println("running TestObservable thread"); - for (String s : values) { - System.out.println("TestObservable onNext: " + s); - observer.onNext(s); + verify(aObserver, never()).onNext(any(String.class)); + verify(aObserver, times(1)).onError(testException); + } + + @Test + public void testUnsubscribeAfterTake() { + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one", "two", "three"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Observable take = Observable.create(takeWhileWithIndex(Observable.create(w), new Func2() { + @Override + public Boolean call(String s, Integer index) { + return index < 1; } - observer.onCompleted(); - } catch (Throwable e) { - throw new RuntimeException(e); - } + })); + take.subscribe(aObserver); + + // wait for the Observable to complete + try { + w.t.join(); + } catch (Throwable e) { + e.printStackTrace(); + fail(e.getMessage()); + } + + System.out.println("TestObservable thread finished"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(s, times(1)).unsubscribe(); + } + + private static class TestObservable implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservable(Subscription s, String... values) { + this.s = s; + this.values = values; } - }); - System.out.println("starting TestObservable thread"); - t.start(); - System.out.println("done starting TestObservable thread"); - return s; + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + observer.onCompleted(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } } - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java b/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java index 76cac5dfe9..f5e75175d9 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java @@ -1,8 +1,14 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.util.concurrent.TimeUnit; + import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; + import rx.Observable; import rx.Observer; import rx.Subscription; @@ -10,105 +16,100 @@ import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; -import java.util.concurrent.TimeUnit; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - public class OperationThrottleFirstTest { - private TestScheduler scheduler; - private Observer observer; - - @Before - @SuppressWarnings("unchecked") - public void before() { - scheduler = new TestScheduler(); - observer = mock(Observer.class); - } - - @Test - public void testThrottlingWithCompleted() { - Observable source = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - publishNext(observer, 100, "one"); // publish as it's first - publishNext(observer, 300, "two"); // skip as it's last within the first 400 - publishNext(observer, 900, "three"); // publish - publishNext(observer, 905, "four"); // skip - publishCompleted(observer, 1000); // Should be published as soon as the timeout expires. - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationThrottleFirst.throttleFirst(source, 400, TimeUnit.MILLISECONDS, scheduler)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); - inOrder.verify(observer, times(1)).onNext("one"); - inOrder.verify(observer, times(0)).onNext("two"); - inOrder.verify(observer, times(1)).onNext("three"); - inOrder.verify(observer, times(0)).onNext("four"); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testThrottlingWithError() { - Observable source = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - Exception error = new TestException(); - publishNext(observer, 100, "one"); // Should be published since it is first - publishNext(observer, 200, "two"); // Should be skipped since onError will arrive before the timeout expires - publishError(observer, 300, error); // Should be published as soon as the timeout expires. - - return Subscriptions.empty(); - } - }); - - Observable sampled = Observable.create(OperationThrottleFirst.throttleFirst(source, 400, TimeUnit.MILLISECONDS, scheduler)); - sampled.subscribe(observer); - - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(400, TimeUnit.MILLISECONDS); - inOrder.verify(observer).onNext("one"); - inOrder.verify(observer).onError(any(TestException.class)); - inOrder.verifyNoMoreInteractions(); - } - - private void publishCompleted(final Observer observer, long delay) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onCompleted(); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void publishError(final Observer observer, long delay, final Exception error) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onError(error); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void publishNext(final Observer observer, long delay, final T value) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onNext(value); - } - }, delay, TimeUnit.MILLISECONDS); - } - - @SuppressWarnings("serial") - private class TestException extends Exception { - } + private TestScheduler scheduler; + private Observer observer; + + @Before + @SuppressWarnings("unchecked") + public void before() { + scheduler = new TestScheduler(); + observer = mock(Observer.class); + } + + @Test + public void testThrottlingWithCompleted() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + publishNext(observer, 100, "one"); // publish as it's first + publishNext(observer, 300, "two"); // skip as it's last within the first 400 + publishNext(observer, 900, "three"); // publish + publishNext(observer, 905, "four"); // skip + publishCompleted(observer, 1000); // Should be published as soon as the timeout expires. + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationThrottleFirst.throttleFirst(source, 400, TimeUnit.MILLISECONDS, scheduler)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + inOrder.verify(observer, times(0)).onNext("two"); + inOrder.verify(observer, times(1)).onNext("three"); + inOrder.verify(observer, times(0)).onNext("four"); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThrottlingWithError() { + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + Exception error = new TestException(); + publishNext(observer, 100, "one"); // Should be published since it is first + publishNext(observer, 200, "two"); // Should be skipped since onError will arrive before the timeout expires + publishError(observer, 300, error); // Should be published as soon as the timeout expires. + + return Subscriptions.empty(); + } + }); + + Observable sampled = Observable.create(OperationThrottleFirst.throttleFirst(source, 400, TimeUnit.MILLISECONDS, scheduler)); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(400, TimeUnit.MILLISECONDS); + inOrder.verify(observer).onNext("one"); + inOrder.verify(observer).onError(any(TestException.class)); + inOrder.verifyNoMoreInteractions(); + } + + private void publishCompleted(final Observer observer, long delay) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onCompleted(); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void publishError(final Observer observer, long delay, final Exception error) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onError(error); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void publishNext(final Observer observer, long delay, final T value) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onNext(value); + } + }, delay, TimeUnit.MILLISECONDS); + } + + @SuppressWarnings("serial") + private class TestException extends Exception { + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java b/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java index 6ce6504b05..ebbd27eb7a 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java @@ -1,60 +1,60 @@ package rx.operators; +import static org.mockito.Mockito.*; + +import java.util.concurrent.TimeUnit; + import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; + import rx.Observable; import rx.Observer; import rx.concurrency.TestScheduler; import rx.subjects.PublishSubject; import rx.util.TimeInterval; -import java.util.concurrent.TimeUnit; - -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.times; - public class OperationTimeIntervalTest { - private static final TimeUnit TIME_UNIT = TimeUnit.MILLISECONDS; - - @Mock - private Observer> observer; - - private TestScheduler testScheduler; - private PublishSubject subject; - private Observable> observable; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - testScheduler = new TestScheduler(); - subject = PublishSubject.create(); - observable = subject.timeInterval(testScheduler); - } - - @Test - public void testTimeInterval() { - InOrder inOrder = inOrder(observer); - observable.subscribe(observer); - - testScheduler.advanceTimeBy(1000, TIME_UNIT); - subject.onNext(1); - testScheduler.advanceTimeBy(2000, TIME_UNIT); - subject.onNext(2); - testScheduler.advanceTimeBy(3000, TIME_UNIT); - subject.onNext(3); - subject.onCompleted(); - - inOrder.verify(observer, times(1)).onNext( - new TimeInterval(1000, 1)); - inOrder.verify(observer, times(1)).onNext( - new TimeInterval(2000, 2)); - inOrder.verify(observer, times(1)).onNext( - new TimeInterval(3000, 3)); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } + private static final TimeUnit TIME_UNIT = TimeUnit.MILLISECONDS; + + @Mock + private Observer> observer; + + private TestScheduler testScheduler; + private PublishSubject subject; + private Observable> observable; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + testScheduler = new TestScheduler(); + subject = PublishSubject.create(); + observable = subject.timeInterval(testScheduler); + } + + @Test + public void testTimeInterval() { + InOrder inOrder = inOrder(observer); + observable.subscribe(observer); + + testScheduler.advanceTimeBy(1000, TIME_UNIT); + subject.onNext(1); + testScheduler.advanceTimeBy(2000, TIME_UNIT); + subject.onNext(2); + testScheduler.advanceTimeBy(3000, TIME_UNIT); + subject.onNext(3); + subject.onCompleted(); + + inOrder.verify(observer, times(1)).onNext( + new TimeInterval(1000, 1)); + inOrder.verify(observer, times(1)).onNext( + new TimeInterval(2000, 2)); + inOrder.verify(observer, times(1)).onNext( + new TimeInterval(3000, 3)); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java index f3891128e1..a2f4daa711 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java @@ -1,47 +1,48 @@ package rx.operators; -import org.junit.Test; -import rx.Observer; -import rx.Subscription; +import static org.mockito.Mockito.*; import java.util.concurrent.Future; -import static org.mockito.Mockito.*; -import static rx.operators.OperationToObservableFuture.ToObservableFuture; +import org.junit.Test; + +import rx.Observer; +import rx.Subscription; +import rx.operators.OperationToObservableFuture.ToObservableFuture; public class OperationToObservableFutureTest { - @Test - public void testSuccess() throws Exception { - Future future = mock(Future.class); - Object value = new Object(); - when(future.get()).thenReturn(value); - ToObservableFuture ob = new ToObservableFuture(future); - Observer o = mock(Observer.class); - - Subscription sub = ob.onSubscribe(o); - sub.unsubscribe(); - - verify(o, times(1)).onNext(value); - verify(o, times(1)).onCompleted(); - verify(o, never()).onError(null); - verify(future, never()).cancel(true); - } - - @Test - public void testFailure() throws Exception { - Future future = mock(Future.class); - RuntimeException e = new RuntimeException(); - when(future.get()).thenThrow(e); - ToObservableFuture ob = new ToObservableFuture(future); - Observer o = mock(Observer.class); - - Subscription sub = ob.onSubscribe(o); - sub.unsubscribe(); - - verify(o, never()).onNext(null); - verify(o, never()).onCompleted(); - verify(o, times(1)).onError(e); - verify(future, never()).cancel(true); - } + @Test + public void testSuccess() throws Exception { + Future future = mock(Future.class); + Object value = new Object(); + when(future.get()).thenReturn(value); + ToObservableFuture ob = new ToObservableFuture(future); + Observer o = mock(Observer.class); + + Subscription sub = ob.onSubscribe(o); + sub.unsubscribe(); + + verify(o, times(1)).onNext(value); + verify(o, times(1)).onCompleted(); + verify(o, never()).onError(null); + verify(future, never()).cancel(true); + } + + @Test + public void testFailure() throws Exception { + Future future = mock(Future.class); + RuntimeException e = new RuntimeException(); + when(future.get()).thenThrow(e); + ToObservableFuture ob = new ToObservableFuture(future); + Observer o = mock(Observer.class); + + Subscription sub = ob.onSubscribe(o); + sub.unsubscribe(); + + verify(o, never()).onNext(null); + verify(o, never()).onCompleted(); + verify(o, times(1)).onError(e); + verify(future, never()).cancel(true); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java index a24cb92694..f99bb27428 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java @@ -1,32 +1,30 @@ package rx.operators; -import org.junit.Ignore; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationToObservableIterable.*; + +import java.util.Arrays; + import org.junit.Test; import org.mockito.Mockito; + import rx.Observable; import rx.Observer; -import java.util.Arrays; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static rx.operators.OperationToObservableIterable.toObservableIterable; - public class OperationToObservableIterableTest { - @Test - public void testIterable() { - Observable observable = Observable.create(toObservableIterable(Arrays. asList("one", "two", "three"))); + @Test + public void testIterable() { + Observable observable = Observable.create(toObservableIterable(Arrays. asList("one", "two", "three"))); - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java index 12e47aa080..58d1456a80 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java @@ -1,53 +1,54 @@ package rx.operators; -import org.junit.Test; -import org.mockito.Mockito; -import rx.Observable; -import rx.Observer; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationToObservableList.*; import java.util.Arrays; import java.util.List; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationToObservableList.toObservableList; +import org.junit.Test; +import org.mockito.Mockito; + +import rx.Observable; +import rx.Observer; public class OperationToObservableListTest { - @Test - public void testList() { - Observable w = Observable.from("one", "two", "three"); - Observable> observable = Observable.create(toObservableList(w)); - - @SuppressWarnings("unchecked") - Observer> aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(Arrays.asList("one", "two", "three")); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testListMultipleObservers() { - Observable w = Observable.from("one", "two", "three"); - Observable> observable = Observable.create(toObservableList(w)); - - @SuppressWarnings("unchecked") - Observer> o1 = mock(Observer.class); - observable.subscribe(o1); - - @SuppressWarnings("unchecked") - Observer> o2 = mock(Observer.class); - observable.subscribe(o2); - - List expected = Arrays.asList("one", "two", "three"); - - verify(o1, times(1)).onNext(expected); - verify(o1, Mockito.never()).onError(any(Throwable.class)); - verify(o1, times(1)).onCompleted(); - - verify(o2, times(1)).onNext(expected); - verify(o2, Mockito.never()).onError(any(Throwable.class)); - verify(o2, times(1)).onCompleted(); - } + @Test + public void testList() { + Observable w = Observable.from("one", "two", "three"); + Observable> observable = Observable.create(toObservableList(w)); + + @SuppressWarnings("unchecked") + Observer> aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(Arrays.asList("one", "two", "three")); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testListMultipleObservers() { + Observable w = Observable.from("one", "two", "three"); + Observable> observable = Observable.create(toObservableList(w)); + + @SuppressWarnings("unchecked") + Observer> o1 = mock(Observer.class); + observable.subscribe(o1); + + @SuppressWarnings("unchecked") + Observer> o2 = mock(Observer.class); + observable.subscribe(o2); + + List expected = Arrays.asList("one", "two", "three"); + + verify(o1, times(1)).onNext(expected); + verify(o1, Mockito.never()).onError(any(Throwable.class)); + verify(o1, times(1)).onCompleted(); + + verify(o2, times(1)).onNext(expected); + verify(o2, Mockito.never()).onError(any(Throwable.class)); + verify(o2, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java index 11b6c9ff6a..9dd78943a5 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java @@ -1,50 +1,51 @@ package rx.operators; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationToObservableSortedList.*; + +import java.util.Arrays; +import java.util.List; + import org.junit.Test; import org.mockito.Mockito; + import rx.Observable; import rx.Observer; import rx.util.functions.Func2; -import java.util.Arrays; -import java.util.List; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; -import static rx.operators.OperationToObservableSortedList.toSortedList; - public class OperationToObservableSortedListTest { - @Test - public void testSortedList() { - Observable w = Observable.from(1, 3, 2, 5, 4); - Observable> observable = Observable.create(toSortedList(w)); - - @SuppressWarnings("unchecked") - Observer> aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(Arrays.asList(1, 2, 3, 4, 5)); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testSortedListWithCustomFunction() { - Observable w = Observable.from(1, 3, 2, 5, 4); - Observable> observable = Observable.create(toSortedList(w, new Func2() { - - @Override - public Integer call(Integer t1, Integer t2) { - return t2 - t1; - } - - })); - - @SuppressWarnings("unchecked") - Observer> aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext(Arrays.asList(5, 4, 3, 2, 1)); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } + @Test + public void testSortedList() { + Observable w = Observable.from(1, 3, 2, 5, 4); + Observable> observable = Observable.create(toSortedList(w)); + + @SuppressWarnings("unchecked") + Observer> aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(Arrays.asList(1, 2, 3, 4, 5)); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSortedListWithCustomFunction() { + Observable w = Observable.from(1, 3, 2, 5, 4); + Observable> observable = Observable.create(toSortedList(w, new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t2 - t1; + } + + })); + + @SuppressWarnings("unchecked") + Observer> aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(Arrays.asList(5, 4, 3, 2, 1)); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java b/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java index 8a552f534f..71a749a0ae 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java @@ -1,7 +1,15 @@ package rx.operators; +import static org.junit.Assert.*; +import static rx.operators.OperationWindow.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + import org.junit.Before; import org.junit.Test; + import rx.Observable; import rx.Observer; import rx.Subscription; @@ -16,300 +24,292 @@ import rx.util.functions.Func0; import rx.util.functions.Func1; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; +public class OperationWindowTest { -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static rx.operators.OperationWindow.window; + private TestScheduler scheduler; -public class OperationWindowTest { + @Before + public void before() { + scheduler = new TestScheduler(); + } + + private static List> toLists(Observable> observable) { + final List list = new ArrayList(); + final List> lists = new ArrayList>(); + + observable.subscribe(new Action1>() { + @Override + public void call(Observable tObservable) { + tObservable.subscribe(new Action1() { + @Override + public void call(T t) { + list.add(t); + } + }); + lists.add(new ArrayList(list)); + list.clear(); + } + }); + return lists; + } + + @Test + public void testNonOverlappingWindows() { + Observable subject = Observable.from("one", "two", "three", "four", "five"); + Observable> windowed = Observable.create(window(subject, 3)); + + List> windows = toLists(windowed); + + assertEquals(2, windows.size()); + assertEquals(list("one", "two", "three"), windows.get(0)); + assertEquals(list("four", "five"), windows.get(1)); + } + + @Test + public void testSkipAndCountGaplessEindows() { + Observable subject = Observable.from("one", "two", "three", "four", "five"); + Observable> windowed = Observable.create(window(subject, 3, 3)); + + List> windows = toLists(windowed); + + assertEquals(2, windows.size()); + assertEquals(list("one", "two", "three"), windows.get(0)); + assertEquals(list("four", "five"), windows.get(1)); + } + + @Test + public void testOverlappingWindows() { + Observable subject = Observable.from("zero", "one", "two", "three", "four", "five"); + Observable> windowed = Observable.create(window(subject, 3, 1)); + + List> windows = toLists(windowed); + + assertEquals(6, windows.size()); + assertEquals(list("zero", "one", "two"), windows.get(0)); + assertEquals(list("one", "two", "three"), windows.get(1)); + assertEquals(list("two", "three", "four"), windows.get(2)); + assertEquals(list("three", "four", "five"), windows.get(3)); + assertEquals(list("four", "five"), windows.get(4)); + assertEquals(list("five"), windows.get(5)); + } + + @Test + public void testSkipAndCountWindowsWithGaps() { + Observable subject = Observable.from("one", "two", "three", "four", "five"); + Observable> windowed = Observable.create(window(subject, 2, 3)); - private TestScheduler scheduler; - - @Before - public void before() { - scheduler = new TestScheduler(); - } - - private static List> toLists(Observable> observable) { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); - - observable.subscribe(new Action1>() { - @Override - public void call(Observable tObservable) { - tObservable.subscribe(new Action1() { - @Override - public void call(T t) { - list.add(t); - } + List> windows = toLists(windowed); + + assertEquals(2, windows.size()); + assertEquals(list("one", "two"), windows.get(0)); + assertEquals(list("four", "five"), windows.get(1)); + } + + @Test + public void testTimedAndCount() { + final List list = new ArrayList(); + final List> lists = new ArrayList>(); + + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 10); + push(observer, "two", 90); + push(observer, "three", 110); + push(observer, "four", 190); + push(observer, "five", 210); + complete(observer, 250); + return Subscriptions.empty(); + } + }); + + Observable> windowed = Observable.create(window(source, 100, TimeUnit.MILLISECONDS, 2, scheduler)); + windowed.subscribe(observeWindow(list, lists)); + + scheduler.advanceTimeTo(100, TimeUnit.MILLISECONDS); + assertEquals(1, lists.size()); + assertEquals(lists.get(0), list("one", "two")); + + scheduler.advanceTimeTo(200, TimeUnit.MILLISECONDS); + assertEquals(2, lists.size()); + assertEquals(lists.get(1), list("three", "four")); + + scheduler.advanceTimeTo(300, TimeUnit.MILLISECONDS); + assertEquals(3, lists.size()); + assertEquals(lists.get(2), list("five")); + } + + @Test + public void testTimed() { + final List list = new ArrayList(); + final List> lists = new ArrayList>(); + + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 98); + push(observer, "two", 99); + push(observer, "three", 100); + push(observer, "four", 101); + push(observer, "five", 102); + complete(observer, 150); + return Subscriptions.empty(); + } }); - lists.add(new ArrayList(list)); - list.clear(); - } - }); - return lists; - } - - @Test - public void testNonOverlappingWindows() { - Observable subject = Observable.from("one", "two", "three", "four", "five"); - Observable> windowed = Observable.create(window(subject, 3)); - - List> windows = toLists(windowed); - - assertEquals(2, windows.size()); - assertEquals(list("one", "two", "three"), windows.get(0)); - assertEquals(list("four", "five"), windows.get(1)); - } - - @Test - public void testSkipAndCountGaplessEindows() { - Observable subject = Observable.from("one", "two", "three", "four", "five"); - Observable> windowed = Observable.create(window(subject, 3, 3)); - - List> windows = toLists(windowed); - - assertEquals(2, windows.size()); - assertEquals(list("one", "two", "three"), windows.get(0)); - assertEquals(list("four", "five"), windows.get(1)); - } - - @Test - public void testOverlappingWindows() { - Observable subject = Observable.from("zero", "one", "two", "three", "four", "five"); - Observable> windowed = Observable.create(window(subject, 3, 1)); - - List> windows = toLists(windowed); - - assertEquals(6, windows.size()); - assertEquals(list("zero", "one", "two"), windows.get(0)); - assertEquals(list("one", "two", "three"), windows.get(1)); - assertEquals(list("two", "three", "four"), windows.get(2)); - assertEquals(list("three", "four", "five"), windows.get(3)); - assertEquals(list("four", "five"), windows.get(4)); - assertEquals(list("five"), windows.get(5)); - } - - @Test - public void testSkipAndCountWindowsWithGaps() { - Observable subject = Observable.from("one", "two", "three", "four", "five"); - Observable> windowed = Observable.create(window(subject, 2, 3)); - - List> windows = toLists(windowed); - - assertEquals(2, windows.size()); - assertEquals(list("one", "two"), windows.get(0)); - assertEquals(list("four", "five"), windows.get(1)); - } - - @Test - public void testTimedAndCount() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); - - Observable source = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 10); - push(observer, "two", 90); - push(observer, "three", 110); - push(observer, "four", 190); - push(observer, "five", 210); - complete(observer, 250); - return Subscriptions.empty(); - } - }); - - Observable> windowed = Observable.create(window(source, 100, TimeUnit.MILLISECONDS, 2, scheduler)); - windowed.subscribe(observeWindow(list, lists)); - - scheduler.advanceTimeTo(100, TimeUnit.MILLISECONDS); - assertEquals(1, lists.size()); - assertEquals(lists.get(0), list("one", "two")); - - scheduler.advanceTimeTo(200, TimeUnit.MILLISECONDS); - assertEquals(2, lists.size()); - assertEquals(lists.get(1), list("three", "four")); - - scheduler.advanceTimeTo(300, TimeUnit.MILLISECONDS); - assertEquals(3, lists.size()); - assertEquals(lists.get(2), list("five")); - } - - @Test - public void testTimed() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); - - Observable source = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 98); - push(observer, "two", 99); - push(observer, "three", 100); - push(observer, "four", 101); - push(observer, "five", 102); - complete(observer, 150); - return Subscriptions.empty(); - } - }); - - Observable> windowed = Observable.create(window(source, 100, TimeUnit.MILLISECONDS, scheduler)); - windowed.subscribe(observeWindow(list, lists)); - - scheduler.advanceTimeTo(101, TimeUnit.MILLISECONDS); - assertEquals(1, lists.size()); - assertEquals(lists.get(0), list("one", "two", "three")); - - scheduler.advanceTimeTo(201, TimeUnit.MILLISECONDS); - assertEquals(2, lists.size()); - assertEquals(lists.get(1), list("four", "five")); - } - - @Test - public void testObservableBasedOpenerAndCloser() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); - - Observable source = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 10); - push(observer, "two", 60); - push(observer, "three", 110); - push(observer, "four", 160); - push(observer, "five", 210); - complete(observer, 500); - return Subscriptions.empty(); - } - }); - - Observable openings = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, Openings.create(), 50); - push(observer, Openings.create(), 200); - complete(observer, 250); - return Subscriptions.empty(); - } - }); - - Func1> closer = new Func1>() { - @Override - public Observable call(Opening opening) { - return Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, Closings.create(), 100); - complete(observer, 101); - return Subscriptions.empty(); - } + + Observable> windowed = Observable.create(window(source, 100, TimeUnit.MILLISECONDS, scheduler)); + windowed.subscribe(observeWindow(list, lists)); + + scheduler.advanceTimeTo(101, TimeUnit.MILLISECONDS); + assertEquals(1, lists.size()); + assertEquals(lists.get(0), list("one", "two", "three")); + + scheduler.advanceTimeTo(201, TimeUnit.MILLISECONDS); + assertEquals(2, lists.size()); + assertEquals(lists.get(1), list("four", "five")); + } + + @Test + public void testObservableBasedOpenerAndCloser() { + final List list = new ArrayList(); + final List> lists = new ArrayList>(); + + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 10); + push(observer, "two", 60); + push(observer, "three", 110); + push(observer, "four", 160); + push(observer, "five", 210); + complete(observer, 500); + return Subscriptions.empty(); + } }); - } - }; - - Observable> windowed = Observable.create(window(source, openings, closer)); - windowed.subscribe(observeWindow(list, lists)); - - scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); - assertEquals(2, lists.size()); - assertEquals(lists.get(0), list("two", "three")); - assertEquals(lists.get(1), list("five")); - } - - @Test - public void testObservableBasedCloser() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); - - Observable source = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, "one", 10); - push(observer, "two", 60); - push(observer, "three", 110); - push(observer, "four", 160); - push(observer, "five", 210); - complete(observer, 250); - return Subscriptions.empty(); - } - }); - - Func0> closer = new Func0>() { - @Override - public Observable call() { - return Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - push(observer, Closings.create(), 100); - complete(observer, 101); - return Subscriptions.empty(); - } + + Observable openings = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, Openings.create(), 50); + push(observer, Openings.create(), 200); + complete(observer, 250); + return Subscriptions.empty(); + } }); - } - }; - - Observable> windowed = Observable.create(window(source, closer)); - windowed.subscribe(observeWindow(list, lists)); - - scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); - assertEquals(3, lists.size()); - assertEquals(lists.get(0), list("one", "two")); - assertEquals(lists.get(1), list("three", "four")); - assertEquals(lists.get(2), list("five")); - } - - private List list(String... args) { - List list = new ArrayList(); - for (String arg : args) { - list.add(arg); + + Func1> closer = new Func1>() { + @Override + public Observable call(Opening opening) { + return Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, Closings.create(), 100); + complete(observer, 101); + return Subscriptions.empty(); + } + }); + } + }; + + Observable> windowed = Observable.create(window(source, openings, closer)); + windowed.subscribe(observeWindow(list, lists)); + + scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); + assertEquals(2, lists.size()); + assertEquals(lists.get(0), list("two", "three")); + assertEquals(lists.get(1), list("five")); } - return list; - } - - private void push(final Observer observer, final T value, int delay) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onNext(value); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private void complete(final Observer observer, int delay) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onCompleted(); - } - }, delay, TimeUnit.MILLISECONDS); - } - - private Action1> observeWindow(final List list, final List> lists) { - return new Action1>() { - @Override - public void call(Observable stringObservable) { - stringObservable.subscribe(new Observer() { - @Override - public void onCompleted() { - lists.add(new ArrayList(list)); - list.clear(); - } - - @Override - public void onError(Throwable e) { - fail(e.getMessage()); - } - - @Override - public void onNext(String args) { - list.add(args); - } + + @Test + public void testObservableBasedCloser() { + final List list = new ArrayList(); + final List> lists = new ArrayList>(); + + Observable source = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, "one", 10); + push(observer, "two", 60); + push(observer, "three", 110); + push(observer, "four", 160); + push(observer, "five", 210); + complete(observer, 250); + return Subscriptions.empty(); + } }); - } - }; - } + + Func0> closer = new Func0>() { + @Override + public Observable call() { + return Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + push(observer, Closings.create(), 100); + complete(observer, 101); + return Subscriptions.empty(); + } + }); + } + }; + + Observable> windowed = Observable.create(window(source, closer)); + windowed.subscribe(observeWindow(list, lists)); + + scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); + assertEquals(3, lists.size()); + assertEquals(lists.get(0), list("one", "two")); + assertEquals(lists.get(1), list("three", "four")); + assertEquals(lists.get(2), list("five")); + } + + private List list(String... args) { + List list = new ArrayList(); + for (String arg : args) { + list.add(arg); + } + return list; + } + + private void push(final Observer observer, final T value, int delay) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onNext(value); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private void complete(final Observer observer, int delay) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onCompleted(); + } + }, delay, TimeUnit.MILLISECONDS); + } + + private Action1> observeWindow(final List list, final List> lists) { + return new Action1>() { + @Override + public void call(Observable stringObservable) { + stringObservable.subscribe(new Observer() { + @Override + public void onCompleted() { + lists.add(new ArrayList(list)); + list.clear(); + } + + @Override + public void onError(Throwable e) { + fail(e.getMessage()); + } + + @Override + public void onNext(String args) { + list.add(args); + } + }); + } + }; + } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationZipTest.java b/rxjava-core/src/test/java/rx/operators/OperationZipTest.java index 2a73ac3eab..bab2d7d2fe 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationZipTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationZipTest.java @@ -1,584 +1,583 @@ package rx.operators; -import org.junit.Ignore; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationZip.*; + +import java.util.Arrays; +import java.util.Collection; + import org.junit.Test; import org.mockito.InOrder; + import rx.Observable; import rx.Observer; import rx.Subscription; +import rx.operators.OperationZip.Aggregator; +import rx.operators.OperationZip.ZipObserver; import rx.subscriptions.Subscriptions; import rx.util.functions.Func2; import rx.util.functions.Func3; import rx.util.functions.FuncN; import rx.util.functions.Functions; -import java.util.Arrays; -import java.util.Collection; +public class OperationZipTest { -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.*; -import static rx.operators.OperationZip.Aggregator; -import static rx.operators.OperationZip.ZipObserver; -import static rx.operators.OperationZip.zip; + @SuppressWarnings("unchecked") + @Test + public void testCollectionSizeDifferentThanFunction() { + FuncN zipr = Functions.fromFunc(getConcatStringIntegerIntArrayZipr()); + //Func3 + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + + @SuppressWarnings("rawtypes") + Collection ws = java.util.Collections.singleton(Observable.from("one", "two")); + Observable w = Observable.create(zip(ws, zipr)); + w.subscribe(aObserver); + + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, never()).onNext(any(String.class)); + } -public class OperationZipTest { + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testZippingDifferentLengthObservableSequences1() { + Observer w = mock(Observer.class); + + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + TestObservable w3 = new TestObservable(); + + Observable zipW = Observable.create(zip(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsZipr())); + zipW.subscribe(w); + + /* simulate sending data */ + // once for w1 + w1.observer.onNext("1a"); + w1.observer.onCompleted(); + // twice for w2 + w2.observer.onNext("2a"); + w2.observer.onNext("2b"); + w2.observer.onCompleted(); + // 4 times for w3 + w3.observer.onNext("3a"); + w3.observer.onNext("3b"); + w3.observer.onNext("3c"); + w3.observer.onNext("3d"); + w3.observer.onCompleted(); + + /* we should have been called 1 time on the Observer */ + InOrder inOrder = inOrder(w); + inOrder.verify(w).onNext("1a2a3a"); + + inOrder.verify(w, times(1)).onCompleted(); + } + + @Test + public void testZippingDifferentLengthObservableSequences2() { + @SuppressWarnings("unchecked") + Observer w = mock(Observer.class); + + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + TestObservable w3 = new TestObservable(); + + Observable zipW = Observable.create(zip(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsZipr())); + zipW.subscribe(w); + + /* simulate sending data */ + // 4 times for w1 + w1.observer.onNext("1a"); + w1.observer.onNext("1b"); + w1.observer.onNext("1c"); + w1.observer.onNext("1d"); + w1.observer.onCompleted(); + // twice for w2 + w2.observer.onNext("2a"); + w2.observer.onNext("2b"); + w2.observer.onCompleted(); + // 1 times for w3 + w3.observer.onNext("3a"); + w3.observer.onCompleted(); + + /* we should have been called 1 time on the Observer */ + InOrder inOrder = inOrder(w); + inOrder.verify(w).onNext("1a2a3a"); + + inOrder.verify(w, times(1)).onCompleted(); + + } + + /** + * Testing internal private logic due to the complexity so I want to use TDD to test as a I build it rather than relying purely on the overall functionality expected by the public methods. + */ + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorSimple() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + + InOrder inOrder = inOrder(aObserver); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("helloworld"); + + a.next(r1, "hello "); + a.next(r2, "again"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("hello again"); + + a.complete(r1); + a.complete(r2); + + inOrder.verify(aObserver, never()).onNext(anyString()); + verify(aObserver, times(1)).onCompleted(); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorDifferentSizedResultsWithOnComplete() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + a.complete(r2); + + InOrder inOrder = inOrder(aObserver); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("helloworld"); + + a.next(r1, "hi"); + a.complete(r1); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verify(aObserver, never()).onNext(anyString()); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregateMultipleTypes() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + a.complete(r2); + + InOrder inOrder = inOrder(aObserver); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("helloworld"); + + a.next(r1, "hi"); + a.complete(r1); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verify(aObserver, never()).onNext(anyString()); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregate3Types() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + ZipObserver r3 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + a.addObserver(r3); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, 2); + a.next(r3, new int[] { 5, 6, 7 }); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("hello2[5, 6, 7]"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorsWithDifferentSizesAndTiming() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "one"); + a.next(r1, "two"); + a.next(r1, "three"); + a.next(r2, "A"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("oneA"); + + a.next(r1, "four"); + a.complete(r1); + a.next(r2, "B"); + verify(aObserver, times(1)).onNext("twoB"); + a.next(r2, "C"); + verify(aObserver, times(1)).onNext("threeC"); + a.next(r2, "D"); + verify(aObserver, times(1)).onNext("fourD"); + a.next(r2, "E"); + verify(aObserver, never()).onNext("E"); + a.complete(r2); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorError() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); + + a.error(r1, new RuntimeException("")); + a.next(r1, "hello"); + a.next(r2, "again"); + + verify(aObserver, times(1)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + // we don't want to be called again after an error + verify(aObserver, times(0)).onNext("helloagain"); + } + + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testAggregatorUnsubscribe() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Subscription subscription = a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "hello"); + a.next(r2, "world"); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); + + subscription.unsubscribe(); + a.next(r1, "hello"); + a.next(r2, "again"); + + verify(aObserver, times(0)).onError(any(Throwable.class)); + verify(aObserver, never()).onCompleted(); + // we don't want to be called again after an error + verify(aObserver, times(0)).onNext("helloagain"); + } - @SuppressWarnings("unchecked") - @Test - public void testCollectionSizeDifferentThanFunction() { - FuncN zipr = Functions.fromFunc(getConcatStringIntegerIntArrayZipr()); - //Func3 - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - - @SuppressWarnings("rawtypes") - Collection ws = java.util.Collections.singleton(Observable.from("one", "two")); - Observable w = Observable.create(zip(ws, zipr)); - w.subscribe(aObserver); - - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, never()).onNext(any(String.class)); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testZippingDifferentLengthObservableSequences1() { - Observer w = mock(Observer.class); - - TestObservable w1 = new TestObservable(); - TestObservable w2 = new TestObservable(); - TestObservable w3 = new TestObservable(); - - Observable zipW = Observable.create(zip(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsZipr())); - zipW.subscribe(w); - - /* simulate sending data */ - // once for w1 - w1.observer.onNext("1a"); - w1.observer.onCompleted(); - // twice for w2 - w2.observer.onNext("2a"); - w2.observer.onNext("2b"); - w2.observer.onCompleted(); - // 4 times for w3 - w3.observer.onNext("3a"); - w3.observer.onNext("3b"); - w3.observer.onNext("3c"); - w3.observer.onNext("3d"); - w3.observer.onCompleted(); - - /* we should have been called 1 time on the Observer */ - InOrder inOrder = inOrder(w); - inOrder.verify(w).onNext("1a2a3a"); - - inOrder.verify(w, times(1)).onCompleted(); - } - - @Test - public void testZippingDifferentLengthObservableSequences2() { @SuppressWarnings("unchecked") - Observer w = mock(Observer.class); - - TestObservable w1 = new TestObservable(); - TestObservable w2 = new TestObservable(); - TestObservable w3 = new TestObservable(); - - Observable zipW = Observable.create(zip(Observable.create(w1), Observable.create(w2), Observable.create(w3), getConcat3StringsZipr())); - zipW.subscribe(w); - - /* simulate sending data */ - // 4 times for w1 - w1.observer.onNext("1a"); - w1.observer.onNext("1b"); - w1.observer.onNext("1c"); - w1.observer.onNext("1d"); - w1.observer.onCompleted(); - // twice for w2 - w2.observer.onNext("2a"); - w2.observer.onNext("2b"); - w2.observer.onCompleted(); - // 1 times for w3 - w3.observer.onNext("3a"); - w3.observer.onCompleted(); - - /* we should have been called 1 time on the Observer */ - InOrder inOrder = inOrder(w); - inOrder.verify(w).onNext("1a2a3a"); - - inOrder.verify(w, times(1)).onCompleted(); - - } - - /** - * Testing internal private logic due to the complexity so I want to use TDD to test as a I build it rather than relying purely on the overall functionality expected by the public methods. - */ - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorSimple() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - - InOrder inOrder = inOrder(aObserver); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("helloworld"); - - a.next(r1, "hello "); - a.next(r2, "again"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("hello again"); - - a.complete(r1); - a.complete(r2); - - inOrder.verify(aObserver, never()).onNext(anyString()); - verify(aObserver, times(1)).onCompleted(); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorDifferentSizedResultsWithOnComplete() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - a.complete(r2); - - InOrder inOrder = inOrder(aObserver); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("helloworld"); - - a.next(r1, "hi"); - a.complete(r1); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, times(1)).onCompleted(); - inOrder.verify(aObserver, never()).onNext(anyString()); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregateMultipleTypes() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - a.complete(r2); - - InOrder inOrder = inOrder(aObserver); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("helloworld"); - - a.next(r1, "hi"); - a.complete(r1); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, times(1)).onCompleted(); - inOrder.verify(aObserver, never()).onNext(anyString()); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregate3Types() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - ZipObserver r3 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - a.addObserver(r3); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, 2); - a.next(r3, new int[] { 5, 6, 7 }); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("hello2[5, 6, 7]"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorsWithDifferentSizesAndTiming() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "one"); - a.next(r1, "two"); - a.next(r1, "three"); - a.next(r2, "A"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("oneA"); - - a.next(r1, "four"); - a.complete(r1); - a.next(r2, "B"); - verify(aObserver, times(1)).onNext("twoB"); - a.next(r2, "C"); - verify(aObserver, times(1)).onNext("threeC"); - a.next(r2, "D"); - verify(aObserver, times(1)).onNext("fourD"); - a.next(r2, "E"); - verify(aObserver, never()).onNext("E"); - a.complete(r2); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorError() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("helloworld"); - - a.error(r1, new RuntimeException("")); - a.next(r1, "hello"); - a.next(r2, "again"); - - verify(aObserver, times(1)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - // we don't want to be called again after an error - verify(aObserver, times(0)).onNext("helloagain"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorUnsubscribe() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - Subscription subscription = a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "hello"); - a.next(r2, "world"); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - verify(aObserver, times(1)).onNext("helloworld"); - - subscription.unsubscribe(); - a.next(r1, "hello"); - a.next(r2, "again"); - - verify(aObserver, times(0)).onError(any(Throwable.class)); - verify(aObserver, never()).onCompleted(); - // we don't want to be called again after an error - verify(aObserver, times(0)).onNext("helloagain"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testAggregatorEarlyCompletion() { - FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all Observables provide values */ - Aggregator a = new Aggregator(zipr); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - a.onSubscribe(aObserver); - - /* mock the Observable Observers that are 'pushing' data for us */ - ZipObserver r1 = mock(ZipObserver.class); - ZipObserver r2 = mock(ZipObserver.class); - - /* pretend we're starting up */ - a.addObserver(r1); - a.addObserver(r2); - - /* simulate the Observables pushing data into the aggregator */ - a.next(r1, "one"); - a.next(r1, "two"); - a.complete(r1); - a.next(r2, "A"); - - InOrder inOrder = inOrder(aObserver); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, never()).onCompleted(); - inOrder.verify(aObserver, times(1)).onNext("oneA"); - - a.complete(r2); - - inOrder.verify(aObserver, never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, times(1)).onCompleted(); - inOrder.verify(aObserver, never()).onNext(anyString()); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testZip2Types() { - Func2 zipr = getConcatStringIntegerZipr(); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - - Observable w = Observable.create(zip(Observable.from("one", "two"), Observable.from(2, 3, 4), zipr)); - w.subscribe(aObserver); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one2"); - verify(aObserver, times(1)).onNext("two3"); - verify(aObserver, never()).onNext("4"); - } - - @SuppressWarnings("unchecked") - /* mock calls don't do generics */ - @Test - public void testZip3Types() { - Func3 zipr = getConcatStringIntegerIntArrayZipr(); - - /* define a Observer to receive aggregated events */ - Observer aObserver = mock(Observer.class); - - Observable w = Observable.create(zip(Observable.from("one", "two"), Observable.from(2), Observable.from(new int[] { 4, 5, 6 }), zipr)); - w.subscribe(aObserver); - - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - verify(aObserver, times(1)).onNext("one2[4, 5, 6]"); - verify(aObserver, never()).onNext("two"); - } - - @Test - public void testOnNextExceptionInvokesOnError() { - Func2 zipr = getDivideZipr(); + /* mock calls don't do generics */ + @Test + public void testAggregatorEarlyCompletion() { + FuncN zipr = getConcatZipr(); + /* create the aggregator which will execute the zip function when all Observables provide values */ + Aggregator a = new Aggregator(zipr); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.onSubscribe(aObserver); + + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + + /* pretend we're starting up */ + a.addObserver(r1); + a.addObserver(r2); + + /* simulate the Observables pushing data into the aggregator */ + a.next(r1, "one"); + a.next(r1, "two"); + a.complete(r1); + a.next(r2, "A"); + + InOrder inOrder = inOrder(aObserver); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("oneA"); + + a.complete(r2); + + inOrder.verify(aObserver, never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verify(aObserver, never()).onNext(anyString()); + } @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); + /* mock calls don't do generics */ + @Test + public void testZip2Types() { + Func2 zipr = getConcatStringIntegerZipr(); + + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + + Observable w = Observable.create(zip(Observable.from("one", "two"), Observable.from(2, 3, 4), zipr)); + w.subscribe(aObserver); + + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one2"); + verify(aObserver, times(1)).onNext("two3"); + verify(aObserver, never()).onNext("4"); + } - Observable w = Observable.create(zip(Observable.from(10, 20, 30), Observable.from(0, 1, 2), zipr)); - w.subscribe(aObserver); + @SuppressWarnings("unchecked") + /* mock calls don't do generics */ + @Test + public void testZip3Types() { + Func3 zipr = getConcatStringIntegerIntArrayZipr(); - verify(aObserver, times(1)).onError(any(Throwable.class)); - } + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); - private Func2 getDivideZipr() { - Func2 zipr = new Func2() { + Observable w = Observable.create(zip(Observable.from("one", "two"), Observable.from(2), Observable.from(new int[] { 4, 5, 6 }), zipr)); + w.subscribe(aObserver); - @Override - public Integer call(Integer i1, Integer i2) { - return i1 / i2; - } + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one2[4, 5, 6]"); + verify(aObserver, never()).onNext("two"); + } - }; - return zipr; - } + @Test + public void testOnNextExceptionInvokesOnError() { + Func2 zipr = getDivideZipr(); - private Func3 getConcat3StringsZipr() { - Func3 zipr = new Func3() { + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); - @Override - public String call(String a1, String a2, String a3) { - if (a1 == null) { - a1 = ""; - } - if (a2 == null) { - a2 = ""; - } - if (a3 == null) { - a3 = ""; - } - return a1 + a2 + a3; - } - - }; - return zipr; - } - - private FuncN getConcatZipr() { - FuncN zipr = new FuncN() { - - @Override - public String call(Object... args) { - String returnValue = ""; - for (Object o : args) { - if (o != null) { - returnValue += getStringValue(o); - } - } - System.out.println("returning: " + returnValue); - return returnValue; - } - - }; - return zipr; - } - - private Func2 getConcatStringIntegerZipr() { - Func2 zipr = new Func2() { - - @Override - public String call(String s, Integer i) { - return getStringValue(s) + getStringValue(i); - } - - }; - return zipr; - } - - private Func3 getConcatStringIntegerIntArrayZipr() { - Func3 zipr = new Func3() { - - @Override - public String call(String s, Integer i, int[] iArray) { - return getStringValue(s) + getStringValue(i) + getStringValue(iArray); - } - - }; - return zipr; - } - - private static String getStringValue(Object o) { - if (o == null) { - return ""; - } else { - if (o instanceof int[]) { - return Arrays.toString((int[]) o); - } else { - return String.valueOf(o); - } + Observable w = Observable.create(zip(Observable.from(10, 20, 30), Observable.from(0, 1, 2), zipr)); + w.subscribe(aObserver); + + verify(aObserver, times(1)).onError(any(Throwable.class)); } - } - private static class TestObservable implements Observable.OnSubscribeFunc { + private Func2 getDivideZipr() { + Func2 zipr = new Func2() { - Observer observer; + @Override + public Integer call(Integer i1, Integer i2) { + return i1 / i2; + } - @Override - public Subscription onSubscribe(Observer Observer) { - // just store the variable where it can be accessed so we can manually trigger it - this.observer = Observer; - return Subscriptions.empty(); + }; + return zipr; } - } + private Func3 getConcat3StringsZipr() { + Func3 zipr = new Func3() { + + @Override + public String call(String a1, String a2, String a3) { + if (a1 == null) { + a1 = ""; + } + if (a2 == null) { + a2 = ""; + } + if (a3 == null) { + a3 = ""; + } + return a1 + a2 + a3; + } + + }; + return zipr; + } + + private FuncN getConcatZipr() { + FuncN zipr = new FuncN() { + + @Override + public String call(Object... args) { + String returnValue = ""; + for (Object o : args) { + if (o != null) { + returnValue += getStringValue(o); + } + } + System.out.println("returning: " + returnValue); + return returnValue; + } + + }; + return zipr; + } + + private Func2 getConcatStringIntegerZipr() { + Func2 zipr = new Func2() { + + @Override + public String call(String s, Integer i) { + return getStringValue(s) + getStringValue(i); + } + + }; + return zipr; + } + + private Func3 getConcatStringIntegerIntArrayZipr() { + Func3 zipr = new Func3() { + + @Override + public String call(String s, Integer i, int[] iArray) { + return getStringValue(s) + getStringValue(i) + getStringValue(iArray); + } + + }; + return zipr; + } + + private static String getStringValue(Object o) { + if (o == null) { + return ""; + } else { + if (o instanceof int[]) { + return Arrays.toString((int[]) o); + } else { + return String.valueOf(o); + } + } + } + + private static class TestObservable implements Observable.OnSubscribeFunc { + + Observer observer; + + @Override + public Subscription onSubscribe(Observer Observer) { + // just store the variable where it can be accessed so we can manually trigger it + this.observer = Observer; + return Subscriptions.empty(); + } + + } } diff --git a/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java b/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java index a4bf9e8e8c..99120b4348 100644 --- a/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java +++ b/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java @@ -1,21 +1,19 @@ package rx.operators; -import org.junit.Ignore; +import static org.mockito.Mockito.*; + import org.junit.Test; -import rx.Subscription; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import rx.Subscription; public class SafeObservableSubscriptionTest { - @Test - public void testWrapAfterUnsubscribe() { - SafeObservableSubscription atomicObservableSubscription = new SafeObservableSubscription(); - atomicObservableSubscription.unsubscribe(); - Subscription innerSubscription = mock(Subscription.class); - atomicObservableSubscription.wrap(innerSubscription); - verify(innerSubscription, times(1)).unsubscribe(); - } + @Test + public void testWrapAfterUnsubscribe() { + SafeObservableSubscription atomicObservableSubscription = new SafeObservableSubscription(); + atomicObservableSubscription.unsubscribe(); + Subscription innerSubscription = mock(Subscription.class); + atomicObservableSubscription.wrap(innerSubscription); + verify(innerSubscription, times(1)).unsubscribe(); + } } diff --git a/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java b/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java index b9e4b2e278..2780eef712 100644 --- a/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java +++ b/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java @@ -1,751 +1,757 @@ package rx.operators; +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; + import rx.Observable; import rx.Observer; import rx.Subscription; -import java.util.Random; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.Assert.*; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - public class SynchronizedObserverTest { - @Mock - Observer aObserver; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testSingleThreadedBasic() { - Subscription s = mock(Subscription.class); - TestSingleThreadedObservable onSubscribe = new TestSingleThreadedObservable(s, "one", "two", "three"); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - SynchronizedObserver aw = new SynchronizedObserver(aObserver, as); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - // verify(s, times(1)).unsubscribe(); - } - - @Test - public void testMultiThreadedBasic() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three"); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - assertEquals(3, busyObserver.onNextCount.get()); - assertFalse(busyObserver.onError); - assertTrue(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - // verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - @Test - public void testMultiThreadedBasicWithLock() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three"); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - - Object lock = new Object(); - ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); - - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); - - externalBusyThread.start(); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - try { - externalBusyThread.join(10000); - assertFalse(externalBusyThread.isAlive()); - assertFalse(externalBusyThread.fail); - } catch (InterruptedException e) { - // ignore - } + @Mock + Observer aObserver; - assertEquals(3, busyObserver.onNextCount.get()); - assertFalse(busyObserver.onError); - assertTrue(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - // verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - @Test - public void testMultiThreadedWithNPE() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); - - // we can't know how many onNext calls will occur since they each run on a separate thread - // that depends on thread scheduling so 0, 1, 2 and 3 are all valid options - // assertEquals(3, busyObserver.onNextCount.get()); - assertTrue(busyObserver.onNextCount.get() < 4); - assertTrue(busyObserver.onError); - // no onCompleted because onError was invoked - assertFalse(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - //verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - @Test - public void testMultiThreadedWithNPEAndLock() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - - Object lock = new Object(); - ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); - - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); - - externalBusyThread.start(); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - try { - externalBusyThread.join(10000); - assertFalse(externalBusyThread.isAlive()); - assertFalse(externalBusyThread.fail); - } catch (InterruptedException e) { - // ignore + @Before + public void before() { + MockitoAnnotations.initMocks(this); } - System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); - - // we can't know how many onNext calls will occur since they each run on a separate thread - // that depends on thread scheduling so 0, 1, 2 and 3 are all valid options - // assertEquals(3, busyObserver.onNextCount.get()); - assertTrue(busyObserver.onNextCount.get() < 4); - assertTrue(busyObserver.onError); - // no onCompleted because onError was invoked - assertFalse(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - //verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - @Test - public void testMultiThreadedWithNPEinMiddle() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); - // this should not be the full number of items since the error should stop it before it completes all 9 - System.out.println("onNext count: " + busyObserver.onNextCount.get()); - assertTrue(busyObserver.onNextCount.get() < 9); - assertTrue(busyObserver.onError); - // no onCompleted because onError was invoked - assertFalse(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - // verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - @Test - public void testMultiThreadedWithNPEinMiddleAndLock() { - Subscription s = mock(Subscription.class); - TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); - Observable w = Observable.create(onSubscribe); - - SafeObservableSubscription as = new SafeObservableSubscription(s); - BusyObserver busyObserver = new BusyObserver(); - - Object lock = new Object(); - ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); - - SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); - - externalBusyThread.start(); - - w.subscribe(aw); - onSubscribe.waitToFinish(); - - try { - externalBusyThread.join(10000); - assertFalse(externalBusyThread.isAlive()); - assertFalse(externalBusyThread.fail); - } catch (InterruptedException e) { - // ignore + @Test + public void testSingleThreadedBasic() { + Subscription s = mock(Subscription.class); + TestSingleThreadedObservable onSubscribe = new TestSingleThreadedObservable(s, "one", "two", "three"); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + SynchronizedObserver aw = new SynchronizedObserver(aObserver, as); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); } - System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); - // this should not be the full number of items since the error should stop it before it completes all 9 - System.out.println("onNext count: " + busyObserver.onNextCount.get()); - assertTrue(busyObserver.onNextCount.get() < 9); - assertTrue(busyObserver.onError); - // no onCompleted because onError was invoked - assertFalse(busyObserver.onCompleted); - // non-deterministic because unsubscribe happens after 'waitToFinish' releases - // so commenting out for now as this is not a critical thing to test here - // verify(s, times(1)).unsubscribe(); - - // we can have concurrency ... - assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); - // ... but the onNext execution should be single threaded - assertEquals(1, busyObserver.maxConcurrentThreads.get()); - } - - /** - * A non-realistic use case that tries to expose thread-safety issues by throwing lots of out-of-order - * events on many threads. - * - * @param w - * @param tw - */ - @Test - public void runConcurrencyTest() { - ExecutorService tp = Executors.newFixedThreadPool(20); - try { - TestConcurrencyObserver tw = new TestConcurrencyObserver(); - SafeObservableSubscription s = new SafeObservableSubscription(); - SynchronizedObserver w = new SynchronizedObserver(tw, s); - - Future f1 = tp.submit(new OnNextThread(w, 12000)); - Future f2 = tp.submit(new OnNextThread(w, 5000)); - Future f3 = tp.submit(new OnNextThread(w, 75000)); - Future f4 = tp.submit(new OnNextThread(w, 13500)); - Future f5 = tp.submit(new OnNextThread(w, 22000)); - Future f6 = tp.submit(new OnNextThread(w, 15000)); - Future f7 = tp.submit(new OnNextThread(w, 7500)); - Future f8 = tp.submit(new OnNextThread(w, 23500)); - - Future f10 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f1, f2, f3, f4)); - try { - Thread.sleep(1); - } catch (InterruptedException e) { - // ignore - } - Future f11 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); - Future f12 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); - Future f13 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); - Future f14 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); - // // the next 4 onError events should wait on same as f10 - Future f15 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); - Future f16 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); - Future f17 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); - Future f18 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); - - waitOnThreads(f1, f2, f3, f4, f5, f6, f7, f8, f10, f11, f12, f13, f14, f15, f16, f17, f18); - @SuppressWarnings("unused") - int numNextEvents = tw.assertEvents(null); // no check of type since we don't want to test barging results here, just interleaving behavior - // System.out.println("Number of events executed: " + numNextEvents); - } catch (Throwable e) { - fail("Concurrency test failed: " + e.getMessage()); - e.printStackTrace(); - } finally { - tp.shutdown(); - try { - tp.awaitTermination(5000, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - private static void waitOnThreads(Future... futures) { - for (Future f : futures) { - try { - f.get(10, TimeUnit.SECONDS); - } catch (Throwable e) { - System.err.println("Failed while waiting on future."); - e.printStackTrace(); - } + @Test + public void testMultiThreadedBasic() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three"); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + assertEquals(3, busyObserver.onNextCount.get()); + assertFalse(busyObserver.onError); + assertTrue(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); } - } - /** - * A thread that will pass data to onNext - */ - public static class OnNextThread implements Runnable { + @Test + public void testMultiThreadedBasicWithLock() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three"); + Observable w = Observable.create(onSubscribe); - private final Observer Observer; - private final int numStringsToSend; + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); - OnNextThread(Observer Observer, int numStringsToSend) { - this.Observer = Observer; - this.numStringsToSend = numStringsToSend; - } - - @Override - public void run() { - for (int i = 0; i < numStringsToSend; i++) { - Observer.onNext("aString"); - } - } - } + Object lock = new Object(); + ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); - /** - * A thread that will call onError or onNext - */ - public static class CompletionThread implements Runnable { + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); - private final Observer Observer; - private final TestConcurrencyObserverEvent event; - private final Future[] waitOnThese; + externalBusyThread.start(); - CompletionThread(Observer Observer, TestConcurrencyObserverEvent event, Future... waitOnThese) { - this.Observer = Observer; - this.event = event; - this.waitOnThese = waitOnThese; - } + w.subscribe(aw); + onSubscribe.waitToFinish(); - @Override - public void run() { - /* if we have 'waitOnThese' futures, we'll wait on them before proceeding */ - if (waitOnThese != null) { - for (Future f : waitOnThese) { - try { - f.get(); - } catch (Throwable e) { - System.err.println("Error while waiting on future in CompletionThread"); - } + try { + externalBusyThread.join(10000); + assertFalse(externalBusyThread.isAlive()); + assertFalse(externalBusyThread.fail); + } catch (InterruptedException e) { + // ignore } - } - /* send the event */ - if (event == TestConcurrencyObserverEvent.onError) { - Observer.onError(new RuntimeException("mocked exception")); - } else if (event == TestConcurrencyObserverEvent.onCompleted) { - Observer.onCompleted(); + assertEquals(3, busyObserver.onNextCount.get()); + assertFalse(busyObserver.onError); + assertTrue(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); + } - } else { - throw new IllegalArgumentException("Expecting either onError or onCompleted"); - } + @Test + public void testMultiThreadedWithNPE() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); + + // we can't know how many onNext calls will occur since they each run on a separate thread + // that depends on thread scheduling so 0, 1, 2 and 3 are all valid options + // assertEquals(3, busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 4); + assertTrue(busyObserver.onError); + // no onCompleted because onError was invoked + assertFalse(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + //verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); } - } - private static enum TestConcurrencyObserverEvent { - onCompleted, onError, onNext - } + @Test + public void testMultiThreadedWithNPEAndLock() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null); + Observable w = Observable.create(onSubscribe); - private static class TestConcurrencyObserver implements Observer { + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); - /** - * used to store the order and number of events received - */ - private final LinkedBlockingQueue events = new LinkedBlockingQueue(); - private final int waitTime; + Object lock = new Object(); + ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); - @SuppressWarnings("unused") - public TestConcurrencyObserver(int waitTimeInNext) { - this.waitTime = waitTimeInNext; - } + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); - public TestConcurrencyObserver() { - this.waitTime = 0; - } + externalBusyThread.start(); + + w.subscribe(aw); + onSubscribe.waitToFinish(); - @Override - public void onCompleted() { - events.add(TestConcurrencyObserverEvent.onCompleted); + try { + externalBusyThread.join(10000); + assertFalse(externalBusyThread.isAlive()); + assertFalse(externalBusyThread.fail); + } catch (InterruptedException e) { + // ignore + } + + System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); + + // we can't know how many onNext calls will occur since they each run on a separate thread + // that depends on thread scheduling so 0, 1, 2 and 3 are all valid options + // assertEquals(3, busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 4); + assertTrue(busyObserver.onError); + // no onCompleted because onError was invoked + assertFalse(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + //verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); } - @Override - public void onError(Throwable e) { - events.add(TestConcurrencyObserverEvent.onError); + @Test + public void testMultiThreadedWithNPEinMiddle() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as); + + w.subscribe(aw); + onSubscribe.waitToFinish(); + + System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); + // this should not be the full number of items since the error should stop it before it completes all 9 + System.out.println("onNext count: " + busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 9); + assertTrue(busyObserver.onError); + // no onCompleted because onError was invoked + assertFalse(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); } - @Override - public void onNext(String args) { - events.add(TestConcurrencyObserverEvent.onNext); - // do some artificial work to make the thread scheduling/timing vary - int s = 0; - for (int i = 0; i < 20; i++) { - s += s * i; - } + @Test + public void testMultiThreadedWithNPEinMiddleAndLock() { + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); + Observable w = Observable.create(onSubscribe); + + SafeObservableSubscription as = new SafeObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + + Object lock = new Object(); + ExternalBusyThread externalBusyThread = new ExternalBusyThread(busyObserver, lock, 10, 100); + + SynchronizedObserver aw = new SynchronizedObserver(busyObserver, as, lock); + + externalBusyThread.start(); + + w.subscribe(aw); + onSubscribe.waitToFinish(); - if (waitTime > 0) { try { - Thread.sleep(waitTime); + externalBusyThread.join(10000); + assertFalse(externalBusyThread.isAlive()); + assertFalse(externalBusyThread.fail); } catch (InterruptedException e) { - // ignore + // ignore } - } + + System.out.println("maxConcurrentThreads: " + onSubscribe.maxConcurrentThreads.get()); + // this should not be the full number of items since the error should stop it before it completes all 9 + System.out.println("onNext count: " + busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 9); + assertTrue(busyObserver.onError); + // no onCompleted because onError was invoked + assertFalse(busyObserver.onCompleted); + // non-deterministic because unsubscribe happens after 'waitToFinish' releases + // so commenting out for now as this is not a critical thing to test here + // verify(s, times(1)).unsubscribe(); + + // we can have concurrency ... + assertTrue(onSubscribe.maxConcurrentThreads.get() > 1); + // ... but the onNext execution should be single threaded + assertEquals(1, busyObserver.maxConcurrentThreads.get()); } /** - * Assert the order of events is correct and return the number of onNext executions. - * - * @param expectedEndingEvent - * @return int count of onNext calls - * @throws IllegalStateException If order of events was invalid. + * A non-realistic use case that tries to expose thread-safety issues by throwing lots of out-of-order + * events on many threads. + * + * @param w + * @param tw */ - public int assertEvents(TestConcurrencyObserverEvent expectedEndingEvent) throws IllegalStateException { - int nextCount = 0; - boolean finished = false; - for (TestConcurrencyObserverEvent e : events) { - if (e == TestConcurrencyObserverEvent.onNext) { - if (finished) { - // already finished, we shouldn't get this again - throw new IllegalStateException("Received onNext but we're already finished."); - } - nextCount++; - } else if (e == TestConcurrencyObserverEvent.onError) { - if (finished) { - // already finished, we shouldn't get this again - throw new IllegalStateException("Received onError but we're already finished."); - } - if (expectedEndingEvent != null && TestConcurrencyObserverEvent.onError != expectedEndingEvent) { - throw new IllegalStateException("Received onError ending event but expected " + expectedEndingEvent); - } - finished = true; - } else if (e == TestConcurrencyObserverEvent.onCompleted) { - if (finished) { - // already finished, we shouldn't get this again - throw new IllegalStateException("Received onCompleted but we're already finished."); - } - if (expectedEndingEvent != null && TestConcurrencyObserverEvent.onCompleted != expectedEndingEvent) { - throw new IllegalStateException("Received onCompleted ending event but expected " + expectedEndingEvent); - } - finished = true; + @Test + public void runConcurrencyTest() { + ExecutorService tp = Executors.newFixedThreadPool(20); + try { + TestConcurrencyObserver tw = new TestConcurrencyObserver(); + SafeObservableSubscription s = new SafeObservableSubscription(); + SynchronizedObserver w = new SynchronizedObserver(tw, s); + + Future f1 = tp.submit(new OnNextThread(w, 12000)); + Future f2 = tp.submit(new OnNextThread(w, 5000)); + Future f3 = tp.submit(new OnNextThread(w, 75000)); + Future f4 = tp.submit(new OnNextThread(w, 13500)); + Future f5 = tp.submit(new OnNextThread(w, 22000)); + Future f6 = tp.submit(new OnNextThread(w, 15000)); + Future f7 = tp.submit(new OnNextThread(w, 7500)); + Future f8 = tp.submit(new OnNextThread(w, 23500)); + + Future f10 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f1, f2, f3, f4)); + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // ignore + } + Future f11 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f12 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f13 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f14 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + // // the next 4 onError events should wait on same as f10 + Future f15 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f16 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f17 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f18 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + + waitOnThreads(f1, f2, f3, f4, f5, f6, f7, f8, f10, f11, f12, f13, f14, f15, f16, f17, f18); + @SuppressWarnings("unused") + int numNextEvents = tw.assertEvents(null); // no check of type since we don't want to test barging results here, just interleaving behavior + // System.out.println("Number of events executed: " + numNextEvents); + } catch (Throwable e) { + fail("Concurrency test failed: " + e.getMessage()); + e.printStackTrace(); + } finally { + tp.shutdown(); + try { + tp.awaitTermination(5000, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } } - } - - return nextCount; } - } + private static void waitOnThreads(Future... futures) { + for (Future f : futures) { + try { + f.get(10, TimeUnit.SECONDS); + } catch (Throwable e) { + System.err.println("Failed while waiting on future."); + e.printStackTrace(); + } + } + } - /** - * This spawns a single thread for the subscribe execution - */ - private static class TestSingleThreadedObservable implements Observable.OnSubscribeFunc { + /** + * A thread that will pass data to onNext + */ + public static class OnNextThread implements Runnable { - final Subscription s; - final String[] values; - private Thread t = null; + private final Observer Observer; + private final int numStringsToSend; - public TestSingleThreadedObservable(final Subscription s, final String... values) { - this.s = s; - this.values = values; + OnNextThread(Observer Observer, int numStringsToSend) { + this.Observer = Observer; + this.numStringsToSend = numStringsToSend; + } + @Override + public void run() { + for (int i = 0; i < numStringsToSend; i++) { + Observer.onNext("aString"); + } + } } - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestSingleThreadedObservable subscribed to ..."); - t = new Thread(new Runnable() { + /** + * A thread that will call onError or onNext + */ + public static class CompletionThread implements Runnable { + + private final Observer Observer; + private final TestConcurrencyObserverEvent event; + private final Future[] waitOnThese; + + CompletionThread(Observer Observer, TestConcurrencyObserverEvent event, Future... waitOnThese) { + this.Observer = Observer; + this.event = event; + this.waitOnThese = waitOnThese; + } @Override public void run() { - try { - System.out.println("running TestSingleThreadedObservable thread"); - for (String s : values) { - System.out.println("TestSingleThreadedObservable onNext: " + s); - observer.onNext(s); + /* if we have 'waitOnThese' futures, we'll wait on them before proceeding */ + if (waitOnThese != null) { + for (Future f : waitOnThese) { + try { + f.get(); + } catch (Throwable e) { + System.err.println("Error while waiting on future in CompletionThread"); + } + } + } + + /* send the event */ + if (event == TestConcurrencyObserverEvent.onError) { + Observer.onError(new RuntimeException("mocked exception")); + } else if (event == TestConcurrencyObserverEvent.onCompleted) { + Observer.onCompleted(); + + } else { + throw new IllegalArgumentException("Expecting either onError or onCompleted"); } - observer.onCompleted(); - } catch (Throwable e) { - throw new RuntimeException(e); - } } + } - }); - System.out.println("starting TestSingleThreadedObservable thread"); - t.start(); - System.out.println("done starting TestSingleThreadedObservable thread"); - return s; + private static enum TestConcurrencyObserverEvent { + onCompleted, onError, onNext } - public void waitToFinish() { - try { - t.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + private static class TestConcurrencyObserver implements Observer { + + /** + * used to store the order and number of events received + */ + private final LinkedBlockingQueue events = new LinkedBlockingQueue(); + private final int waitTime; + + @SuppressWarnings("unused") + public TestConcurrencyObserver(int waitTimeInNext) { + this.waitTime = waitTimeInNext; + } + + public TestConcurrencyObserver() { + this.waitTime = 0; + } + + @Override + public void onCompleted() { + events.add(TestConcurrencyObserverEvent.onCompleted); + } + + @Override + public void onError(Throwable e) { + events.add(TestConcurrencyObserverEvent.onError); + } + + @Override + public void onNext(String args) { + events.add(TestConcurrencyObserverEvent.onNext); + // do some artificial work to make the thread scheduling/timing vary + int s = 0; + for (int i = 0; i < 20; i++) { + s += s * i; + } + + if (waitTime > 0) { + try { + Thread.sleep(waitTime); + } catch (InterruptedException e) { + // ignore + } + } + } + + /** + * Assert the order of events is correct and return the number of onNext executions. + * + * @param expectedEndingEvent + * @return int count of onNext calls + * @throws IllegalStateException + * If order of events was invalid. + */ + public int assertEvents(TestConcurrencyObserverEvent expectedEndingEvent) throws IllegalStateException { + int nextCount = 0; + boolean finished = false; + for (TestConcurrencyObserverEvent e : events) { + if (e == TestConcurrencyObserverEvent.onNext) { + if (finished) { + // already finished, we shouldn't get this again + throw new IllegalStateException("Received onNext but we're already finished."); + } + nextCount++; + } else if (e == TestConcurrencyObserverEvent.onError) { + if (finished) { + // already finished, we shouldn't get this again + throw new IllegalStateException("Received onError but we're already finished."); + } + if (expectedEndingEvent != null && TestConcurrencyObserverEvent.onError != expectedEndingEvent) { + throw new IllegalStateException("Received onError ending event but expected " + expectedEndingEvent); + } + finished = true; + } else if (e == TestConcurrencyObserverEvent.onCompleted) { + if (finished) { + // already finished, we shouldn't get this again + throw new IllegalStateException("Received onCompleted but we're already finished."); + } + if (expectedEndingEvent != null && TestConcurrencyObserverEvent.onCompleted != expectedEndingEvent) { + throw new IllegalStateException("Received onCompleted ending event but expected " + expectedEndingEvent); + } + finished = true; + } + } + + return nextCount; + } + } - } + /** + * This spawns a single thread for the subscribe execution + */ + private static class TestSingleThreadedObservable implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + private Thread t = null; + + public TestSingleThreadedObservable(final Subscription s, final String... values) { + this.s = s; + this.values = values; + + } + + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestSingleThreadedObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestSingleThreadedObservable thread"); + for (String s : values) { + System.out.println("TestSingleThreadedObservable onNext: " + s); + observer.onNext(s); + } + observer.onCompleted(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } - /** - * This spawns a thread for the subscription, then a separate thread for each onNext call. - */ - private static class TestMultiThreadedObservable implements Observable.OnSubscribeFunc { + }); + System.out.println("starting TestSingleThreadedObservable thread"); + t.start(); + System.out.println("done starting TestSingleThreadedObservable thread"); + return s; + } - final Subscription s; - final String[] values; - Thread t = null; - AtomicInteger threadsRunning = new AtomicInteger(); - AtomicInteger maxConcurrentThreads = new AtomicInteger(); - ExecutorService threadPool; + public void waitToFinish() { + try { + t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } - public TestMultiThreadedObservable(Subscription s, String... values) { - this.s = s; - this.values = values; - this.threadPool = Executors.newCachedThreadPool(); } - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("TestMultiThreadedObservable subscribed to ..."); - t = new Thread(new Runnable() { + /** + * This spawns a thread for the subscription, then a separate thread for each onNext call. + */ + private static class TestMultiThreadedObservable implements Observable.OnSubscribeFunc { + + final Subscription s; + final String[] values; + Thread t = null; + AtomicInteger threadsRunning = new AtomicInteger(); + AtomicInteger maxConcurrentThreads = new AtomicInteger(); + ExecutorService threadPool; + + public TestMultiThreadedObservable(Subscription s, String... values) { + this.s = s; + this.values = values; + this.threadPool = Executors.newCachedThreadPool(); + } @Override - public void run() { - try { - System.out.println("running TestMultiThreadedObservable thread"); - for (final String s : values) { - threadPool.execute(new Runnable() { + public Subscription onSubscribe(final Observer observer) { + System.out.println("TestMultiThreadedObservable subscribed to ..."); + t = new Thread(new Runnable() { @Override public void run() { - threadsRunning.incrementAndGet(); - try { - // perform onNext call - System.out.println("TestMultiThreadedObservable onNext: " + s); - if (s == null) { - // force an error - throw new NullPointerException(); + try { + System.out.println("running TestMultiThreadedObservable thread"); + for (final String s : values) { + threadPool.execute(new Runnable() { + + @Override + public void run() { + threadsRunning.incrementAndGet(); + try { + // perform onNext call + System.out.println("TestMultiThreadedObservable onNext: " + s); + if (s == null) { + // force an error + throw new NullPointerException(); + } + observer.onNext(s); + // capture 'maxThreads' + int concurrentThreads = threadsRunning.get(); + int maxThreads = maxConcurrentThreads.get(); + if (concurrentThreads > maxThreads) { + maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + } + } catch (Throwable e) { + observer.onError(e); + } finally { + threadsRunning.decrementAndGet(); + } + } + }); + } + // we are done spawning threads + threadPool.shutdown(); + } catch (Throwable e) { + throw new RuntimeException(e); } - observer.onNext(s); - // capture 'maxThreads' - int concurrentThreads = threadsRunning.get(); - int maxThreads = maxConcurrentThreads.get(); - if (concurrentThreads > maxThreads) { - maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + + // wait until all threads are done, then mark it as COMPLETED + try { + // wait for all the threads to finish + threadPool.awaitTermination(2, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); } - } catch (Throwable e) { - observer.onError(e); - } finally { - threadsRunning.decrementAndGet(); - } + observer.onCompleted(); } - }); + }); + System.out.println("starting TestMultiThreadedObservable thread"); + t.start(); + System.out.println("done starting TestMultiThreadedObservable thread"); + return s; + } + + public void waitToFinish() { + try { + t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); } - // we are done spawning threads - threadPool.shutdown(); - } catch (Throwable e) { - throw new RuntimeException(e); - } - - // wait until all threads are done, then mark it as COMPLETED - try { - // wait for all the threads to finish - threadPool.awaitTermination(2, TimeUnit.SECONDS); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - observer.onCompleted(); } - }); - System.out.println("starting TestMultiThreadedObservable thread"); - t.start(); - System.out.println("done starting TestMultiThreadedObservable thread"); - return s; } - public void waitToFinish() { - try { - t.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - } - - private static class BusyObserver implements Observer { - volatile boolean onCompleted = false; - volatile boolean onError = false; - AtomicInteger onNextCount = new AtomicInteger(); - AtomicInteger threadsRunning = new AtomicInteger(); - AtomicInteger maxConcurrentThreads = new AtomicInteger(); - - @Override - public void onCompleted() { - threadsRunning.incrementAndGet(); - - System.out.println(">>> BusyObserver received onCompleted"); - onCompleted = true; - - int concurrentThreads = threadsRunning.get(); - int maxThreads = maxConcurrentThreads.get(); - if (concurrentThreads > maxThreads) { - maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); - } - threadsRunning.decrementAndGet(); - } + private static class BusyObserver implements Observer { + volatile boolean onCompleted = false; + volatile boolean onError = false; + AtomicInteger onNextCount = new AtomicInteger(); + AtomicInteger threadsRunning = new AtomicInteger(); + AtomicInteger maxConcurrentThreads = new AtomicInteger(); - @Override - public void onError(Throwable e) { - threadsRunning.incrementAndGet(); + @Override + public void onCompleted() { + threadsRunning.incrementAndGet(); - System.out.println(">>> BusyObserver received onError: " + e.getMessage()); - onError = true; + System.out.println(">>> BusyObserver received onCompleted"); + onCompleted = true; - int concurrentThreads = threadsRunning.get(); - int maxThreads = maxConcurrentThreads.get(); - if (concurrentThreads > maxThreads) { - maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); - } - threadsRunning.decrementAndGet(); - } + int concurrentThreads = threadsRunning.get(); + int maxThreads = maxConcurrentThreads.get(); + if (concurrentThreads > maxThreads) { + maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + } + threadsRunning.decrementAndGet(); + } - @Override - public void onNext(String args) { - threadsRunning.incrementAndGet(); - try { - onNextCount.incrementAndGet(); - System.out.println(">>> BusyObserver received onNext: " + args); - try { - // simulate doing something computational - Thread.sleep(200); - } catch (InterruptedException e) { - e.printStackTrace(); + @Override + public void onError(Throwable e) { + threadsRunning.incrementAndGet(); + + System.out.println(">>> BusyObserver received onError: " + e.getMessage()); + onError = true; + + int concurrentThreads = threadsRunning.get(); + int maxThreads = maxConcurrentThreads.get(); + if (concurrentThreads > maxThreads) { + maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + } + threadsRunning.decrementAndGet(); } - } finally { - // capture 'maxThreads' - int concurrentThreads = threadsRunning.get(); - int maxThreads = maxConcurrentThreads.get(); - if (concurrentThreads > maxThreads) { - maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + + @Override + public void onNext(String args) { + threadsRunning.incrementAndGet(); + try { + onNextCount.incrementAndGet(); + System.out.println(">>> BusyObserver received onNext: " + args); + try { + // simulate doing something computational + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } finally { + // capture 'maxThreads' + int concurrentThreads = threadsRunning.get(); + int maxThreads = maxConcurrentThreads.get(); + if (concurrentThreads > maxThreads) { + maxConcurrentThreads.compareAndSet(maxThreads, concurrentThreads); + } + threadsRunning.decrementAndGet(); + } } - threadsRunning.decrementAndGet(); - } - } - } + } - private static class ExternalBusyThread extends Thread { + private static class ExternalBusyThread extends Thread { - private BusyObserver observer; - private Object lock; - private int lockTimes; - private int waitTime; - public volatile boolean fail; + private BusyObserver observer; + private Object lock; + private int lockTimes; + private int waitTime; + public volatile boolean fail; - public ExternalBusyThread(BusyObserver observer, Object lock, int lockTimes, int waitTime) { - this.observer = observer; - this.lock = lock; - this.lockTimes = lockTimes; - this.waitTime = waitTime; - this.fail = false; - } + public ExternalBusyThread(BusyObserver observer, Object lock, int lockTimes, int waitTime) { + this.observer = observer; + this.lock = lock; + this.lockTimes = lockTimes; + this.waitTime = waitTime; + this.fail = false; + } - @Override - public void run() { - Random r = new Random(); - for (int i = 0; i < lockTimes; i++) { - synchronized (lock) { - int oldOnNextCount = observer.onNextCount.get(); - boolean oldOnCompleted = observer.onCompleted; - boolean oldOnError = observer.onError; - try { - Thread.sleep(r.nextInt(waitTime)); - } catch (InterruptedException e) { - // ignore - } - // Since we own the lock, onNextCount, onCompleted and - // onError must not be changed. - int newOnNextCount = observer.onNextCount.get(); - boolean newOnCompleted = observer.onCompleted; - boolean newOnError = observer.onError; - if (oldOnNextCount != newOnNextCount) { - System.out.println(">>> ExternalBusyThread received different onNextCount: " - + oldOnNextCount - + " -> " - + newOnNextCount); - fail = true; - break; - } - if (oldOnCompleted != newOnCompleted) { - System.out.println(">>> ExternalBusyThread received different onCompleted: " - + oldOnCompleted - + " -> " - + newOnCompleted); - fail = true; - break; - } - if (oldOnError != newOnError) { - System.out.println(">>> ExternalBusyThread received different onError: " - + oldOnError - + " -> " - + newOnError); - fail = true; - break; - } + @Override + public void run() { + Random r = new Random(); + for (int i = 0; i < lockTimes; i++) { + synchronized (lock) { + int oldOnNextCount = observer.onNextCount.get(); + boolean oldOnCompleted = observer.onCompleted; + boolean oldOnError = observer.onError; + try { + Thread.sleep(r.nextInt(waitTime)); + } catch (InterruptedException e) { + // ignore + } + // Since we own the lock, onNextCount, onCompleted and + // onError must not be changed. + int newOnNextCount = observer.onNextCount.get(); + boolean newOnCompleted = observer.onCompleted; + boolean newOnError = observer.onError; + if (oldOnNextCount != newOnNextCount) { + System.out.println(">>> ExternalBusyThread received different onNextCount: " + + oldOnNextCount + + " -> " + + newOnNextCount); + fail = true; + break; + } + if (oldOnCompleted != newOnCompleted) { + System.out.println(">>> ExternalBusyThread received different onCompleted: " + + oldOnCompleted + + " -> " + + newOnCompleted); + fail = true; + break; + } + if (oldOnError != newOnError) { + System.out.println(">>> ExternalBusyThread received different onError: " + + oldOnError + + " -> " + + newOnError); + fail = true; + break; + } + } + } } - } - } - } + } } diff --git a/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java b/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java index 3ea17bf152..94c4fdcc0d 100644 --- a/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java +++ b/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java @@ -1,77 +1,77 @@ package rx.plugins; -import org.junit.Test; +import static org.junit.Assert.*; -import static org.junit.Assert.assertTrue; +import org.junit.Test; public class RxJavaPluginsTest { - @Test - public void testErrorHandlerDefaultImpl() { - RxJavaErrorHandler impl = new RxJavaPlugins().getErrorHandler(); - assertTrue(impl instanceof RxJavaErrorHandlerDefault); - } + @Test + public void testErrorHandlerDefaultImpl() { + RxJavaErrorHandler impl = new RxJavaPlugins().getErrorHandler(); + assertTrue(impl instanceof RxJavaErrorHandlerDefault); + } - @Test - public void testErrorHandlerViaRegisterMethod() { - RxJavaPlugins p = new RxJavaPlugins(); - p.registerErrorHandler(new RxJavaErrorHandlerTestImpl()); - RxJavaErrorHandler impl = p.getErrorHandler(); - assertTrue(impl instanceof RxJavaErrorHandlerTestImpl); - } + @Test + public void testErrorHandlerViaRegisterMethod() { + RxJavaPlugins p = new RxJavaPlugins(); + p.registerErrorHandler(new RxJavaErrorHandlerTestImpl()); + RxJavaErrorHandler impl = p.getErrorHandler(); + assertTrue(impl instanceof RxJavaErrorHandlerTestImpl); + } - @Test - public void testErrorHandlerViaProperty() { - try { - RxJavaPlugins p = new RxJavaPlugins(); - String fullClass = getFullClassNameForTestClass(RxJavaErrorHandlerTestImpl.class); - System.setProperty("rxjava.plugin.RxJavaErrorHandler.implementation", fullClass); - RxJavaErrorHandler impl = p.getErrorHandler(); - assertTrue(impl instanceof RxJavaErrorHandlerTestImpl); - } finally { - System.clearProperty("rxjava.plugin.RxJavaErrorHandler.implementation"); + @Test + public void testErrorHandlerViaProperty() { + try { + RxJavaPlugins p = new RxJavaPlugins(); + String fullClass = getFullClassNameForTestClass(RxJavaErrorHandlerTestImpl.class); + System.setProperty("rxjava.plugin.RxJavaErrorHandler.implementation", fullClass); + RxJavaErrorHandler impl = p.getErrorHandler(); + assertTrue(impl instanceof RxJavaErrorHandlerTestImpl); + } finally { + System.clearProperty("rxjava.plugin.RxJavaErrorHandler.implementation"); + } } - } - // inside test so it is stripped from Javadocs - public static class RxJavaErrorHandlerTestImpl extends RxJavaErrorHandler { - // just use defaults - } + // inside test so it is stripped from Javadocs + public static class RxJavaErrorHandlerTestImpl extends RxJavaErrorHandler { + // just use defaults + } - @Test - public void testObservableExecutionHookDefaultImpl() { - RxJavaPlugins p = new RxJavaPlugins(); - RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); - assertTrue(impl instanceof RxJavaObservableExecutionHookDefault); - } + @Test + public void testObservableExecutionHookDefaultImpl() { + RxJavaPlugins p = new RxJavaPlugins(); + RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); + assertTrue(impl instanceof RxJavaObservableExecutionHookDefault); + } - @Test - public void testObservableExecutionHookViaRegisterMethod() { - RxJavaPlugins p = new RxJavaPlugins(); - p.registerObservableExecutionHook(new RxJavaObservableExecutionHookTestImpl()); - RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); - assertTrue(impl instanceof RxJavaObservableExecutionHookTestImpl); - } + @Test + public void testObservableExecutionHookViaRegisterMethod() { + RxJavaPlugins p = new RxJavaPlugins(); + p.registerObservableExecutionHook(new RxJavaObservableExecutionHookTestImpl()); + RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); + assertTrue(impl instanceof RxJavaObservableExecutionHookTestImpl); + } - @Test - public void testObservableExecutionHookViaProperty() { - try { - RxJavaPlugins p = new RxJavaPlugins(); - String fullClass = getFullClassNameForTestClass(RxJavaObservableExecutionHookTestImpl.class); - System.setProperty("rxjava.plugin.RxJavaObservableExecutionHook.implementation", fullClass); - RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); - assertTrue(impl instanceof RxJavaObservableExecutionHookTestImpl); - } finally { - System.clearProperty("rxjava.plugin.RxJavaObservableExecutionHook.implementation"); + @Test + public void testObservableExecutionHookViaProperty() { + try { + RxJavaPlugins p = new RxJavaPlugins(); + String fullClass = getFullClassNameForTestClass(RxJavaObservableExecutionHookTestImpl.class); + System.setProperty("rxjava.plugin.RxJavaObservableExecutionHook.implementation", fullClass); + RxJavaObservableExecutionHook impl = p.getObservableExecutionHook(); + assertTrue(impl instanceof RxJavaObservableExecutionHookTestImpl); + } finally { + System.clearProperty("rxjava.plugin.RxJavaObservableExecutionHook.implementation"); + } } - } - // inside test so it is stripped from Javadocs - public static class RxJavaObservableExecutionHookTestImpl extends RxJavaObservableExecutionHook { - // just use defaults - } + // inside test so it is stripped from Javadocs + public static class RxJavaObservableExecutionHookTestImpl extends RxJavaObservableExecutionHook { + // just use defaults + } - private static String getFullClassNameForTestClass(Class cls) { - return RxJavaPlugins.class.getPackage().getName() + "." + RxJavaPluginsTest.class.getSimpleName() + "$" + cls.getSimpleName(); - } + private static String getFullClassNameForTestClass(Class cls) { + return RxJavaPlugins.class.getPackage().getName() + "." + RxJavaPluginsTest.class.getSimpleName() + "$" + cls.getSimpleName(); + } } diff --git a/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java index fb5220640b..243dda02bf 100644 --- a/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java @@ -1,135 +1,134 @@ package rx.subjects; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + import org.junit.Test; import org.mockito.Mockito; + import rx.Observer; import rx.Subscription; import rx.util.functions.Action1; import rx.util.functions.Func0; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.*; - public class AsyncSubjectTest { - - private final Throwable testException = new Throwable(); - - @Test - public void testNeverCompleted() { - AsyncSubject subject = AsyncSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - - assertNeverCompletedObserver(aObserver); - } - - private void assertNeverCompletedObserver(Observer aObserver) { - verify(aObserver, Mockito.never()).onNext(anyString()); - verify(aObserver, Mockito.never()).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testCompleted() { - AsyncSubject subject = AsyncSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onCompleted(); - - assertCompletedObserver(aObserver); - } - - private void assertCompletedObserver(Observer aObserver) { - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testError() { - AsyncSubject subject = AsyncSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onError(testException); - subject.onNext("four"); - subject.onError(new Throwable()); - subject.onCompleted(); - - assertErrorObserver(aObserver); - } - - private void assertErrorObserver(Observer aObserver) { - verify(aObserver, Mockito.never()).onNext(anyString()); - verify(aObserver, times(1)).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testUnsubscribeBeforeCompleted() { - AsyncSubject subject = AsyncSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - Subscription subscription = subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - - subscription.unsubscribe(); - assertNoOnNextEventsReceived(aObserver); - - subject.onNext("three"); - subject.onCompleted(); - - assertNoOnNextEventsReceived(aObserver); - } - - private void assertNoOnNextEventsReceived(Observer aObserver) { - verify(aObserver, Mockito.never()).onNext(anyString()); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testUnsubscribe() { - UnsubscribeTester.test( - new Func0>() { - @Override - public AsyncSubject call() { - return AsyncSubject.create(); - } - }, new Action1>() { - @Override - public void call(AsyncSubject DefaultSubject) { - DefaultSubject.onCompleted(); - } - }, new Action1>() { - @Override - public void call(AsyncSubject DefaultSubject) { - DefaultSubject.onError(new Throwable()); - } - }, - null - ); - } + private final Throwable testException = new Throwable(); + + @Test + public void testNeverCompleted() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + + assertNeverCompletedObserver(aObserver); + } + + private void assertNeverCompletedObserver(Observer aObserver) { + verify(aObserver, Mockito.never()).onNext(anyString()); + verify(aObserver, Mockito.never()).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testCompleted() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onCompleted(); + + assertCompletedObserver(aObserver); + } + + private void assertCompletedObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testError() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onError(testException); + subject.onNext("four"); + subject.onError(new Throwable()); + subject.onCompleted(); + + assertErrorObserver(aObserver); + } + + private void assertErrorObserver(Observer aObserver) { + verify(aObserver, Mockito.never()).onNext(anyString()); + verify(aObserver, times(1)).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testUnsubscribeBeforeCompleted() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Subscription subscription = subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + + subscription.unsubscribe(); + assertNoOnNextEventsReceived(aObserver); + + subject.onNext("three"); + subject.onCompleted(); + + assertNoOnNextEventsReceived(aObserver); + } + + private void assertNoOnNextEventsReceived(Observer aObserver) { + verify(aObserver, Mockito.never()).onNext(anyString()); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testUnsubscribe() { + UnsubscribeTester.test( + new Func0>() { + @Override + public AsyncSubject call() { + return AsyncSubject.create(); + } + }, new Action1>() { + @Override + public void call(AsyncSubject DefaultSubject) { + DefaultSubject.onCompleted(); + } + }, new Action1>() { + @Override + public void call(AsyncSubject DefaultSubject) { + DefaultSubject.onError(new Throwable()); + } + }, + null + ); + } } diff --git a/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java index eae3ffbc06..8c7a52f61d 100644 --- a/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java @@ -1,135 +1,135 @@ package rx.subjects; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + import org.junit.Test; import org.mockito.Mockito; + import rx.Observer; import rx.util.functions.Action1; import rx.util.functions.Func0; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - public class BehaviorSubjectTest { - - private final Throwable testException = new Throwable(); - - @Test - public void testThatObserverReceivesDefaultValueIfNothingWasPublished() { - BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - - assertReceivedAllEvents(aObserver); - } - - private void assertReceivedAllEvents(Observer aObserver) { - verify(aObserver, times(1)).onNext("default"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testThatObserverDoesNotReceiveDefaultValueIfSomethingWasPublished() { - BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); - - subject.onNext("one"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("two"); - subject.onNext("three"); - - assertDidNotReceiveTheDefaultValue(aObserver); - } - - private void assertDidNotReceiveTheDefaultValue(Observer aObserver) { - verify(aObserver, Mockito.never()).onNext("default"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testCompleted() { - BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onCompleted(); - - assertCompletedObserver(aObserver); - } - - private void assertCompletedObserver(Observer aObserver) { - verify(aObserver, times(1)).onNext("default"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testCompletedAfterError() { - BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onError(testException); - subject.onNext("two"); - subject.onCompleted(); - - assertErrorObserver(aObserver); - } - - private void assertErrorObserver(Observer aObserver) { - verify(aObserver, times(1)).onNext("default"); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onError(testException); - } - - @Test - public void testUnsubscribe() { - UnsubscribeTester.test( - new Func0>() { - @Override - public BehaviorSubject call() { - return BehaviorSubject.createWithDefaultValue("default"); - } - }, new Action1>() { - @Override - public void call(BehaviorSubject DefaultSubject) { - DefaultSubject.onCompleted(); - } - }, new Action1>() { - @Override - public void call(BehaviorSubject DefaultSubject) { - DefaultSubject.onError(new Throwable()); - } - }, new Action1>() { - @Override - public void call(BehaviorSubject DefaultSubject) { - DefaultSubject.onNext("one"); - } - } - ); - } + private final Throwable testException = new Throwable(); + + @Test + public void testThatObserverReceivesDefaultValueIfNothingWasPublished() { + BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + + assertReceivedAllEvents(aObserver); + } + + private void assertReceivedAllEvents(Observer aObserver) { + verify(aObserver, times(1)).onNext("default"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testThatObserverDoesNotReceiveDefaultValueIfSomethingWasPublished() { + BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); + + subject.onNext("one"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("two"); + subject.onNext("three"); + + assertDidNotReceiveTheDefaultValue(aObserver); + } + + private void assertDidNotReceiveTheDefaultValue(Observer aObserver) { + verify(aObserver, Mockito.never()).onNext("default"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testCompleted() { + BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onCompleted(); + + assertCompletedObserver(aObserver); + } + + private void assertCompletedObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("default"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testCompletedAfterError() { + BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default"); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onError(testException); + subject.onNext("two"); + subject.onCompleted(); + + assertErrorObserver(aObserver); + } + + private void assertErrorObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("default"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onError(testException); + } + + @Test + public void testUnsubscribe() { + UnsubscribeTester.test( + new Func0>() { + @Override + public BehaviorSubject call() { + return BehaviorSubject.createWithDefaultValue("default"); + } + }, new Action1>() { + @Override + public void call(BehaviorSubject DefaultSubject) { + DefaultSubject.onCompleted(); + } + }, new Action1>() { + @Override + public void call(BehaviorSubject DefaultSubject) { + DefaultSubject.onError(new Throwable()); + } + }, new Action1>() { + @Override + public void call(BehaviorSubject DefaultSubject) { + DefaultSubject.onNext("one"); + } + } + ); + } } diff --git a/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java index da6abf7c44..9c2ca321ee 100644 --- a/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java @@ -1,9 +1,21 @@ package rx.subjects; +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + import junit.framework.Assert; + import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mockito; + import rx.Notification; import rx.Observable; import rx.Observer; @@ -12,353 +24,343 @@ import rx.util.functions.Func0; import rx.util.functions.Func1; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; +public class PublishSubjectTest { -import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; + @Test + public void test() { + PublishSubject subject = PublishSubject.create(); + final AtomicReference>> actualRef = new AtomicReference>>(); -public class PublishSubjectTest { + Observable>> wNotificationsList = subject.materialize().toList(); + wNotificationsList.subscribe(new Action1>>() { + @Override + public void call(List> actual) { + actualRef.set(actual); + } + }); - @Test - public void test() { - PublishSubject subject = PublishSubject.create(); - final AtomicReference>> actualRef = new AtomicReference>>(); - - Observable>> wNotificationsList = subject.materialize().toList(); - wNotificationsList.subscribe(new Action1>>() { - @Override - public void call(List> actual) { - actualRef.set(actual); - } - }); - - Subscription sub = Observable.create(new Observable.OnSubscribeFunc() { - @Override - public Subscription onSubscribe(final Observer observer) { - final AtomicBoolean stop = new AtomicBoolean(false); - new Thread() { - @Override - public void run() { - int i = 1; - while (!stop.get()) { - observer.onNext(i++); + Subscription sub = Observable.create(new Observable.OnSubscribeFunc() { + @Override + public Subscription onSubscribe(final Observer observer) { + final AtomicBoolean stop = new AtomicBoolean(false); + new Thread() { + @Override + public void run() { + int i = 1; + while (!stop.get()) { + observer.onNext(i++); + } + observer.onCompleted(); + } + }.start(); + return new Subscription() { + @Override + public void unsubscribe() { + stop.set(true); + } + }; } - observer.onCompleted(); - } - }.start(); - return new Subscription() { - @Override - public void unsubscribe() { - stop.set(true); - } - }; - } - }).subscribe(subject); - // the subject has received an onComplete from the first subscribe because - // it is synchronous and the next subscribe won't do anything. - Observable.from(-1, -2, -3).subscribe(subject); - - List> expected = new ArrayList>(); - expected.add(new Notification(-1)); - expected.add(new Notification(-2)); - expected.add(new Notification(-3)); - expected.add(new Notification()); - Assert.assertTrue(actualRef.get().containsAll(expected)); - - sub.unsubscribe(); - } - - private final Throwable testException = new Throwable(); - - @Test - public void testCompleted() { - PublishSubject subject = PublishSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onCompleted(); - - @SuppressWarnings("unchecked") - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - - subject.onNext("four"); - subject.onCompleted(); - subject.onError(new Throwable()); - - assertCompletedObserver(aObserver); - // todo bug? assertNeverObserver(anotherObserver); - } - - private void assertCompletedObserver(Observer aObserver) { - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testError() { - PublishSubject subject = PublishSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onError(testException); - - @SuppressWarnings("unchecked") - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - - subject.onNext("four"); - subject.onError(new Throwable()); - subject.onCompleted(); - - assertErrorObserver(aObserver); - // todo bug? assertNeverObserver(anotherObserver); - } - - private void assertErrorObserver(Observer aObserver) { - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, times(1)).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testSubscribeMidSequence() { - PublishSubject subject = PublishSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - - assertObservedUntilTwo(aObserver); - - @SuppressWarnings("unchecked") - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - - subject.onNext("three"); - subject.onCompleted(); - - assertCompletedObserver(aObserver); - assertCompletedStartingWithThreeObserver(anotherObserver); - } - - private void assertCompletedStartingWithThreeObserver(Observer aObserver) { - verify(aObserver, Mockito.never()).onNext("one"); - verify(aObserver, Mockito.never()).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testUnsubscribeFirstObserver() { - PublishSubject subject = PublishSubject.create(); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - Subscription subscription = subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - - subscription.unsubscribe(); - assertObservedUntilTwo(aObserver); - - @SuppressWarnings("unchecked") - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - - subject.onNext("three"); - subject.onCompleted(); - - assertObservedUntilTwo(aObserver); - assertCompletedStartingWithThreeObserver(anotherObserver); - } - - private void assertObservedUntilTwo(Observer aObserver) { - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testUnsubscribe() { - UnsubscribeTester.test( - new Func0>() { - @Override - public PublishSubject call() { - return PublishSubject.create(); - } - }, new Action1>() { - @Override - public void call(PublishSubject DefaultSubject) { - DefaultSubject.onCompleted(); - } - }, new Action1>() { - @Override - public void call(PublishSubject DefaultSubject) { - DefaultSubject.onError(new Throwable()); - } - }, new Action1>() { - @Override - public void call(PublishSubject DefaultSubject) { - DefaultSubject.onNext("one"); - } - } - ); - } + }).subscribe(subject); + // the subject has received an onComplete from the first subscribe because + // it is synchronous and the next subscribe won't do anything. + Observable.from(-1, -2, -3).subscribe(subject); + + List> expected = new ArrayList>(); + expected.add(new Notification(-1)); + expected.add(new Notification(-2)); + expected.add(new Notification(-3)); + expected.add(new Notification()); + Assert.assertTrue(actualRef.get().containsAll(expected)); + + sub.unsubscribe(); + } - @Test - public void testNestedSubscribe() { - final PublishSubject s = PublishSubject.create(); + private final Throwable testException = new Throwable(); - final AtomicInteger countParent = new AtomicInteger(); - final AtomicInteger countChildren = new AtomicInteger(); - final AtomicInteger countTotal = new AtomicInteger(); + @Test + public void testCompleted() { + PublishSubject subject = PublishSubject.create(); - final ArrayList list = new ArrayList(); + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); - s.mapMany(new Func1>() { + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onCompleted(); - @Override - public Observable call(final Integer v) { - countParent.incrementAndGet(); + @SuppressWarnings("unchecked") + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); - // then subscribe to subject again (it will not receive the previous value) - return s.map(new Func1() { + subject.onNext("four"); + subject.onCompleted(); + subject.onError(new Throwable()); - @Override - public String call(Integer v2) { - countChildren.incrementAndGet(); - return "Parent: " + v + " Child: " + v2; - } + assertCompletedObserver(aObserver); + // todo bug? assertNeverObserver(anotherObserver); + } - }); - } + private void assertCompletedObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testError() { + PublishSubject subject = PublishSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onError(testException); + + @SuppressWarnings("unchecked") + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); + + subject.onNext("four"); + subject.onError(new Throwable()); + subject.onCompleted(); + + assertErrorObserver(aObserver); + // todo bug? assertNeverObserver(anotherObserver); + } + + private void assertErrorObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, times(1)).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testSubscribeMidSequence() { + PublishSubject subject = PublishSubject.create(); - }).subscribe(new Action1() { + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); - @Override - public void call(String v) { - countTotal.incrementAndGet(); - list.add(v); - } + subject.onNext("one"); + subject.onNext("two"); - }); + assertObservedUntilTwo(aObserver); - for (int i = 0; i < 10; i++) { - s.onNext(i); + @SuppressWarnings("unchecked") + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); + + subject.onNext("three"); + subject.onCompleted(); + + assertCompletedObserver(aObserver); + assertCompletedStartingWithThreeObserver(anotherObserver); } - s.onCompleted(); - // System.out.println("countParent: " + countParent.get()); - // System.out.println("countChildren: " + countChildren.get()); - // System.out.println("countTotal: " + countTotal.get()); + private void assertCompletedStartingWithThreeObserver(Observer aObserver) { + verify(aObserver, Mockito.never()).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testUnsubscribeFirstObserver() { + PublishSubject subject = PublishSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Subscription subscription = subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + + subscription.unsubscribe(); + assertObservedUntilTwo(aObserver); + + @SuppressWarnings("unchecked") + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); + + subject.onNext("three"); + subject.onCompleted(); - // 9+8+7+6+5+4+3+2+1+0 == 45 - assertEquals(45, list.size()); - } + assertObservedUntilTwo(aObserver); + assertCompletedStartingWithThreeObserver(anotherObserver); + } - /** - * Should be able to unsubscribe all Observers, have it stop emitting, then subscribe new ones and it start emitting again. - */ - @Test - public void testReSubscribe() { - final PublishSubject ps = PublishSubject.create(); + private void assertObservedUntilTwo(Observer aObserver) { + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onCompleted(); + } - Observer o1 = mock(Observer.class); - Subscription s1 = ps.subscribe(o1); + @Test + public void testUnsubscribe() { + UnsubscribeTester.test( + new Func0>() { + @Override + public PublishSubject call() { + return PublishSubject.create(); + } + }, new Action1>() { + @Override + public void call(PublishSubject DefaultSubject) { + DefaultSubject.onCompleted(); + } + }, new Action1>() { + @Override + public void call(PublishSubject DefaultSubject) { + DefaultSubject.onError(new Throwable()); + } + }, new Action1>() { + @Override + public void call(PublishSubject DefaultSubject) { + DefaultSubject.onNext("one"); + } + } + ); + } - // emit - ps.onNext(1); + @Test + public void testNestedSubscribe() { + final PublishSubject s = PublishSubject.create(); - // validate we got it - InOrder inOrder1 = inOrder(o1); - inOrder1.verify(o1, times(1)).onNext(1); - inOrder1.verifyNoMoreInteractions(); + final AtomicInteger countParent = new AtomicInteger(); + final AtomicInteger countChildren = new AtomicInteger(); + final AtomicInteger countTotal = new AtomicInteger(); - // unsubscribe - s1.unsubscribe(); + final ArrayList list = new ArrayList(); - // emit again but nothing will be there to receive it - ps.onNext(2); + s.mapMany(new Func1>() { - Observer o2 = mock(Observer.class); - Subscription s2 = ps.subscribe(o2); + @Override + public Observable call(final Integer v) { + countParent.incrementAndGet(); - // emit - ps.onNext(3); + // then subscribe to subject again (it will not receive the previous value) + return s.map(new Func1() { - // validate we got it - InOrder inOrder2 = inOrder(o2); - inOrder2.verify(o2, times(1)).onNext(3); - inOrder2.verifyNoMoreInteractions(); + @Override + public String call(Integer v2) { + countChildren.incrementAndGet(); + return "Parent: " + v + " Child: " + v2; + } - s2.unsubscribe(); - } + }); + } - /** - * Even if subject received an onError/onCompleted, new subscriptions should be able to restart it. - */ - @Test - public void testReSubscribeAfterTerminalState() { - final PublishSubject ps = PublishSubject.create(); + }).subscribe(new Action1() { - Observer o1 = mock(Observer.class); - Subscription s1 = ps.subscribe(o1); + @Override + public void call(String v) { + countTotal.incrementAndGet(); + list.add(v); + } - // emit - ps.onNext(1); + }); + + for (int i = 0; i < 10; i++) { + s.onNext(i); + } + s.onCompleted(); - // validate we got it - InOrder inOrder1 = inOrder(o1); - inOrder1.verify(o1, times(1)).onNext(1); - inOrder1.verifyNoMoreInteractions(); + // System.out.println("countParent: " + countParent.get()); + // System.out.println("countChildren: " + countChildren.get()); + // System.out.println("countTotal: " + countTotal.get()); - // unsubscribe - s1.unsubscribe(); + // 9+8+7+6+5+4+3+2+1+0 == 45 + assertEquals(45, list.size()); + } - ps.onCompleted(); + /** + * Should be able to unsubscribe all Observers, have it stop emitting, then subscribe new ones and it start emitting again. + */ + @Test + public void testReSubscribe() { + final PublishSubject ps = PublishSubject.create(); - // emit again but nothing will be there to receive it - ps.onNext(2); + Observer o1 = mock(Observer.class); + Subscription s1 = ps.subscribe(o1); - Observer o2 = mock(Observer.class); - Subscription s2 = ps.subscribe(o2); + // emit + ps.onNext(1); - // emit - ps.onNext(3); + // validate we got it + InOrder inOrder1 = inOrder(o1); + inOrder1.verify(o1, times(1)).onNext(1); + inOrder1.verifyNoMoreInteractions(); - // validate we got it - InOrder inOrder2 = inOrder(o2); - inOrder2.verify(o2, times(1)).onNext(3); - inOrder2.verifyNoMoreInteractions(); + // unsubscribe + s1.unsubscribe(); - s2.unsubscribe(); - } + // emit again but nothing will be there to receive it + ps.onNext(2); + + Observer o2 = mock(Observer.class); + Subscription s2 = ps.subscribe(o2); + + // emit + ps.onNext(3); + + // validate we got it + InOrder inOrder2 = inOrder(o2); + inOrder2.verify(o2, times(1)).onNext(3); + inOrder2.verifyNoMoreInteractions(); + + s2.unsubscribe(); + } + + /** + * Even if subject received an onError/onCompleted, new subscriptions should be able to restart it. + */ + @Test + public void testReSubscribeAfterTerminalState() { + final PublishSubject ps = PublishSubject.create(); + + Observer o1 = mock(Observer.class); + Subscription s1 = ps.subscribe(o1); + + // emit + ps.onNext(1); + + // validate we got it + InOrder inOrder1 = inOrder(o1); + inOrder1.verify(o1, times(1)).onNext(1); + inOrder1.verifyNoMoreInteractions(); + + // unsubscribe + s1.unsubscribe(); + + ps.onCompleted(); + + // emit again but nothing will be there to receive it + ps.onNext(2); + + Observer o2 = mock(Observer.class); + Subscription s2 = ps.subscribe(o2); + + // emit + ps.onNext(3); + + // validate we got it + InOrder inOrder2 = inOrder(o2); + inOrder2.verify(o2, times(1)).onNext(3); + inOrder2.verifyNoMoreInteractions(); + + s2.unsubscribe(); + } } diff --git a/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java b/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java index dc0298a7e3..728f9e53f1 100644 --- a/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java @@ -1,169 +1,170 @@ package rx.subjects; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mockito; + import rx.Observer; import rx.Subscription; import rx.util.functions.Action1; import rx.util.functions.Func0; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - public class ReplaySubjectTest { - private final Throwable testException = new Throwable(); + private final Throwable testException = new Throwable(); - @SuppressWarnings("unchecked") - @Test - public void testCompleted() { - ReplaySubject subject = ReplaySubject.create(); + @SuppressWarnings("unchecked") + @Test + public void testCompleted() { + ReplaySubject subject = ReplaySubject.create(); - Observer o1 = mock(Observer.class); - subject.subscribe(o1); + Observer o1 = mock(Observer.class); + subject.subscribe(o1); - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onCompleted(); + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onCompleted(); - subject.onNext("four"); - subject.onCompleted(); - subject.onError(new Throwable()); + subject.onNext("four"); + subject.onCompleted(); + subject.onError(new Throwable()); - assertCompletedObserver(o1); + assertCompletedObserver(o1); - // assert that subscribing a 2nd time gets the same data - Observer o2 = mock(Observer.class); - subject.subscribe(o2); - assertCompletedObserver(o2); - } + // assert that subscribing a 2nd time gets the same data + Observer o2 = mock(Observer.class); + subject.subscribe(o2); + assertCompletedObserver(o2); + } - private void assertCompletedObserver(Observer aObserver) { - InOrder inOrder = inOrder(aObserver); + private void assertCompletedObserver(Observer aObserver) { + InOrder inOrder = inOrder(aObserver); - inOrder.verify(aObserver, times(1)).onNext("one"); - inOrder.verify(aObserver, times(1)).onNext("two"); - inOrder.verify(aObserver, times(1)).onNext("three"); - inOrder.verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - inOrder.verify(aObserver, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @SuppressWarnings("unchecked") - @Test - public void testError() { - ReplaySubject subject = ReplaySubject.create(); - - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - subject.onNext("three"); - subject.onError(testException); - - subject.onNext("four"); - subject.onError(new Throwable()); - subject.onCompleted(); - - assertErrorObserver(aObserver); - - aObserver = mock(Observer.class); - subject.subscribe(aObserver); - assertErrorObserver(aObserver); - } - - private void assertErrorObserver(Observer aObserver) { - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, times(1)).onError(testException); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @SuppressWarnings("unchecked") - @Test - public void testSubscribeMidSequence() { - ReplaySubject subject = ReplaySubject.create(); - - Observer aObserver = mock(Observer.class); - subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - - assertObservedUntilTwo(aObserver); - - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - assertObservedUntilTwo(anotherObserver); - - subject.onNext("three"); - subject.onCompleted(); - - assertCompletedObserver(aObserver); - assertCompletedObserver(anotherObserver); - } - - @SuppressWarnings("unchecked") - @Test - public void testUnsubscribeFirstObserver() { - ReplaySubject subject = ReplaySubject.create(); - - Observer aObserver = mock(Observer.class); - Subscription subscription = subject.subscribe(aObserver); - - subject.onNext("one"); - subject.onNext("two"); - - subscription.unsubscribe(); - assertObservedUntilTwo(aObserver); - - Observer anotherObserver = mock(Observer.class); - subject.subscribe(anotherObserver); - assertObservedUntilTwo(anotherObserver); - - subject.onNext("three"); - subject.onCompleted(); - - assertObservedUntilTwo(aObserver); - assertCompletedObserver(anotherObserver); - } - - private void assertObservedUntilTwo(Observer aObserver) { - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, Mockito.never()).onCompleted(); - } - - @Test - public void testUnsubscribe() { - UnsubscribeTester.test( - new Func0>() { - @Override - public ReplaySubject call() { - return ReplaySubject.create(); - } - }, new Action1>() { - @Override - public void call(ReplaySubject repeatSubject) { - repeatSubject.onCompleted(); - } - }, new Action1>() { - @Override - public void call(ReplaySubject repeatSubject) { - repeatSubject.onError(new Throwable()); - } - }, new Action1>() { - @Override - public void call(ReplaySubject repeatSubject) { - repeatSubject.onNext("one"); - } - } - ); - } + inOrder.verify(aObserver, times(1)).onNext("one"); + inOrder.verify(aObserver, times(1)).onNext("two"); + inOrder.verify(aObserver, times(1)).onNext("three"); + inOrder.verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @SuppressWarnings("unchecked") + @Test + public void testError() { + ReplaySubject subject = ReplaySubject.create(); + + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onError(testException); + + subject.onNext("four"); + subject.onError(new Throwable()); + subject.onCompleted(); + + assertErrorObserver(aObserver); + + aObserver = mock(Observer.class); + subject.subscribe(aObserver); + assertErrorObserver(aObserver); + } + + private void assertErrorObserver(Observer aObserver) { + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, times(1)).onError(testException); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @SuppressWarnings("unchecked") + @Test + public void testSubscribeMidSequence() { + ReplaySubject subject = ReplaySubject.create(); + + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + + assertObservedUntilTwo(aObserver); + + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); + assertObservedUntilTwo(anotherObserver); + + subject.onNext("three"); + subject.onCompleted(); + + assertCompletedObserver(aObserver); + assertCompletedObserver(anotherObserver); + } + + @SuppressWarnings("unchecked") + @Test + public void testUnsubscribeFirstObserver() { + ReplaySubject subject = ReplaySubject.create(); + + Observer aObserver = mock(Observer.class); + Subscription subscription = subject.subscribe(aObserver); + + subject.onNext("one"); + subject.onNext("two"); + + subscription.unsubscribe(); + assertObservedUntilTwo(aObserver); + + Observer anotherObserver = mock(Observer.class); + subject.subscribe(anotherObserver); + assertObservedUntilTwo(anotherObserver); + + subject.onNext("three"); + subject.onCompleted(); + + assertObservedUntilTwo(aObserver); + assertCompletedObserver(anotherObserver); + } + + private void assertObservedUntilTwo(Observer aObserver) { + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onCompleted(); + } + + @Test + public void testUnsubscribe() { + UnsubscribeTester.test( + new Func0>() { + @Override + public ReplaySubject call() { + return ReplaySubject.create(); + } + }, new Action1>() { + @Override + public void call(ReplaySubject repeatSubject) { + repeatSubject.onCompleted(); + } + }, new Action1>() { + @Override + public void call(ReplaySubject repeatSubject) { + repeatSubject.onError(new Throwable()); + } + }, new Action1>() { + @Override + public void call(ReplaySubject repeatSubject) { + repeatSubject.onNext("one"); + } + } + ); + } } diff --git a/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java index 3bb8b718a0..e7257d9f1a 100644 --- a/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java +++ b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java @@ -1,70 +1,70 @@ package rx.subscriptions; +import static org.junit.Assert.*; + +import java.util.concurrent.atomic.AtomicInteger; + import org.junit.Test; + import rx.Subscription; import rx.util.CompositeException; -import java.util.concurrent.atomic.AtomicInteger; +public class CompositeSubscriptionTest { -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; + @Test + public void testSuccess() { + final AtomicInteger counter = new AtomicInteger(); + CompositeSubscription s = new CompositeSubscription(); + s.add(new Subscription() { -public class CompositeSubscriptionTest { + @Override + public void unsubscribe() { + counter.incrementAndGet(); + } + }); + + s.add(new Subscription() { + + @Override + public void unsubscribe() { + counter.incrementAndGet(); + } + }); - @Test - public void testSuccess() { - final AtomicInteger counter = new AtomicInteger(); - CompositeSubscription s = new CompositeSubscription(); - s.add(new Subscription() { - - @Override - public void unsubscribe() { - counter.incrementAndGet(); - } - }); - - s.add(new Subscription() { - - @Override - public void unsubscribe() { - counter.incrementAndGet(); - } - }); - - s.unsubscribe(); - - assertEquals(2, counter.get()); - } - - @Test - public void testException() { - final AtomicInteger counter = new AtomicInteger(); - CompositeSubscription s = new CompositeSubscription(); - s.add(new Subscription() { - - @Override - public void unsubscribe() { - throw new RuntimeException("failed on first one"); - } - }); - - s.add(new Subscription() { - - @Override - public void unsubscribe() { - counter.incrementAndGet(); - } - }); - - try { - s.unsubscribe(); - fail("Expecting an exception"); - } catch (CompositeException e) { - // we expect this - assertEquals(1, e.getExceptions().size()); + s.unsubscribe(); + + assertEquals(2, counter.get()); } - // we should still have unsubscribed to the second one - assertEquals(1, counter.get()); - } + @Test + public void testException() { + final AtomicInteger counter = new AtomicInteger(); + CompositeSubscription s = new CompositeSubscription(); + s.add(new Subscription() { + + @Override + public void unsubscribe() { + throw new RuntimeException("failed on first one"); + } + }); + + s.add(new Subscription() { + + @Override + public void unsubscribe() { + counter.incrementAndGet(); + } + }); + + try { + s.unsubscribe(); + fail("Expecting an exception"); + } catch (CompositeException e) { + // we expect this + assertEquals(1, e.getExceptions().size()); + } + + // we should still have unsubscribed to the second one + assertEquals(1, counter.get()); + } } diff --git a/rxjava-core/src/test/java/rx/subscriptions/SerialSubscriptionTests.java b/rxjava-core/src/test/java/rx/subscriptions/SerialSubscriptionTests.java index 4ffc5cef27..cd63539c87 100644 --- a/rxjava-core/src/test/java/rx/subscriptions/SerialSubscriptionTests.java +++ b/rxjava-core/src/test/java/rx/subscriptions/SerialSubscriptionTests.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -15,12 +15,13 @@ */ package rx.subscriptions; +import static org.mockito.Mockito.*; + import org.junit.Before; import org.junit.Test; import org.mockito.MockitoAnnotations; -import rx.Subscription; -import static org.mockito.Mockito.*; +import rx.Subscription; public class SerialSubscriptionTests { private SerialSubscription serialSubscription; diff --git a/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java b/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java index 5ac0242a95..806a5e52e9 100644 --- a/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java +++ b/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java @@ -1,20 +1,21 @@ package rx.subscriptions; +import static org.mockito.Mockito.*; +import static rx.subscriptions.Subscriptions.*; + import org.junit.Test; + import rx.Subscription; import rx.util.functions.Action0; -import static org.mockito.Mockito.*; -import static rx.subscriptions.Subscriptions.create; - public class SubscriptionsTest { - @Test - public void testUnsubscribeOnlyOnce() { - Action0 unsubscribe = mock(Action0.class); - Subscription subscription = create(unsubscribe); - subscription.unsubscribe(); - subscription.unsubscribe(); - verify(unsubscribe, times(1)).call(); - } + @Test + public void testUnsubscribeOnlyOnce() { + Action0 unsubscribe = mock(Action0.class); + Subscription subscription = create(unsubscribe); + subscription.unsubscribe(); + subscription.unsubscribe(); + verify(unsubscribe, times(1)).call(); + } } diff --git a/rxjava-core/src/test/java/rx/test/OperatorTester.java b/rxjava-core/src/test/java/rx/test/OperatorTester.java index ab64251583..6bd905c264 100644 --- a/rxjava-core/src/test/java/rx/test/OperatorTester.java +++ b/rxjava-core/src/test/java/rx/test/OperatorTester.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -15,13 +15,13 @@ */ package rx.test; +import java.util.concurrent.TimeUnit; + import rx.Scheduler; import rx.Subscription; import rx.util.functions.Action0; import rx.util.functions.Func2; -import java.util.concurrent.TimeUnit; - /** * Common utility functions for testing operator implementations. */ @@ -36,59 +36,59 @@ public class OperatorTester { * If they are truly useful for everyone to use then an "rx.testing" package may make sense. */ - private OperatorTester() { - } + private OperatorTester() { + } - /** - * Used for mocking of Schedulers since many Scheduler implementations are static/final. - * - * @param underlying - * @return - */ - public static Scheduler forwardingScheduler(Scheduler underlying) { - return new ForwardingScheduler(underlying); - } + /** + * Used for mocking of Schedulers since many Scheduler implementations are static/final. + * + * @param underlying + * @return + */ + public static Scheduler forwardingScheduler(Scheduler underlying) { + return new ForwardingScheduler(underlying); + } - public static class ForwardingScheduler extends Scheduler { - private final Scheduler underlying; + public static class ForwardingScheduler extends Scheduler { + private final Scheduler underlying; - public ForwardingScheduler(Scheduler underlying) { - this.underlying = underlying; - } + public ForwardingScheduler(Scheduler underlying) { + this.underlying = underlying; + } - @Override - public Subscription schedule(Action0 action) { - return underlying.schedule(action); - } + @Override + public Subscription schedule(Action0 action) { + return underlying.schedule(action); + } - @Override - public Subscription schedule(T state, Func2 action) { - return underlying.schedule(state, action); - } + @Override + public Subscription schedule(T state, Func2 action) { + return underlying.schedule(state, action); + } - @Override - public Subscription schedule(Action0 action, long dueTime, TimeUnit unit) { - return underlying.schedule(action, dueTime, unit); - } + @Override + public Subscription schedule(Action0 action, long dueTime, TimeUnit unit) { + return underlying.schedule(action, dueTime, unit); + } - @Override - public Subscription schedule(T state, Func2 action, long dueTime, TimeUnit unit) { - return underlying.schedule(state, action, dueTime, unit); - } + @Override + public Subscription schedule(T state, Func2 action, long dueTime, TimeUnit unit) { + return underlying.schedule(state, action, dueTime, unit); + } - @Override - public Subscription schedulePeriodically(Action0 action, long initialDelay, long period, TimeUnit unit) { - return underlying.schedulePeriodically(action, initialDelay, period, unit); - } + @Override + public Subscription schedulePeriodically(Action0 action, long initialDelay, long period, TimeUnit unit) { + return underlying.schedulePeriodically(action, initialDelay, period, unit); + } - @Override - public Subscription schedulePeriodically(T state, Func2 action, long initialDelay, long period, TimeUnit unit) { - return underlying.schedulePeriodically(state, action, initialDelay, period, unit); - } + @Override + public Subscription schedulePeriodically(T state, Func2 action, long initialDelay, long period, TimeUnit unit) { + return underlying.schedulePeriodically(state, action, initialDelay, period, unit); + } - @Override - public long now() { - return underlying.now(); + @Override + public long now() { + return underlying.now(); + } } - } } \ No newline at end of file diff --git a/rxjava-core/src/test/java/rx/util/RangeTest.java b/rxjava-core/src/test/java/rx/util/RangeTest.java index 03ec6eef19..c788b32e82 100644 --- a/rxjava-core/src/test/java/rx/util/RangeTest.java +++ b/rxjava-core/src/test/java/rx/util/RangeTest.java @@ -1,50 +1,50 @@ package rx.util; -import org.junit.Test; +import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static org.junit.Assert.assertEquals; +import org.junit.Test; public class RangeTest { - @Test - public void testSimpleRange() { - assertEquals(Arrays.asList(1, 2, 3, 4), toList(Range.create(1, 5))); - } - - @Test - public void testRangeWithStep() { - assertEquals(Arrays.asList(1, 3, 5, 7, 9), toList(Range.createWithStep(1, 10, 2))); - } - - @Test - public void testRangeWithCount() { - assertEquals(Arrays.asList(1, 2, 3, 4, 5), toList(Range.createWithCount(1, 5))); - } - - @Test - public void testRangeWithCount2() { - assertEquals(Arrays.asList(2, 3, 4, 5), toList(Range.createWithCount(2, 4))); - } - - @Test - public void testRangeWithCount3() { - assertEquals(Arrays.asList(0, 1, 2, 3), toList(Range.createWithCount(0, 4))); - } - - @Test - public void testRangeWithCount4() { - assertEquals(Arrays.asList(10, 11, 12, 13, 14), toList(Range.createWithCount(10, 5))); - } - - private static List toList(Iterable iterable) { - List result = new ArrayList(); - for (T element : iterable) { - result.add(element); + @Test + public void testSimpleRange() { + assertEquals(Arrays.asList(1, 2, 3, 4), toList(Range.create(1, 5))); + } + + @Test + public void testRangeWithStep() { + assertEquals(Arrays.asList(1, 3, 5, 7, 9), toList(Range.createWithStep(1, 10, 2))); + } + + @Test + public void testRangeWithCount() { + assertEquals(Arrays.asList(1, 2, 3, 4, 5), toList(Range.createWithCount(1, 5))); + } + + @Test + public void testRangeWithCount2() { + assertEquals(Arrays.asList(2, 3, 4, 5), toList(Range.createWithCount(2, 4))); + } + + @Test + public void testRangeWithCount3() { + assertEquals(Arrays.asList(0, 1, 2, 3), toList(Range.createWithCount(0, 4))); + } + + @Test + public void testRangeWithCount4() { + assertEquals(Arrays.asList(10, 11, 12, 13, 14), toList(Range.createWithCount(10, 5))); + } + + private static List toList(Iterable iterable) { + List result = new ArrayList(); + for (T element : iterable) { + result.add(element); + } + return result; } - return result; - } } From 6de7fd15b7b4f42263d847fe462552110c405bb3 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 4 Nov 2013 20:03:56 -0800 Subject: [PATCH 226/333] Move last 6 remaining unit tests out. --- .../java/rx/operators/OperationToFuture.java | 55 --------------- .../rx/operators/OperationToIterator.java | 50 -------------- .../rx/operators/OperationToFutureTest.java | 68 +++++++++++++++++++ .../rx/operators/OperationToIteratorTest.java | 62 +++++++++++++++++ 4 files changed, 130 insertions(+), 105 deletions(-) create mode 100644 rxjava-core/src/test/java/rx/operators/OperationToFutureTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationToIteratorTest.java diff --git a/rxjava-core/src/main/java/rx/operators/OperationToFuture.java b/rxjava-core/src/main/java/rx/operators/OperationToFuture.java index a3ecc49efe..d4433da9d6 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToFuture.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToFuture.java @@ -15,9 +15,6 @@ */ package rx.operators; -import static org.junit.Assert.*; - -import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -25,13 +22,9 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; -import org.junit.Test; - import rx.Observable; -import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.subscriptions.Subscriptions; /** * Returns a Future representing the single value emitted by an Observable. @@ -136,52 +129,4 @@ private T getValue() throws ExecutionException { } - @Test - public void testToFuture() throws InterruptedException, ExecutionException { - Observable obs = Observable.from("one"); - Future f = toFuture(obs); - assertEquals("one", f.get()); - } - - @Test - public void testToFutureList() throws InterruptedException, ExecutionException { - Observable obs = Observable.from("one", "two", "three"); - Future> f = toFuture(obs.toList()); - assertEquals("one", f.get().get(0)); - assertEquals("two", f.get().get(1)); - assertEquals("three", f.get().get(2)); - } - - @Test(expected = ExecutionException.class) - public void testExceptionWithMoreThanOneElement() throws InterruptedException, ExecutionException { - Observable obs = Observable.from("one", "two"); - Future f = toFuture(obs); - assertEquals("one", f.get()); - // we expect an exception since there are more than 1 element - } - - @Test - public void testToFutureWithException() { - Observable obs = Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(Observer observer) { - observer.onNext("one"); - observer.onError(new TestException()); - return Subscriptions.empty(); - } - }); - - Future f = toFuture(obs); - try { - f.get(); - fail("expected exception"); - } catch (Throwable e) { - assertEquals(TestException.class, e.getCause().getClass()); - } - } - - private static class TestException extends RuntimeException { - private static final long serialVersionUID = 1L; - } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationToIterator.java b/rxjava-core/src/main/java/rx/operators/OperationToIterator.java index 59debafcb9..2fcd51872e 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToIterator.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToIterator.java @@ -15,20 +15,13 @@ */ package rx.operators; -import static org.junit.Assert.*; - import java.util.Iterator; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -import org.junit.Test; - import rx.Notification; import rx.Observable; -import rx.Observable.OnSubscribeFunc; import rx.Observer; -import rx.Subscription; -import rx.subscriptions.Subscriptions; import rx.util.Exceptions; /** @@ -108,47 +101,4 @@ public void remove() { }; } - @Test - public void testToIterator() { - Observable obs = Observable.from("one", "two", "three"); - - Iterator it = toIterator(obs); - - assertEquals(true, it.hasNext()); - assertEquals("one", it.next()); - - assertEquals(true, it.hasNext()); - assertEquals("two", it.next()); - - assertEquals(true, it.hasNext()); - assertEquals("three", it.next()); - - assertEquals(false, it.hasNext()); - - } - - @Test(expected = TestException.class) - public void testToIteratorWithException() { - Observable obs = Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(Observer observer) { - observer.onNext("one"); - observer.onError(new TestException()); - return Subscriptions.empty(); - } - }); - - Iterator it = toIterator(obs); - - assertEquals(true, it.hasNext()); - assertEquals("one", it.next()); - - assertEquals(true, it.hasNext()); - it.next(); - } - - private static class TestException extends RuntimeException { - private static final long serialVersionUID = 1L; - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationToFutureTest.java b/rxjava-core/src/test/java/rx/operators/OperationToFutureTest.java new file mode 100644 index 0000000000..c18131e5c7 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToFutureTest.java @@ -0,0 +1,68 @@ +package rx.operators; + +import static org.junit.Assert.*; +import static rx.operators.OperationToFuture.*; + +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.junit.Test; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.Subscriptions; + +public class OperationToFutureTest { + + @Test + public void testToFuture() throws InterruptedException, ExecutionException { + Observable obs = Observable.from("one"); + Future f = toFuture(obs); + assertEquals("one", f.get()); + } + + @Test + public void testToFutureList() throws InterruptedException, ExecutionException { + Observable obs = Observable.from("one", "two", "three"); + Future> f = toFuture(obs.toList()); + assertEquals("one", f.get().get(0)); + assertEquals("two", f.get().get(1)); + assertEquals("three", f.get().get(2)); + } + + @Test(expected = ExecutionException.class) + public void testExceptionWithMoreThanOneElement() throws InterruptedException, ExecutionException { + Observable obs = Observable.from("one", "two"); + Future f = toFuture(obs); + assertEquals("one", f.get()); + // we expect an exception since there are more than 1 element + } + + @Test + public void testToFutureWithException() { + Observable obs = Observable.create(new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onError(new TestException()); + return Subscriptions.empty(); + } + }); + + Future f = toFuture(obs); + try { + f.get(); + fail("expected exception"); + } catch (Throwable e) { + assertEquals(TestException.class, e.getCause().getClass()); + } + } + + private static class TestException extends RuntimeException { + private static final long serialVersionUID = 1L; + } +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationToIteratorTest.java b/rxjava-core/src/test/java/rx/operators/OperationToIteratorTest.java new file mode 100644 index 0000000000..1994bd4a0c --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToIteratorTest.java @@ -0,0 +1,62 @@ +package rx.operators; + +import static org.junit.Assert.*; +import static rx.operators.OperationToIterator.*; + +import java.util.Iterator; + +import org.junit.Test; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.Subscriptions; + +public class OperationToIteratorTest { + + @Test + public void testToIterator() { + Observable obs = Observable.from("one", "two", "three"); + + Iterator it = toIterator(obs); + + assertEquals(true, it.hasNext()); + assertEquals("one", it.next()); + + assertEquals(true, it.hasNext()); + assertEquals("two", it.next()); + + assertEquals(true, it.hasNext()); + assertEquals("three", it.next()); + + assertEquals(false, it.hasNext()); + + } + + @Test(expected = TestException.class) + public void testToIteratorWithException() { + Observable obs = Observable.create(new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(Observer observer) { + observer.onNext("one"); + observer.onError(new TestException()); + return Subscriptions.empty(); + } + }); + + Iterator it = toIterator(obs); + + assertEquals(true, it.hasNext()); + assertEquals("one", it.next()); + + assertEquals(true, it.hasNext()); + it.next(); + } + + private static class TestException extends RuntimeException { + private static final long serialVersionUID = 1L; + } + +} From 2319d0a00ad913699090a5b91c534d87e14efc06 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 4 Nov 2013 20:07:34 -0800 Subject: [PATCH 227/333] Add missing license headers. --- .../groovy/rx/lang/groovy/TestParallel.groovy | 15 +++++++++++++++ .../rx/lang/scala/examples/MovieLibUsage.java | 15 +++++++++++++++ .../scala/rx/lang/scala/examples/Olympics.scala | 15 +++++++++++++++ .../main/scala/rx/lang/scala/Notification.scala | 15 +++++++++++++++ .../src/main/scala/rx/lang/scala/Scheduler.scala | 15 +++++++++++++++ .../rx/lang/scala/concurrency/Schedulers.scala | 15 +++++++++++++++ .../rx/lang/scala/concurrency/TestScheduler.scala | 15 +++++++++++++++ .../scala/rx/lang/scala/observables/package.scala | 15 +++++++++++++++ .../scala/rx/lang/scala/subjects/package.scala | 15 +++++++++++++++ .../scala/rx/lang/scala/CompletenessTest.scala | 15 +++++++++++++++ .../rx/android/concurrency/AndroidSchedulers.java | 15 +++++++++++++++ .../concurrency/HandlerThreadScheduler.java | 15 +++++++++++++++ .../rx/android/observables/AndroidObservable.java | 15 +++++++++++++++ .../OperationObserveFromAndroidComponent.java | 15 +++++++++++++++ .../http/examples/ExampleObservableHttp.java | 15 +++++++++++++++ .../src/main/java/rx/operators/OperationCast.java | 15 +++++++++++++++ .../rx/operators/OperationDefaultIfEmpty.java | 15 +++++++++++++++ .../MultipleAssignmentSubscription.java | 15 +++++++++++++++ .../src/test/java/rx/CombineLatestTests.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/ConcatTests.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/CovarianceTest.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/EventStream.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/GroupByTests.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/IntervalDemo.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/MergeTests.java | 15 +++++++++++++++ .../src/test/java/rx/ObservableWindowTests.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/ObserveOnTests.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/ReduceTests.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/RefCountTests.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/ScanTests.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/StartWithTests.java | 15 +++++++++++++++ .../src/test/java/rx/ThrottleFirstTests.java | 15 +++++++++++++++ .../src/test/java/rx/ThrottleLastTests.java | 15 +++++++++++++++ .../test/java/rx/ThrottleWithTimeoutTests.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/ZipTests.java | 15 +++++++++++++++ .../concurrency/CurrentThreadSchedulerTest.java | 15 +++++++++++++++ .../rx/concurrency/ImmediateSchedulerTest.java | 15 +++++++++++++++ .../rx/observables/BlockingObservableTest.java | 15 +++++++++++++++ .../test/java/rx/operators/OperationAllTest.java | 15 +++++++++++++++ .../test/java/rx/operators/OperationAnyTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationAverageTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationBufferTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationCacheTest.java | 15 +++++++++++++++ .../test/java/rx/operators/OperationCastTest.java | 15 +++++++++++++++ .../rx/operators/OperationCombineLatestTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationConcatTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationDebounceTest.java | 15 +++++++++++++++ .../rx/operators/OperationDefaultIfEmptyTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationDeferTest.java | 15 +++++++++++++++ .../rx/operators/OperationDematerializeTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationDistinctTest.java | 15 +++++++++++++++ .../OperationDistinctUntilChangedTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationElementAtTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationFilterTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationFinallyTest.java | 15 +++++++++++++++ .../rx/operators/OperationFirstOrDefaultTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationGroupByTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationIntervalTest.java | 15 +++++++++++++++ .../test/java/rx/operators/OperationMapTest.java | 15 +++++++++++++++ .../rx/operators/OperationMaterializeTest.java | 15 +++++++++++++++ .../operators/OperationMergeDelayErrorTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationMergeTest.java | 15 +++++++++++++++ .../rx/operators/OperationMostRecentTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationMulticastTest.java | 15 +++++++++++++++ .../test/java/rx/operators/OperationNextTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationObserveOnTest.java | 15 +++++++++++++++ ...OperationOnErrorResumeNextViaFunctionTest.java | 15 +++++++++++++++ ...erationOnErrorResumeNextViaObservableTest.java | 15 +++++++++++++++ .../rx/operators/OperationOnErrorReturnTest.java | 15 +++++++++++++++ ...ionOnExceptionResumeNextViaObservableTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationParallelTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationRetryTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationSampleTest.java | 15 +++++++++++++++ .../test/java/rx/operators/OperationScanTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationSkipLastTest.java | 15 +++++++++++++++ .../test/java/rx/operators/OperationSkipTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationSkipWhileTest.java | 15 +++++++++++++++ .../rx/operators/OperationSubscribeOnTest.java | 15 +++++++++++++++ .../test/java/rx/operators/OperationSumTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationSwitchTest.java | 15 +++++++++++++++ .../rx/operators/OperationSynchronizeTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationTakeLastTest.java | 15 +++++++++++++++ .../test/java/rx/operators/OperationTakeTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationTakeUntilTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationTakeWhileTest.java | 15 +++++++++++++++ .../rx/operators/OperationThrottleFirstTest.java | 15 +++++++++++++++ .../rx/operators/OperationTimeIntervalTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationToFutureTest.java | 15 +++++++++++++++ .../rx/operators/OperationToIteratorTest.java | 15 +++++++++++++++ .../OperationToObservableFutureTest.java | 15 +++++++++++++++ .../OperationToObservableIterableTest.java | 15 +++++++++++++++ .../operators/OperationToObservableListTest.java | 15 +++++++++++++++ .../OperationToObservableSortedListTest.java | 15 +++++++++++++++ .../java/rx/operators/OperationWindowTest.java | 15 +++++++++++++++ .../test/java/rx/operators/OperationZipTest.java | 15 +++++++++++++++ .../java/rx/operators/OperatorTesterTest.java | 15 +++++++++++++++ .../operators/SafeObservableSubscriptionTest.java | 15 +++++++++++++++ .../rx/operators/SynchronizedObserverTest.java | 15 +++++++++++++++ .../src/test/java/rx/operators/TakeWhileTest.java | 15 +++++++++++++++ .../rx/operators/TimeIntervalObserverTest.java | 15 +++++++++++++++ .../test/java/rx/plugins/RxJavaPluginsTest.java | 15 +++++++++++++++ .../test/java/rx/subjects/AsyncSubjectTest.java | 15 +++++++++++++++ .../java/rx/subjects/BehaviorSubjectTest.java | 15 +++++++++++++++ .../test/java/rx/subjects/PublishSubjectTest.java | 15 +++++++++++++++ .../test/java/rx/subjects/ReplaySubjectTest.java | 15 +++++++++++++++ .../subscriptions/CompositeSubscriptionTest.java | 15 +++++++++++++++ .../java/rx/subscriptions/SubscriptionsTest.java | 15 +++++++++++++++ rxjava-core/src/test/java/rx/util/RangeTest.java | 15 +++++++++++++++ 108 files changed, 1620 insertions(+) diff --git a/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/TestParallel.groovy b/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/TestParallel.groovy index c2e2eb52bd..509b7b0ca5 100644 --- a/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/TestParallel.groovy +++ b/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/TestParallel.groovy @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.groovy import org.junit.Test diff --git a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java index cacd48bd79..84920e0d12 100644 --- a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java +++ b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.examples; import org.junit.Test; diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala index d2a0cdcbcb..699523ea55 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.examples import rx.lang.scala.Observable diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala index 27fb82a69e..133157d5ca 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala index c717a94af5..1165bd4620 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala import java.util.Date diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala index 8ba88ba2d0..960df660d4 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.concurrency import java.util.concurrent.Executor diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala index 7023ca2f4e..a8090a887e 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.concurrency import scala.concurrent.duration.Duration diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala index 8507f0a54c..2b43860b53 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala index ec096e92eb..07076772f5 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala /** diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala index f38ac0d521..c5c13d3070 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala import java.util.Calendar diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/AndroidSchedulers.java b/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/AndroidSchedulers.java index 36a8154d16..0b238b1644 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/AndroidSchedulers.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/AndroidSchedulers.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.android.concurrency; import android.os.Handler; diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/HandlerThreadScheduler.java b/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/HandlerThreadScheduler.java index cd1af987ae..ae01d17c1a 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/HandlerThreadScheduler.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/HandlerThreadScheduler.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.android.concurrency; import android.os.Handler; diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java b/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java index e411074be3..c70ba970c3 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.android.observables; import rx.Observable; diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java index a1aaff5354..cfce38f8bb 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.any; diff --git a/rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java b/rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java index 3f396a3894..4fe7e0549a 100644 --- a/rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java +++ b/rxjava-contrib/rxjava-apache-http/src/test/java/rx/apache/http/examples/ExampleObservableHttp.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.apache.http.examples; import java.io.IOException; diff --git a/rxjava-core/src/main/java/rx/operators/OperationCast.java b/rxjava-core/src/main/java/rx/operators/OperationCast.java index 0643624a02..dc54c204f6 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationCast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationCast.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import rx.Observable; diff --git a/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java b/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java index 7c08aca675..7bc74ac156 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import rx.Observable; diff --git a/rxjava-core/src/main/java/rx/subscriptions/MultipleAssignmentSubscription.java b/rxjava-core/src/main/java/rx/subscriptions/MultipleAssignmentSubscription.java index 2af6501425..74ed285f77 100644 --- a/rxjava-core/src/main/java/rx/subscriptions/MultipleAssignmentSubscription.java +++ b/rxjava-core/src/main/java/rx/subscriptions/MultipleAssignmentSubscription.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.subscriptions; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/rxjava-core/src/test/java/rx/CombineLatestTests.java b/rxjava-core/src/test/java/rx/CombineLatestTests.java index 78dbd4c4e3..c52daa3870 100644 --- a/rxjava-core/src/test/java/rx/CombineLatestTests.java +++ b/rxjava-core/src/test/java/rx/CombineLatestTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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 org.junit.Test; diff --git a/rxjava-core/src/test/java/rx/ConcatTests.java b/rxjava-core/src/test/java/rx/ConcatTests.java index a83e9f5a9b..80f7eb5ef7 100644 --- a/rxjava-core/src/test/java/rx/ConcatTests.java +++ b/rxjava-core/src/test/java/rx/ConcatTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.*; diff --git a/rxjava-core/src/test/java/rx/CovarianceTest.java b/rxjava-core/src/test/java/rx/CovarianceTest.java index 01f2030c52..48ca64f61e 100644 --- a/rxjava-core/src/test/java/rx/CovarianceTest.java +++ b/rxjava-core/src/test/java/rx/CovarianceTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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 java.util.ArrayList; diff --git a/rxjava-core/src/test/java/rx/EventStream.java b/rxjava-core/src/test/java/rx/EventStream.java index 5fbffba528..f32787bac1 100644 --- a/rxjava-core/src/test/java/rx/EventStream.java +++ b/rxjava-core/src/test/java/rx/EventStream.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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 java.util.Collections; diff --git a/rxjava-core/src/test/java/rx/GroupByTests.java b/rxjava-core/src/test/java/rx/GroupByTests.java index 87448f8510..de3310b35c 100644 --- a/rxjava-core/src/test/java/rx/GroupByTests.java +++ b/rxjava-core/src/test/java/rx/GroupByTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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 org.junit.Test; diff --git a/rxjava-core/src/test/java/rx/IntervalDemo.java b/rxjava-core/src/test/java/rx/IntervalDemo.java index 2d16901674..0222bc1b55 100644 --- a/rxjava-core/src/test/java/rx/IntervalDemo.java +++ b/rxjava-core/src/test/java/rx/IntervalDemo.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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 java.util.ArrayList; diff --git a/rxjava-core/src/test/java/rx/MergeTests.java b/rxjava-core/src/test/java/rx/MergeTests.java index 83a71044ef..1f697ce46a 100644 --- a/rxjava-core/src/test/java/rx/MergeTests.java +++ b/rxjava-core/src/test/java/rx/MergeTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.*; diff --git a/rxjava-core/src/test/java/rx/ObservableWindowTests.java b/rxjava-core/src/test/java/rx/ObservableWindowTests.java index 75e5cb565a..987ce255c9 100644 --- a/rxjava-core/src/test/java/rx/ObservableWindowTests.java +++ b/rxjava-core/src/test/java/rx/ObservableWindowTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.*; diff --git a/rxjava-core/src/test/java/rx/ObserveOnTests.java b/rxjava-core/src/test/java/rx/ObserveOnTests.java index e6e4e46b2d..57d13c3585 100644 --- a/rxjava-core/src/test/java/rx/ObserveOnTests.java +++ b/rxjava-core/src/test/java/rx/ObserveOnTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.*; diff --git a/rxjava-core/src/test/java/rx/ReduceTests.java b/rxjava-core/src/test/java/rx/ReduceTests.java index b812ba3638..08ff57405b 100644 --- a/rxjava-core/src/test/java/rx/ReduceTests.java +++ b/rxjava-core/src/test/java/rx/ReduceTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.*; diff --git a/rxjava-core/src/test/java/rx/RefCountTests.java b/rxjava-core/src/test/java/rx/RefCountTests.java index 94b4ed50d4..b83e94fcae 100644 --- a/rxjava-core/src/test/java/rx/RefCountTests.java +++ b/rxjava-core/src/test/java/rx/RefCountTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.*; diff --git a/rxjava-core/src/test/java/rx/ScanTests.java b/rxjava-core/src/test/java/rx/ScanTests.java index bef93e471b..d7948963d7 100644 --- a/rxjava-core/src/test/java/rx/ScanTests.java +++ b/rxjava-core/src/test/java/rx/ScanTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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 java.util.HashMap; diff --git a/rxjava-core/src/test/java/rx/StartWithTests.java b/rxjava-core/src/test/java/rx/StartWithTests.java index 2204b6f081..5bf3c518c2 100644 --- a/rxjava-core/src/test/java/rx/StartWithTests.java +++ b/rxjava-core/src/test/java/rx/StartWithTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.*; diff --git a/rxjava-core/src/test/java/rx/ThrottleFirstTests.java b/rxjava-core/src/test/java/rx/ThrottleFirstTests.java index 5e8f3ef1cb..655754d398 100644 --- a/rxjava-core/src/test/java/rx/ThrottleFirstTests.java +++ b/rxjava-core/src/test/java/rx/ThrottleFirstTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/ThrottleLastTests.java b/rxjava-core/src/test/java/rx/ThrottleLastTests.java index 742bbc09ba..c3a037a78c 100644 --- a/rxjava-core/src/test/java/rx/ThrottleLastTests.java +++ b/rxjava-core/src/test/java/rx/ThrottleLastTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/ThrottleWithTimeoutTests.java b/rxjava-core/src/test/java/rx/ThrottleWithTimeoutTests.java index 3503fe7adb..ead4ddb24e 100644 --- a/rxjava-core/src/test/java/rx/ThrottleWithTimeoutTests.java +++ b/rxjava-core/src/test/java/rx/ThrottleWithTimeoutTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/ZipTests.java b/rxjava-core/src/test/java/rx/ZipTests.java index 64d96c904a..dd406ee2e0 100644 --- a/rxjava-core/src/test/java/rx/ZipTests.java +++ b/rxjava-core/src/test/java/rx/ZipTests.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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 java.util.HashMap; diff --git a/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java b/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java index e9a1a1061a..3613b7c592 100644 --- a/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java +++ b/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.concurrency; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java b/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java index fc2da52963..593f6b6a52 100644 --- a/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java +++ b/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.concurrency; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java b/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java index e97765c6ab..b546fd64bb 100644 --- a/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java +++ b/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.observables; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationAllTest.java b/rxjava-core/src/test/java/rx/operators/OperationAllTest.java index 8418e821d6..0b05ee1d4e 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationAllTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationAllTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java b/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java index f9e64791d8..f98149376a 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java b/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java index 5985959669..a8ffa4706b 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java b/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java index c9f0e8b8e7..9318af8fd6 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java b/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java index 60f4b5f4fc..79a811b6de 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationCastTest.java b/rxjava-core/src/test/java/rx/operators/OperationCastTest.java index f5c0b6b515..782fe6bc90 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationCastTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationCastTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java b/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java index b5bb863b2e..c576337e1e 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java b/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java index d6fc1e2d56..f42d8b81ad 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java b/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java index 667c607301..e831c7b829 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java b/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java index fa4910707c..68003ceb01 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java b/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java index 6ad98c9a8d..550f428134 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java b/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java index 3ca31f160c..1465b4f3d2 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java b/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java index 9d0e1569b5..de57e8fc1e 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java b/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java index bd31b91ebc..b4568baaf7 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java b/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java index 8642e78300..a28de23d08 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java b/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java index 88fb4deacb..c22c3d46b8 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationFilterTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java b/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java index c0fd0d9ef2..1ce2111fe9 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java b/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java index b17ba4740e..026caa1319 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java b/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java index 86d5af6e46..cf47d9e683 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java b/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java index 86764bd6ae..5f64452e3b 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java index 279a40eb7f..6d639d5235 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java b/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java index 0795ec03ed..54d1ab0474 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java b/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java index 50ebda0bb3..693b9d4801 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java b/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java index d7d399486e..311a838691 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java b/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java index 9dfb1ef9c1..d20f79e819 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java b/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java index 9e0189941d..8f824f1484 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationNextTest.java b/rxjava-core/src/test/java/rx/operators/OperationNextTest.java index 4b16458c4e..140cc0560c 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationNextTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationNextTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java b/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java index 75f2603df7..e114f637ff 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java index ba81230ccf..ab9293251e 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java index 48ef273932..dcbbdc3fb4 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java index 288f4f4995..e436dd29ee 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java b/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java index 18326a306b..8b5cc9271a 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java b/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java index 2a0959a14f..d52c8bc0b9 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java b/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java index 40845167c4..1b6006b923 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java b/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java index 7ef3906eaa..d868697f9f 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSampleTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationScanTest.java b/rxjava-core/src/test/java/rx/operators/OperationScanTest.java index 5c28f9efdf..0dedef787c 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationScanTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationScanTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java index ed213f3d5d..9391424e07 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java index 9aa3985315..16bc76820f 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java b/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java index 17d7aced6a..153c9fb14e 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java b/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java index d95bfdfc8a..635d4d8755 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationSumTest.java b/rxjava-core/src/test/java/rx/operators/OperationSumTest.java index 98a75fd836..e124ad13d5 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSumTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSumTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java b/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java index 090e9e5d68..fa38f02ff8 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java b/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java index 2815ea03f4..73db076000 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java index 2bc6ec2da8..5ec876368b 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java index 57a549e752..773154eda0 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java index 7033f9154a..c5182a71ba 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java index dee73d2cdf..08c6d8e83f 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java b/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java index f5e75175d9..ad58e13604 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java b/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java index ebbd27eb7a..056b97bf11 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationToFutureTest.java b/rxjava-core/src/test/java/rx/operators/OperationToFutureTest.java index c18131e5c7..ffaf775f8b 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToFutureTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToFutureTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationToIteratorTest.java b/rxjava-core/src/test/java/rx/operators/OperationToIteratorTest.java index 1994bd4a0c..b40dd9b62b 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToIteratorTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToIteratorTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java index a2f4daa711..dcc6efcc6a 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java index f99bb27428..8d8be93dcb 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java index 58d1456a80..1124ea6211 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java b/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java index 9dd78943a5..8c572a31b6 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java b/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java index 71a749a0ae..b26cf42cae 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperationZipTest.java b/rxjava-core/src/test/java/rx/operators/OperationZipTest.java index bab2d7d2fe..43cca02327 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationZipTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationZipTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/operators/OperatorTesterTest.java b/rxjava-core/src/test/java/rx/operators/OperatorTesterTest.java index f5a4161708..645cea8e3b 100644 --- a/rxjava-core/src/test/java/rx/operators/OperatorTesterTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorTesterTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import org.junit.Ignore; diff --git a/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java b/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java index 99120b4348..bb317884bc 100644 --- a/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java +++ b/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java b/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java index 2780eef712..f1c40c3674 100644 --- a/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java +++ b/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/operators/TakeWhileTest.java b/rxjava-core/src/test/java/rx/operators/TakeWhileTest.java index c81169eac2..25e17ba13b 100644 --- a/rxjava-core/src/test/java/rx/operators/TakeWhileTest.java +++ b/rxjava-core/src/test/java/rx/operators/TakeWhileTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import org.junit.Ignore; diff --git a/rxjava-core/src/test/java/rx/operators/TimeIntervalObserverTest.java b/rxjava-core/src/test/java/rx/operators/TimeIntervalObserverTest.java index 6f073696f0..29337619ff 100644 --- a/rxjava-core/src/test/java/rx/operators/TimeIntervalObserverTest.java +++ b/rxjava-core/src/test/java/rx/operators/TimeIntervalObserverTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.operators; import org.junit.Ignore; diff --git a/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java b/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java index 94c4fdcc0d..ce96b43edc 100644 --- a/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java +++ b/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.plugins; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java index 243dda02bf..a0b82c7167 100644 --- a/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.subjects; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java index 8c7a52f61d..ac543b7a18 100644 --- a/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.subjects; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java index 9c2ca321ee..9e93c89e01 100644 --- a/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.subjects; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java b/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java index 728f9e53f1..fef45ca63d 100644 --- a/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.subjects; import static org.mockito.Matchers.*; diff --git a/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java index e7257d9f1a..ce23bf1d68 100644 --- a/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java +++ b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.subscriptions; import static org.junit.Assert.*; diff --git a/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java b/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java index 806a5e52e9..abeab16833 100644 --- a/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java +++ b/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.subscriptions; import static org.mockito.Mockito.*; diff --git a/rxjava-core/src/test/java/rx/util/RangeTest.java b/rxjava-core/src/test/java/rx/util/RangeTest.java index c788b32e82..297e7a769a 100644 --- a/rxjava-core/src/test/java/rx/util/RangeTest.java +++ b/rxjava-core/src/test/java/rx/util/RangeTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.util; import static org.junit.Assert.*; From 4b9ca5777ae10dcfe10bcaa87b7217a94bf16cdc Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 4 Nov 2013 20:16:28 -0800 Subject: [PATCH 228/333] Separating unit tests out due to https://github.com/Netflix/RxJava/pull/466 --- .../main/java/rx/operators/OperationAmb.java | 132 --------------- .../java/rx/operators/OperationAmbTest.java | 157 ++++++++++++++++++ 2 files changed, 157 insertions(+), 132 deletions(-) create mode 100644 rxjava-core/src/test/java/rx/operators/OperationAmbTest.java diff --git a/rxjava-core/src/main/java/rx/operators/OperationAmb.java b/rxjava-core/src/main/java/rx/operators/OperationAmb.java index 3074b4d3e9..639de2fa85 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAmb.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAmb.java @@ -15,27 +15,15 @@ */ package rx.operators; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; - -import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; -import rx.concurrency.TestScheduler; import rx.subscriptions.CompositeSubscription; -import rx.util.functions.Action0; /** * Propagates the observable sequence that reacts first. @@ -200,124 +188,4 @@ private boolean isSelected() { } } - public static class UnitTest { - private TestScheduler scheduler; - - @Before - public void setUp() { - scheduler = new TestScheduler(); - } - - private Observable createObservable(final String[] values, - final long interval, final Throwable e) { - return Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe( - final Observer observer) { - CompositeSubscription parentSubscription = new CompositeSubscription(); - long delay = interval; - for (final String value : values) { - parentSubscription.add(scheduler.schedule( - new Action0() { - @Override - public void call() { - observer.onNext(value); - } - }, delay, TimeUnit.MILLISECONDS)); - delay += interval; - } - parentSubscription.add(scheduler.schedule(new Action0() { - @Override - public void call() { - if (e == null) { - observer.onCompleted(); - } else { - observer.onError(e); - } - } - }, delay, TimeUnit.MILLISECONDS)); - return parentSubscription; - } - }); - } - - @Test - public void testAmb() { - Observable observable1 = createObservable(new String[] { - "1", "11", "111", "1111" }, 2000, null); - Observable observable2 = createObservable(new String[] { - "2", "22", "222", "2222" }, 1000, null); - Observable observable3 = createObservable(new String[] { - "3", "33", "333", "3333" }, 3000, null); - - Observable o = Observable.create(amb(observable1, - observable2, observable3)); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - o.subscribe(observer); - - scheduler.advanceTimeBy(100000, TimeUnit.MILLISECONDS); - - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext("2"); - inOrder.verify(observer, times(1)).onNext("22"); - inOrder.verify(observer, times(1)).onNext("222"); - inOrder.verify(observer, times(1)).onNext("2222"); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testAmb2() { - IOException needHappenedException = new IOException( - "fake exception"); - Observable observable1 = createObservable(new String[] {}, - 2000, new IOException("fake exception")); - Observable observable2 = createObservable(new String[] { - "2", "22", "222", "2222" }, 1000, needHappenedException); - Observable observable3 = createObservable(new String[] {}, - 3000, new IOException("fake exception")); - - Observable o = Observable.create(amb(observable1, - observable2, observable3)); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - o.subscribe(observer); - - scheduler.advanceTimeBy(100000, TimeUnit.MILLISECONDS); - - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext("2"); - inOrder.verify(observer, times(1)).onNext("22"); - inOrder.verify(observer, times(1)).onNext("222"); - inOrder.verify(observer, times(1)).onNext("2222"); - inOrder.verify(observer, times(1)).onError(needHappenedException); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testAmb3() { - Observable observable1 = createObservable(new String[] { - "1" }, 2000, null); - Observable observable2 = createObservable(new String[] {}, - 1000, null); - Observable observable3 = createObservable(new String[] { - "3" }, 3000, null); - - Observable o = Observable.create(amb(observable1, - observable2, observable3)); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - o.subscribe(observer); - - scheduler.advanceTimeBy(100000, TimeUnit.MILLISECONDS); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationAmbTest.java b/rxjava-core/src/test/java/rx/operators/OperationAmbTest.java new file mode 100644 index 0000000000..784d686198 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationAmbTest.java @@ -0,0 +1,157 @@ +/** + * Copyright 2013 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.operators; + +import static org.mockito.Mockito.*; +import static rx.operators.OperationAmb.*; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.concurrency.TestScheduler; +import rx.subscriptions.CompositeSubscription; +import rx.util.functions.Action0; + +public class OperationAmbTest { + + private TestScheduler scheduler; + + @Before + public void setUp() { + scheduler = new TestScheduler(); + } + + private Observable createObservable(final String[] values, + final long interval, final Throwable e) { + return Observable.create(new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe( + final Observer observer) { + CompositeSubscription parentSubscription = new CompositeSubscription(); + long delay = interval; + for (final String value : values) { + parentSubscription.add(scheduler.schedule( + new Action0() { + @Override + public void call() { + observer.onNext(value); + } + }, delay, TimeUnit.MILLISECONDS)); + delay += interval; + } + parentSubscription.add(scheduler.schedule(new Action0() { + @Override + public void call() { + if (e == null) { + observer.onCompleted(); + } else { + observer.onError(e); + } + } + }, delay, TimeUnit.MILLISECONDS)); + return parentSubscription; + } + }); + } + + @Test + public void testAmb() { + Observable observable1 = createObservable(new String[] { + "1", "11", "111", "1111" }, 2000, null); + Observable observable2 = createObservable(new String[] { + "2", "22", "222", "2222" }, 1000, null); + Observable observable3 = createObservable(new String[] { + "3", "33", "333", "3333" }, 3000, null); + + Observable o = Observable.create(amb(observable1, + observable2, observable3)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + o.subscribe(observer); + + scheduler.advanceTimeBy(100000, TimeUnit.MILLISECONDS); + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext("2"); + inOrder.verify(observer, times(1)).onNext("22"); + inOrder.verify(observer, times(1)).onNext("222"); + inOrder.verify(observer, times(1)).onNext("2222"); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testAmb2() { + IOException needHappenedException = new IOException( + "fake exception"); + Observable observable1 = createObservable(new String[] {}, + 2000, new IOException("fake exception")); + Observable observable2 = createObservable(new String[] { + "2", "22", "222", "2222" }, 1000, needHappenedException); + Observable observable3 = createObservable(new String[] {}, + 3000, new IOException("fake exception")); + + Observable o = Observable.create(amb(observable1, + observable2, observable3)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + o.subscribe(observer); + + scheduler.advanceTimeBy(100000, TimeUnit.MILLISECONDS); + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext("2"); + inOrder.verify(observer, times(1)).onNext("22"); + inOrder.verify(observer, times(1)).onNext("222"); + inOrder.verify(observer, times(1)).onNext("2222"); + inOrder.verify(observer, times(1)).onError(needHappenedException); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testAmb3() { + Observable observable1 = createObservable(new String[] { + "1" }, 2000, null); + Observable observable2 = createObservable(new String[] {}, + 1000, null); + Observable observable3 = createObservable(new String[] { + "3" }, 3000, null); + + Observable o = Observable.create(amb(observable1, + observable2, observable3)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + o.subscribe(observer); + + scheduler.advanceTimeBy(100000, TimeUnit.MILLISECONDS); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + +} From d39acf062490803090daa7eba3f13011731e4689 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Tue, 5 Nov 2013 04:44:11 +0000 Subject: [PATCH 229/333] [Gradle Release Plugin] - pre tag commit: '0.14.8'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 70d33a25c8..fffafc86ac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.8-SNAPSHOT +version=0.14.8 From 2cbc613b0c5a524b05585011fb775427b544fe5c Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Tue, 5 Nov 2013 04:44:15 +0000 Subject: [PATCH 230/333] [Gradle Release Plugin] - new version commit: '0.14.9-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index fffafc86ac..ac10ebec80 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.8 +version=0.14.9-SNAPSHOT From 0b8014c85fd7a1545b21380a9c7bbb52d0aa41b6 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Sun, 3 Nov 2013 18:06:08 +0800 Subject: [PATCH 231/333] Added the rest overloads of Timeout operator --- rxjava-core/src/main/java/rx/Observable.java | 79 ++++++++++-- .../java/rx/operators/OperationTimeout.java | 37 +++++- .../src/test/java/rx/TimeoutTests.java | 114 +++++++++++++++++- 3 files changed, 212 insertions(+), 18 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index ce47c7085c..8eb9d478f4 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4526,31 +4526,86 @@ public Observable ignoreElements() { } /** - * Returns either the observable sequence or an TimeoutException if timeout elapses. - * + * Applies a timeout policy for each element in the observable sequence, + * using the specified scheduler to run timeout timers. If the next element + * isn't received within the specified timeout duration starting from its + * predecessor, a TimeoutException is propagated to the observer. + * * @param timeout - * The timeout duration + * Maximum duration between values before a timeout occurs. * @param timeUnit - * The time unit of the timeout + * The unit of time which applies to the "timeout" argument. + * + * @return The source sequence with a TimeoutException in case of a timeout. + * @see MSDN: Observable.Timeout + */ + public Observable timeout(long timeout, TimeUnit timeUnit) { + return create(OperationTimeout.timeout(this, timeout, timeUnit)); + } + + /** + * Applies a timeout policy for each element in the observable sequence, + * using the specified scheduler to run timeout timers. If the next element + * isn't received within the specified timeout duration starting from its + * predecessor, the other observable sequence is used to produce future + * messages from that point on. + * + * @param timeout + * Maximum duration between values before a timeout occurs. + * @param timeUnit + * The unit of time which applies to the "timeout" argument. + * @param other + * Sequence to return in case of a timeout. + * + * @return The source sequence switching to the other sequence in case of a timeout. + * @see MSDN: Observable.Timeout + */ + public Observable timeout(long timeout, TimeUnit timeUnit, Observable other) { + return create(OperationTimeout.timeout(this, timeout, timeUnit, other)); + } + + /** + * Applies a timeout policy for each element in the observable sequence, + * using the specified scheduler to run timeout timers. If the next element + * isn't received within the specified timeout duration starting from its + * predecessor, a TimeoutException is propagated to the observer. + * + * @param timeout + * Maximum duration between values before a timeout occurs. + * @param timeUnit + * The unit of time which applies to the "timeout" argument. * @param scheduler - * The scheduler to run the timeout timers on. + * Scheduler to run the timeout timers on. + * * @return The source sequence with a TimeoutException in case of a timeout. + * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { return create(OperationTimeout.timeout(this, timeout, timeUnit, scheduler)); } /** - * Returns either the observable sequence or an TimeoutException if timeout elapses. - * + * Applies a timeout policy for each element in the observable sequence, + * using the specified scheduler to run timeout timers. If the next element + * isn't received within the specified timeout duration starting from its + * predecessor, the other observable sequence is used to produce future + * messages from that point on. + * * @param timeout - * The timeout duration + * Maximum duration between values before a timeout occurs. * @param timeUnit - * The time unit of the timeout - * @return The source sequence with a TimeoutException in case of a timeout. + * The unit of time which applies to the "timeout" argument. + * @param other + * Sequence to return in case of a timeout. + * @param scheduler + * Scheduler to run the timeout timers on. + * + * @return The source sequence switching to the other sequence in case of a + * timeout. + * @see MSDN: Observable.Timeout */ - public Observable timeout(long timeout, TimeUnit timeUnit) { - return create(OperationTimeout.timeout(this, timeout, timeUnit, Schedulers.threadPoolForComputation())); + public Observable timeout(long timeout, TimeUnit timeUnit, Observable other, Scheduler scheduler) { + return create(OperationTimeout.timeout(this, timeout, timeUnit, other, scheduler)); } /** diff --git a/rxjava-core/src/main/java/rx/operators/OperationTimeout.java b/rxjava-core/src/main/java/rx/operators/OperationTimeout.java index 3df6cd2929..b52c7a5a43 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTimeout.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTimeout.java @@ -21,17 +21,39 @@ import java.util.concurrent.atomic.AtomicLong; import rx.Observable; +import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; import rx.Subscription; +import rx.concurrency.Schedulers; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.SerialSubscription; import rx.util.functions.Action0; import rx.util.functions.Func0; +/** + * Applies a timeout policy for each element in the observable sequence, using + * the specified scheduler to run timeout timers. If the next element isn't + * received within the specified timeout duration starting from its predecessor, + * the other observable sequence is used to produce future messages from that + * point on. + */ public final class OperationTimeout { - public static Observable.OnSubscribeFunc timeout(Observable source, long timeout, TimeUnit timeUnit, Scheduler scheduler) { - return new Timeout(source, timeout, timeUnit, scheduler); + + public static OnSubscribeFunc timeout(Observable source, long timeout, TimeUnit timeUnit) { + return new Timeout(source, timeout, timeUnit, null, Schedulers.threadPoolForComputation()); + } + + public static OnSubscribeFunc timeout(Observable sequence, long timeout, TimeUnit timeUnit, Observable other) { + return new Timeout(sequence, timeout, timeUnit, other, Schedulers.threadPoolForComputation()); + } + + public static OnSubscribeFunc timeout(Observable source, long timeout, TimeUnit timeUnit, Scheduler scheduler) { + return new Timeout(source, timeout, timeUnit, null, scheduler); + } + + public static OnSubscribeFunc timeout(Observable sequence, long timeout, TimeUnit timeUnit, Observable other, Scheduler scheduler) { + return new Timeout(sequence, timeout, timeUnit, other, scheduler); } private static class Timeout implements Observable.OnSubscribeFunc { @@ -39,11 +61,13 @@ private static class Timeout implements Observable.OnSubscribeFunc { private final long timeout; private final TimeUnit timeUnit; private final Scheduler scheduler; + private final Observable other; - private Timeout(Observable source, long timeout, TimeUnit timeUnit, Scheduler scheduler) { + private Timeout(Observable source, long timeout, TimeUnit timeUnit, Observable other, Scheduler scheduler) { this.source = source; this.timeout = timeout; this.timeUnit = timeUnit; + this.other = other; this.scheduler = scheduler; } @@ -68,7 +92,12 @@ public void call() { } } if (timeoutWins) { - observer.onError(new TimeoutException()); + if (other == null) { + observer.onError(new TimeoutException()); + } + else { + serial.setSubscription(other.subscribe(observer)); + } } } diff --git a/rxjava-core/src/test/java/rx/TimeoutTests.java b/rxjava-core/src/test/java/rx/TimeoutTests.java index 26da4a3713..46a3620ce5 100644 --- a/rxjava-core/src/test/java/rx/TimeoutTests.java +++ b/rxjava-core/src/test/java/rx/TimeoutTests.java @@ -15,14 +15,19 @@ */ package rx; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; +import static org.mockito.Matchers.any; +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.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.junit.Before; import org.junit.Test; +import org.mockito.InOrder; import org.mockito.MockitoAnnotations; import rx.concurrency.TestScheduler; @@ -46,6 +51,7 @@ public void setUp() { @Test public void shouldNotTimeoutIfOnNextWithinTimeout() { + @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); Subscription subscription = withTimeout.subscribe(observer); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -58,6 +64,7 @@ public void shouldNotTimeoutIfOnNextWithinTimeout() { @Test public void shouldNotTimeoutIfSecondOnNextWithinTimeout() { + @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); Subscription subscription = withTimeout.subscribe(observer); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -72,6 +79,7 @@ public void shouldNotTimeoutIfSecondOnNextWithinTimeout() { @Test public void shouldTimeoutIfOnNextNotWithinTimeout() { + @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); Subscription subscription = withTimeout.subscribe(observer); testScheduler.advanceTimeBy(TIMEOUT + 1, TimeUnit.SECONDS); @@ -81,6 +89,7 @@ public void shouldTimeoutIfOnNextNotWithinTimeout() { @Test public void shouldTimeoutIfSecondOnNextNotWithinTimeout() { + @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); Subscription subscription = withTimeout.subscribe(observer); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -93,6 +102,7 @@ public void shouldTimeoutIfSecondOnNextNotWithinTimeout() { @Test public void shouldCompleteIfUnderlyingComletes() { + @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); Subscription subscription = withTimeout.subscribe(observer); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -105,6 +115,7 @@ public void shouldCompleteIfUnderlyingComletes() { @Test public void shouldErrorIfUnderlyingErrors() { + @SuppressWarnings("unchecked") Observer observer = mock(Observer.class); Subscription subscription = withTimeout.subscribe(observer); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -113,4 +124,103 @@ public void shouldErrorIfUnderlyingErrors() { verify(observer).onError(any(UnsupportedOperationException.class)); subscription.unsubscribe(); } + + @Test + public void shouldSwitchToOtherIfOnNextNotWithinTimeout() { + Observable other = Observable.from("a", "b", "c"); + Observable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, other, testScheduler); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Subscription subscription = source.subscribe(observer); + + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + underlyingSubject.onNext("One"); + testScheduler.advanceTimeBy(4, TimeUnit.SECONDS); + underlyingSubject.onNext("Two"); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext("One"); + inOrder.verify(observer, times(1)).onNext("a"); + inOrder.verify(observer, times(1)).onNext("b"); + inOrder.verify(observer, times(1)).onNext("c"); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + subscription.unsubscribe(); + } + + @Test + public void shouldSwitchToOtherIfOnErrorNotWithinTimeout() { + Observable other = Observable.from("a", "b", "c"); + Observable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, other, testScheduler); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Subscription subscription = source.subscribe(observer); + + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + underlyingSubject.onNext("One"); + testScheduler.advanceTimeBy(4, TimeUnit.SECONDS); + underlyingSubject.onError(new UnsupportedOperationException()); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext("One"); + inOrder.verify(observer, times(1)).onNext("a"); + inOrder.verify(observer, times(1)).onNext("b"); + inOrder.verify(observer, times(1)).onNext("c"); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + subscription.unsubscribe(); + } + + @Test + public void shouldSwitchToOtherIfOnCompletedNotWithinTimeout() { + Observable other = Observable.from("a", "b", "c"); + Observable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, other, testScheduler); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Subscription subscription = source.subscribe(observer); + + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + underlyingSubject.onNext("One"); + testScheduler.advanceTimeBy(4, TimeUnit.SECONDS); + underlyingSubject.onCompleted(); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext("One"); + inOrder.verify(observer, times(1)).onNext("a"); + inOrder.verify(observer, times(1)).onNext("b"); + inOrder.verify(observer, times(1)).onNext("c"); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + subscription.unsubscribe(); + } + + @Test + public void shouldSwitchToOtherAndCanBeUnsubscribedIfOnNextNotWithinTimeout() { + PublishSubject other = PublishSubject.create(); + Observable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, other, testScheduler); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + Subscription subscription = source.subscribe(observer); + + testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); + underlyingSubject.onNext("One"); + testScheduler.advanceTimeBy(4, TimeUnit.SECONDS); + underlyingSubject.onNext("Two"); + + other.onNext("a"); + other.onNext("b"); + subscription.unsubscribe(); + + // The following messages should not be delivered. + other.onNext("c"); + other.onNext("d"); + other.onCompleted(); + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext("One"); + inOrder.verify(observer, times(1)).onNext("a"); + inOrder.verify(observer, times(1)).onNext("b"); + inOrder.verifyNoMoreInteractions(); + } } From bcfa81e5f050ad247ed1be014c64ab4ae7f62b56 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 4 Nov 2013 22:06:04 -0800 Subject: [PATCH 232/333] Version 0.14.8 --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0b3262002a..cb88a7c064 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # RxJava Releases # +### Version 0.14.8 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.8%22)) ### + +* [Pull 460](https://github.com/Netflix/RxJava/pull/460) Operator: Amb +* [Pull 466](https://github.com/Netflix/RxJava/pull/466) Refactor Unit Tests + ### Version 0.14.7 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.7%22)) ### * [Pull 459](https://github.com/Netflix/RxJava/pull/459) Fix multiple unsubscribe behavior From 11af5d612de712316952b80c9a27c4e154942306 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 6 Nov 2013 19:14:10 -0800 Subject: [PATCH 233/333] Last operator Equivalent to lastAsync in Rx.Net. --- rxjava-core/src/main/java/rx/Observable.java | 12 ++- .../main/java/rx/operators/OperationLast.java | 82 +++++++++++++++++++ .../java/rx/operators/OperationLastTest.java | 49 +++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationLast.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationLastTest.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index ce47c7085c..6bcd3e9f77 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -49,6 +49,7 @@ import rx.operators.OperationFirstOrDefault; import rx.operators.OperationGroupBy; import rx.operators.OperationInterval; +import rx.operators.OperationLast; import rx.operators.OperationMap; import rx.operators.OperationMaterialize; import rx.operators.OperationMerge; @@ -4461,12 +4462,21 @@ public Observable> groupBy(final Func1 * In Rx.Net this is negated as the any operator but renamed in RxJava to better match Java naming idioms. * - * @return A subscription function for creating the target Observable. + * @return An Observable that emits Boolean. * @see MSDN: Observable.Any */ public Observable isEmpty() { return create(OperationAny.isEmpty(this)); } + + /** + * Returns an {@link Observable} that emits the last element of the source or an IllegalArgumentException if the source {@link Observable} is empty. + * + * @return Observable + */ + public Observable last() { + return create(OperationLast.last(this)); + } /** * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking diff --git a/rxjava-core/src/main/java/rx/operators/OperationLast.java b/rxjava-core/src/main/java/rx/operators/OperationLast.java new file mode 100644 index 0000000000..964afd5176 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationLast.java @@ -0,0 +1,82 @@ +/** + * Copyright 2013 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.operators; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; + +/** + * Emit an Observable with the last emitted item + * or onError(new IllegalArgumentException("Sequence contains no elements")) if no elements received. + */ +public class OperationLast { + + /** + * Accepts a sequence and returns a sequence that is the last emitted item + * or an error if no items are emitted (empty sequence). + * + * @param sequence + * the input sequence. + * @param + * the type of the sequence. + * @return a sequence containing the last emitted item or that has onError invoked on it if no items + */ + public static OnSubscribeFunc last(final Observable sequence) { + return new OnSubscribeFunc() { + final AtomicReference last = new AtomicReference(); + final AtomicBoolean hasLast = new AtomicBoolean(false); + + @Override + public Subscription onSubscribe(final Observer observer) { + return sequence.subscribe(new Observer() { + + @Override + public void onCompleted() { + /* + * We don't need to worry about the following being non-atomic + * since an Observable sequence is serial so we will not receive + * concurrent executions. + */ + if (hasLast.get()) { + observer.onNext(last.get()); + observer.onCompleted(); + } else { + observer.onError(new IllegalArgumentException("Sequence contains no elements")); + } + } + + @Override + public void onError(Throwable e) { + observer.onError(e); + } + + @Override + public void onNext(T value) { + last.set(value); + hasLast.set(true); + } + }); + } + + }; + } + +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationLastTest.java b/rxjava-core/src/test/java/rx/operators/OperationLastTest.java new file mode 100644 index 0000000000..006de63060 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationLastTest.java @@ -0,0 +1,49 @@ +/** + * Copyright 2013 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.operators; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import rx.Observable; + +public class OperationLastTest { + + @Test + public void testLastWithElements() { + Observable last = Observable.create(OperationLast.last(Observable.from(1, 2, 3))); + assertEquals(3, last.toBlockingObservable().single().intValue()); + } + + @Test(expected = IllegalArgumentException.class) + public void testLastWithNoElements() { + Observable last = Observable.create(OperationLast.last(Observable.empty())); + last.toBlockingObservable().single(); + } + + @Test + public void testLastMultiSubscribe() { + Observable last = Observable.create(OperationLast.last(Observable.from(1, 2, 3))); + assertEquals(3, last.toBlockingObservable().single().intValue()); + assertEquals(3, last.toBlockingObservable().single().intValue()); + } + + @Test + public void testLastViaObservable() { + Observable.from(1, 2, 3).last(); + } +} From 997546f4b5906cbea94dbd5c9fb05408724c61f3 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 6 Nov 2013 19:24:08 -0800 Subject: [PATCH 234/333] Fix behavior of BlockingObservable.last MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It now throws an IllegalArgumentException instead of returning null if no elements are emitted. This is based on feedback from @headinthebox confirming this expected behavior: var xs = Observable.Range(1,0); // empty sequence int x = xs.Last().Dump(); // throws “sequence contains no elements” RxJava => BlockingObservable.last() IObservable ys = xs.TakeLast(1).Dump(); // OnCompleted() RxJava => Observable.takeLast(1) IObservable zs = xs.LastAsync().Dump(); // OnError(“sequence contains no elements”) RxJava => Observable.last() --- .../src/main/java/rx/observables/BlockingObservable.java | 7 ++----- .../test/java/rx/observables/BlockingObservableTest.java | 5 ++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/rxjava-core/src/main/java/rx/observables/BlockingObservable.java b/rxjava-core/src/main/java/rx/observables/BlockingObservable.java index 36f372685e..8fe13c9e88 100644 --- a/rxjava-core/src/main/java/rx/observables/BlockingObservable.java +++ b/rxjava-core/src/main/java/rx/observables/BlockingObservable.java @@ -178,13 +178,10 @@ public Iterator getIterator() { * * * @return the last item emitted by the source {@link Observable} + * @throws IllegalArgumentException if source contains no elements */ public T last() { - T result = null; - for (T value : toIterable()) { - result = value; - } - return result; + return new BlockingObservable(o.last()).single(); } /** diff --git a/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java b/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java index b546fd64bb..f3e05189c1 100644 --- a/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java +++ b/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java @@ -49,11 +49,10 @@ public void testLast() { assertEquals("three", obs.last()); } - @Test + @Test(expected = IllegalArgumentException.class) public void testLastEmptyObservable() { BlockingObservable obs = BlockingObservable.from(Observable.empty()); - - assertNull(obs.last()); + obs.last(); } @Test From 1945713fd50d0e6740df94fb63358a0939105ab5 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 6 Nov 2013 20:12:00 -0800 Subject: [PATCH 235/333] Fix unit test after last() changed behavior --- .../src/test/java/rx/operators/OperationTakeWhileTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java b/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java index 08c6d8e83f..830dd59b33 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.java @@ -119,7 +119,7 @@ public Subscription onSubscribe(Observer observer) { public Boolean call(String s) { return false; } - })).toBlockingObservable().last(); + })).toBlockingObservable().lastOrDefault(""); } @Test From 115b6d327784fbf3300dab4bfd076318432038fd Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 6 Nov 2013 20:12:12 -0800 Subject: [PATCH 236/333] Add and clarify unit tests in map --- .../java/rx/operators/OperationMapTest.java | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java index 6d639d5235..773534a7e8 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java @@ -212,8 +212,12 @@ public String call(String s) { verify(stringObserver, times(1)).onError(any(Throwable.class)); } + /** + * This is testing how unsubscribe behavior is handled when an error occurs in a user provided function + * and the source is unsubscribed from ... but ignores or can't receive the unsubscribe as it is synchronous. + */ @Test - public void testMapWithSynchronousObservableContainingError() { + public void testMapContainingErrorWithSequenceThatDoesntUnsubscribe() { Observable w = Observable.from("one", "fail", "two", "three", "fail"); final AtomicInteger c1 = new AtomicInteger(); final AtomicInteger c2 = new AtomicInteger(); @@ -243,7 +247,9 @@ public String call(String s) { verify(stringObserver, never()).onCompleted(); verify(stringObserver, times(1)).onError(any(Throwable.class)); - // we should have only returned 1 value: "one" + // We should have only returned 1 value: "one" + // Since the unsubscribe doesn't propagate, we will actually be sent all events and need + // to ignore all after the first failure. assertEquals(1, c1.get()); assertEquals(1, c2.get()); } @@ -282,6 +288,52 @@ public String call(String arg0) { inorder.verify(stringObserver, times(1)).onError(any(IllegalArgumentException.class)); inorder.verifyNoMoreInteractions(); } + + /** + * While mapping over range(1,1).last() we expect IllegalArgumentException since the sequence is empty. + */ + @Test(expected = IllegalArgumentException.class) + public void testErrorPassesThruMap() { + Observable.range(1,0).last().map(new Func1() { + + @Override + public Integer call(Integer i) { + return i; + } + + }).toBlockingObservable().single(); + } + + /** + * We expect IllegalStateException to pass thru map. + */ + @Test(expected = IllegalStateException.class) + public void testErrorPassesThruMap2() { + Observable.error(new IllegalStateException()).map(new Func1() { + + @Override + public Object call(Object i) { + return i; + } + + }).toBlockingObservable().single(); + } + + /** + * We expect an ArithmeticException exception here because last() emits a single value + * but then we divide by 0. + */ + @Test(expected = ArithmeticException.class) + public void testMapWithErrorInFunc() { + Observable.range(1,1).last().map(new Func1() { + + @Override + public Integer call(Integer i) { + return i/0; + } + + }).toBlockingObservable().single(); + } private static Map getMap(String prefix) { Map m = new HashMap(); From ac74a7928d00a0a9fdb4263223cf414c4cb39249 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 6 Nov 2013 20:47:59 -0800 Subject: [PATCH 237/333] BugFix: Unsubscribing does not work when using subscribeOn(Schedulers.newThread()) I believe this fixes https://github.com/Netflix/RxJava/issues/431 --- .../rx/concurrency/ExecutorScheduler.java | 8 +- .../rx/concurrency/NewThreadScheduler.java | 21 +++-- .../concurrency/SchedulerUnsubscribeTest.java | 88 +++++++++++++++++++ 3 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 rxjava-core/src/test/java/rx/concurrency/SchedulerUnsubscribeTest.java diff --git a/rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java b/rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java index cee84eae44..1e35735c67 100644 --- a/rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java +++ b/rxjava-core/src/main/java/rx/concurrency/ExecutorScheduler.java @@ -57,7 +57,7 @@ public void run() { } }, initialDelay, period, unit); - subscriptions.add(Subscriptions.create(f)); + subscriptions.add(Subscriptions.from(f)); return subscriptions; } else { @@ -84,7 +84,7 @@ public void run() { } }, delayTime, unit); // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens - subscription.add(Subscriptions.create(f)); + subscription.add(Subscriptions.from(f)); } else { // we are not a ScheduledExecutorService so can't directly schedule if (delayTime == 0) { @@ -106,7 +106,7 @@ public void run() { } }, delayTime, unit); // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens - subscription.add(Subscriptions.create(f)); + subscription.add(Subscriptions.from(f)); } } return subscription; @@ -134,7 +134,7 @@ public void run() { // we are an ExecutorService so get a Future back that supports unsubscribe Future f = ((ExecutorService) executor).submit(r); // add the Future as a subscription so we can cancel the scheduled action if an unsubscribe happens - subscription.add(Subscriptions.create(f)); + subscription.add(Subscriptions.from(f)); } else { // we are the lowest common denominator so can't unsubscribe once we execute executor.execute(r); diff --git a/rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java b/rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java index c33918353b..036ba62127 100644 --- a/rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java +++ b/rxjava-core/src/main/java/rx/concurrency/NewThreadScheduler.java @@ -58,15 +58,22 @@ public Thread newThread(Runnable r) { } @Override - public Subscription schedule(final T state, final Func2 action) { + public Subscription schedule(T state, Func2 action) { + final DiscardableAction discardableAction = new DiscardableAction(state, action); + // all subscriptions that may need to be unsubscribed + final CompositeSubscription subscription = new CompositeSubscription(discardableAction); + final Scheduler _scheduler = this; - return Subscriptions.from(executor.submit(new Runnable() { + subscription.add(Subscriptions.from(executor.submit(new Runnable() { @Override public void run() { - action.call(_scheduler, state); + Subscription s = discardableAction.call(_scheduler); + subscription.add(s); } - })); + }))); + + return subscription; } @Override @@ -89,7 +96,7 @@ public void run() { }, delayTime, unit); // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens - subscription.add(Subscriptions.create(f)); + subscription.add(Subscriptions.from(f)); return subscription; } @@ -97,7 +104,7 @@ public void run() { } @Override - public Subscription schedule(final T state, final Func2 action) { + public Subscription schedule(T state, Func2 action) { EventLoopScheduler s = new EventLoopScheduler(); return s.schedule(state, action); } @@ -122,7 +129,7 @@ public void run() { }, delay, unit); // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens - subscription.add(Subscriptions.create(f)); + subscription.add(Subscriptions.from(f)); return subscription; } diff --git a/rxjava-core/src/test/java/rx/concurrency/SchedulerUnsubscribeTest.java b/rxjava-core/src/test/java/rx/concurrency/SchedulerUnsubscribeTest.java new file mode 100644 index 0000000000..e99a25eaf3 --- /dev/null +++ b/rxjava-core/src/test/java/rx/concurrency/SchedulerUnsubscribeTest.java @@ -0,0 +1,88 @@ +package rx.concurrency; + +import static org.junit.Assert.*; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import rx.Observable; +import rx.Observer; +import rx.Scheduler; +import rx.operators.SafeObservableSubscription; +import rx.util.functions.Func1; + +public class SchedulerUnsubscribeTest { + + /** + * Bug report: https://github.com/Netflix/RxJava/issues/431 + */ + @Test + public void testUnsubscribeOfNewThread() throws InterruptedException { + testUnSubscribeForScheduler(Schedulers.newThread()); + } + + @Test + public void testUnsubscribeOfThreadPoolForIO() throws InterruptedException { + testUnSubscribeForScheduler(Schedulers.threadPoolForIO()); + } + + @Test + public void testUnsubscribeOfThreadPoolForComputation() throws InterruptedException { + testUnSubscribeForScheduler(Schedulers.threadPoolForComputation()); + } + + @Test + public void testUnsubscribeOfCurrentThread() throws InterruptedException { + testUnSubscribeForScheduler(Schedulers.currentThread()); + } + + public void testUnSubscribeForScheduler(Scheduler scheduler) throws InterruptedException { + + final AtomicInteger countReceived = new AtomicInteger(); + final AtomicInteger countGenerated = new AtomicInteger(); + final SafeObservableSubscription s = new SafeObservableSubscription(); + final CountDownLatch latch = new CountDownLatch(1); + + s.wrap(Observable.interval(50, TimeUnit.MILLISECONDS) + .map(new Func1() { + @Override + public Long call(Long aLong) { + System.out.println("generated " + aLong); + countGenerated.incrementAndGet(); + return aLong; + } + }) + .subscribeOn(scheduler) + .observeOn(Schedulers.currentThread()) + .subscribe(new Observer() { + @Override + public void onCompleted() { + System.out.println("--- completed"); + } + + @Override + public void onError(Throwable e) { + System.out.println("--- onError"); + } + + @Override + public void onNext(Long args) { + if (countReceived.incrementAndGet() == 2) { + s.unsubscribe(); + latch.countDown(); + } + System.out.println("==> Received " + args); + } + })); + + latch.await(1000, TimeUnit.MILLISECONDS); + + System.out.println("----------- it thinks it is finished ------------------ "); + Thread.sleep(100); + + assertEquals(2, countGenerated.get()); + } +} From 8acdaa1560821d4e8323dea5f9014cac6bfa2c34 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 7 Nov 2013 15:13:49 -0800 Subject: [PATCH 238/333] Fix non-deterministic unit test - the latch was before onError is called - use BlockingObservable and expect the IllegalArgumentException to be thrown --- .../test/java/rx/operators/OperationMapTest.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java index 773534a7e8..175cd66f75 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java @@ -264,29 +264,21 @@ public Integer call(Integer arg0) { }).toBlockingObservable().single(); } - @Test + @Test(expected = IllegalArgumentException.class) public void testMapWithErrorInFuncAndThreadPoolScheduler() throws InterruptedException { // The error will throw in one of threads in the thread pool. // If map does not handle it, the error will disappear. // so map needs to handle the error by itself. - final CountDownLatch latch = new CountDownLatch(1); Observable m = Observable.from("one") .observeOn(Schedulers.threadPoolForComputation()) .map(new Func1() { public String call(String arg0) { - try { - throw new IllegalArgumentException("any error"); - } finally { - latch.countDown(); - } + throw new IllegalArgumentException("any error"); } }); - m.subscribe(stringObserver); - latch.await(); - InOrder inorder = inOrder(stringObserver); - inorder.verify(stringObserver, times(1)).onError(any(IllegalArgumentException.class)); - inorder.verifyNoMoreInteractions(); + // block for response, expecting exception thrown + m.toBlockingObservable().last(); } /** From f1a21147939eb2173c64c5e488c3ce784816dd61 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 7 Nov 2013 15:25:29 -0800 Subject: [PATCH 239/333] BugFix: Reduce an empty observable This fixes issue https://github.com/Netflix/RxJava/issues/423 The fix is based on this comment by @headinthebox: https://github.com/Netflix/RxJava/issues/423#issuecomment-27642532 --- rxjava-core/src/main/java/rx/Observable.java | 9 ++++++- .../src/test/java/rx/ObservableTests.java | 26 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 6bcd3e9f77..3dcff7fb69 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -3520,11 +3520,18 @@ public Observable onErrorReturn(Func1 resumeFunction) * Observable, whose result will be used in the next accumulator call * @return an Observable that emits a single item that is the result of accumulating the * output from the source Observable + * @throws IllegalArgumentException + * if Observable sequence is empty. * @see MSDN: Observable.Aggregate * @see Wikipedia: Fold (higher-order function) */ public Observable reduce(Func2 accumulator) { - return create(OperationScan.scan(this, accumulator)).takeLast(1); + /* + * Discussion and confirmation of implementation at https://github.com/Netflix/RxJava/issues/423#issuecomment-27642532 + * + * It should use last() not takeLast(1) since it needs to emit an error if the sequence is empty. + */ + return create(OperationScan.scan(this, accumulator)).last(); } /** diff --git a/rxjava-core/src/test/java/rx/ObservableTests.java b/rxjava-core/src/test/java/rx/ObservableTests.java index 496f520a11..d2ff7d5d88 100644 --- a/rxjava-core/src/test/java/rx/ObservableTests.java +++ b/rxjava-core/src/test/java/rx/ObservableTests.java @@ -36,6 +36,7 @@ import rx.observables.ConnectableObservable; import rx.subscriptions.BooleanSubscription; import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; import rx.util.functions.Action1; import rx.util.functions.Func1; import rx.util.functions.Func2; @@ -210,6 +211,31 @@ public Integer call(Integer t1, Integer t2) { verify(w).onNext(10); } + + /** + * A reduce should fail with an IllegalArgumentException if done on an empty Observable. + */ + @Test(expected = IllegalArgumentException.class) + public void testReduceWithEmptyObservable() { + Observable observable = Observable.range(1, 0); + observable.reduce(new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + + }).toBlockingObservable().forEach(new Action1() { + + @Override + public void call(Integer t1) { + // do nothing ... we expect an exception instead + } + }); + + fail("Expected an exception to be thrown"); + } + @Test public void testReduceWithInitialValue() { Observable observable = Observable.from(1, 2, 3, 4); From d2d4252652d277b0c7aee26e1bd35f2a2fcc7d79 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 7 Nov 2013 15:31:35 -0800 Subject: [PATCH 240/333] Additional unit test for reduce --- .../src/test/java/rx/ObservableTests.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/rxjava-core/src/test/java/rx/ObservableTests.java b/rxjava-core/src/test/java/rx/ObservableTests.java index d2ff7d5d88..ee817e08b5 100644 --- a/rxjava-core/src/test/java/rx/ObservableTests.java +++ b/rxjava-core/src/test/java/rx/ObservableTests.java @@ -236,6 +236,26 @@ public void call(Integer t1) { 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/Netflix/RxJava/issues/423#issuecomment-27642456 + */ + @Test + public void testReduceWithEmptyObservableAndSeed() { + Observable observable = Observable.range(1, 0); + int value = observable.reduce(1, new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + + }).toBlockingObservable().last(); + + assertEquals(1, value); + } + @Test public void testReduceWithInitialValue() { Observable observable = Observable.from(1, 2, 3, 4); From fe7e8a711c82dadc6f40e3eb903ccfb877785eaf Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 7 Nov 2013 15:43:10 -0800 Subject: [PATCH 241/333] Don't emit null onComplete when no onNext received --- .../main/java/rx/subjects/AsyncSubject.java | 9 ++-- .../java/rx/subjects/AsyncSubjectTest.java | 47 ++++++++++--------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java index fc20aaf274..183efdfda9 100644 --- a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java @@ -16,6 +16,7 @@ package rx.subjects; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import rx.Observer; @@ -85,6 +86,7 @@ public void unsubscribe() { private final ConcurrentHashMap> observers; private final AtomicReference currentValue; + private final AtomicBoolean hasValue = new AtomicBoolean(); protected AsyncSubject(OnSubscribeFunc onSubscribe, ConcurrentHashMap> observers) { super(onSubscribe); @@ -96,9 +98,9 @@ protected AsyncSubject(OnSubscribeFunc onSubscribe, ConcurrentHashMap observer : observers.values()) { - observer.onNext(finalValue); - } - for (Observer observer : observers.values()) { + if (hasValue.get()) { + observer.onNext(finalValue); + } observer.onCompleted(); } } @@ -112,6 +114,7 @@ public void onError(Throwable e) { @Override public void onNext(T args) { + hasValue.set(true); currentValue.set(args); } } diff --git a/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java index a0b82c7167..321b7b422a 100644 --- a/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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 - * + * + * 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. @@ -19,6 +19,7 @@ import static org.mockito.Mockito.*; import org.junit.Test; +import org.mockito.InOrder; import org.mockito.Mockito; import rx.Observer; @@ -42,10 +43,6 @@ public void testNeverCompleted() { subject.onNext("two"); subject.onNext("three"); - assertNeverCompletedObserver(aObserver); - } - - private void assertNeverCompletedObserver(Observer aObserver) { verify(aObserver, Mockito.never()).onNext(anyString()); verify(aObserver, Mockito.never()).onError(testException); verify(aObserver, Mockito.never()).onCompleted(); @@ -64,10 +61,6 @@ public void testCompleted() { subject.onNext("three"); subject.onCompleted(); - assertCompletedObserver(aObserver); - } - - private void assertCompletedObserver(Observer aObserver) { verify(aObserver, times(1)).onNext("three"); verify(aObserver, Mockito.never()).onError(any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); @@ -89,10 +82,6 @@ public void testError() { subject.onError(new Throwable()); subject.onCompleted(); - assertErrorObserver(aObserver); - } - - private void assertErrorObserver(Observer aObserver) { verify(aObserver, Mockito.never()).onNext(anyString()); verify(aObserver, times(1)).onError(testException); verify(aObserver, Mockito.never()).onCompleted(); @@ -110,15 +99,14 @@ public void testUnsubscribeBeforeCompleted() { subject.onNext("two"); subscription.unsubscribe(); - assertNoOnNextEventsReceived(aObserver); + + verify(aObserver, Mockito.never()).onNext(anyString()); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, Mockito.never()).onCompleted(); subject.onNext("three"); subject.onCompleted(); - assertNoOnNextEventsReceived(aObserver); - } - - private void assertNoOnNextEventsReceived(Observer aObserver) { verify(aObserver, Mockito.never()).onNext(anyString()); verify(aObserver, Mockito.never()).onError(any(Throwable.class)); verify(aObserver, Mockito.never()).onCompleted(); @@ -146,4 +134,21 @@ public void call(AsyncSubject DefaultSubject) { null ); } + + @Test + public void testEmptySubjectCompleted() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onCompleted(); + + InOrder inOrder = inOrder(aObserver); + inOrder.verify(aObserver, never()).onNext(null); + inOrder.verify(aObserver, never()).onNext(any(String.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } } From b765b23541ab5332715dfbbd4cab0b620611d967 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 7 Nov 2013 15:55:22 -0800 Subject: [PATCH 242/333] CompositeSubscription - remove now also unsubscribes the removed subscription - added clear method --- .../subscriptions/CompositeSubscription.java | 28 +++++++++++ .../CompositeSubscriptionTest.java | 50 +++++++++++++++++-- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java b/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java index 326de38471..a94fd047ae 100644 --- a/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java +++ b/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java @@ -51,8 +51,36 @@ public CompositeSubscription(Subscription... subscriptions) { } } + /** + * Remove and unsubscribe all subscriptions but do not unsubscribe the outer CompositeSubscription. + */ + public void clear() { + Collection es = null; + for (Subscription s : subscriptions.keySet()) { + try { + s.unsubscribe(); + this.subscriptions.remove(s); + } catch (Throwable e) { + if (es == null) { + es = new ArrayList(); + } + es.add(e); + } + } + if (es != null) { + throw new CompositeException("Failed to unsubscribe to 1 or more subscriptions.", es); + } + } + + /** + * Remove the {@link Subscription} and unsubscribe it. + * + * @param s + */ public void remove(Subscription s) { this.subscriptions.remove(s); + // also unsubscribe from it: http://msdn.microsoft.com/en-us/library/system.reactive.disposables.compositedisposable.remove(v=vs.103).aspx + s.unsubscribe(); } public boolean isUnsubscribed() { diff --git a/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java index ce23bf1d68..99e3f29610 100644 --- a/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java +++ b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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 - * + * + * 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. @@ -82,4 +82,46 @@ public void unsubscribe() { // we should still have unsubscribed to the second one assertEquals(1, counter.get()); } + + @Test + public void testRemoveUnsubscribes() { + BooleanSubscription s1 = new BooleanSubscription(); + BooleanSubscription s2 = new BooleanSubscription(); + + CompositeSubscription s = new CompositeSubscription(); + s.add(s1); + s.add(s2); + + s.remove(s1); + + assertTrue(s1.isUnsubscribed()); + assertFalse(s2.isUnsubscribed()); + } + + @Test + public void testClear() { + BooleanSubscription s1 = new BooleanSubscription(); + BooleanSubscription s2 = new BooleanSubscription(); + + CompositeSubscription s = new CompositeSubscription(); + s.add(s1); + s.add(s2); + + assertFalse(s1.isUnsubscribed()); + assertFalse(s2.isUnsubscribed()); + + s.clear(); + + assertTrue(s1.isUnsubscribed()); + assertTrue(s1.isUnsubscribed()); + assertFalse(s.isUnsubscribed()); + + BooleanSubscription s3 = new BooleanSubscription(); + + s.add(s3); + s.unsubscribe(); + + assertTrue(s3.isUnsubscribed()); + assertTrue(s.isUnsubscribed()); + } } From 1d5991c0f11c5e728980f4d04b410226f029c763 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 7 Nov 2013 15:59:07 -0800 Subject: [PATCH 243/333] Unsubscribe Idempotence Test --- .../CompositeSubscriptionTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java index 99e3f29610..551a6b2f66 100644 --- a/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java +++ b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java @@ -124,4 +124,24 @@ public void testClear() { assertTrue(s3.isUnsubscribed()); assertTrue(s.isUnsubscribed()); } + + @Test + public void testUnsubscribeIdempotence() { + final AtomicInteger counter = new AtomicInteger(); + CompositeSubscription s = new CompositeSubscription(); + s.add(new Subscription() { + + @Override + public void unsubscribe() { + counter.incrementAndGet(); + } + }); + + s.unsubscribe(); + s.unsubscribe(); + s.unsubscribe(); + + // we should have only unsubscribed once + assertEquals(1, counter.get()); + } } From bd50a1163b6b26cc720d9309c612c6964e579d3a Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Fri, 8 Nov 2013 00:10:20 +0000 Subject: [PATCH 244/333] [Gradle Release Plugin] - pre tag commit: '0.14.9'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ac10ebec80..745692a3f7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.9-SNAPSHOT +version=0.14.9 From 20a86954480a7eb5e177966e7eb5b9582dc97b8c Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Fri, 8 Nov 2013 00:10:24 +0000 Subject: [PATCH 245/333] [Gradle Release Plugin] - new version commit: '0.14.10-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 745692a3f7..6867e2eeab 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.9 +version=0.14.10-SNAPSHOT From e844de52c4c31726ccff9a3d30e6d38a2dab00eb Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 28 Oct 2013 17:57:50 +0800 Subject: [PATCH 246/333] Implemented the 'min' and 'max' operators --- .../main/java/rx/operators/OperationMax.java | 280 ++++++++++++++++++ .../main/java/rx/operators/OperationMin.java | 280 ++++++++++++++++++ 2 files changed, 560 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationMax.java create mode 100644 rxjava-core/src/main/java/rx/operators/OperationMin.java diff --git a/rxjava-core/src/main/java/rx/operators/OperationMax.java b/rxjava-core/src/main/java/rx/operators/OperationMax.java new file mode 100644 index 0000000000..21a7b6105a --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationMax.java @@ -0,0 +1,280 @@ +/** + * Copyright 2013 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.operators; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import org.junit.Test; +import org.mockito.InOrder; + +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +public class OperationMax { + + public static > Observable max( + Observable source) { + return source.reduce(new Func2() { + @Override + public T call(T acc, T value) { + if (acc.compareTo(value) > 0) { + return acc; + } + return value; + } + }); + } + + public static Observable max(Observable source, + final Comparator comparator) { + return source.reduce(new Func2() { + @Override + public T call(T acc, T value) { + if (comparator.compare(acc, value) > 0) { + return acc; + } + return value; + } + }); + } + + public static > Observable> maxBy( + Observable source, final Func1 selector) { + return source.reduce(new ArrayList(), + new Func2, T, List>() { + + @Override + public List call(List acc, T value) { + if (acc.isEmpty()) { + acc.add(value); + } else { + int flag = selector.call(acc.get(0)).compareTo( + selector.call(value)); + if (flag < 0) { + acc.clear(); + acc.add(value); + } else if (flag == 0) { + acc.add(value); + } + } + return acc; + } + }); + } + + public static Observable> maxBy(Observable source, + final Func1 selector, final Comparator comparator) { + return source.reduce(new ArrayList(), + new Func2, T, List>() { + + @Override + public List call(List acc, T value) { + if (acc.isEmpty()) { + acc.add(value); + } else { + int flag = comparator.compare( + selector.call(acc.get(0)), + selector.call(value)); + if (flag < 0) { + acc.clear(); + acc.add(value); + } else if (flag == 0) { + acc.add(value); + } + } + return acc; + } + }); + } + + public static class UnitTest { + + @Test + public void testMax() { + Observable observable = OperationMax.max(Observable.from( + 2, 3, 1, 4)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(4); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxWithEmpty() { + Observable observable = OperationMax.max(Observable + . empty()); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + any(UnsupportedOperationException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxWithComparator() { + Observable observable = OperationMax.max( + Observable.from(2, 3, 1, 4), new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(1); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxWithComparatorAndEmpty() { + Observable observable = OperationMax.max( + Observable. empty(), new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + any(UnsupportedOperationException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxBy() { + Observable> observable = OperationMax.maxBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext( + Arrays.asList("1", "3", "5")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxByWithEmpty() { + Observable> observable = OperationMax.maxBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxByWithComparator() { + Observable> observable = OperationMax.maxBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext( + Arrays.asList("2", "4", "6")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxByWithComparatorAndEmpty() { + Observable> observable = OperationMax.maxBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + } + +} diff --git a/rxjava-core/src/main/java/rx/operators/OperationMin.java b/rxjava-core/src/main/java/rx/operators/OperationMin.java new file mode 100644 index 0000000000..0b08a2f9ae --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationMin.java @@ -0,0 +1,280 @@ +/** + * Copyright 2013 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.operators; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import org.junit.Test; +import org.mockito.InOrder; + +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +public class OperationMin { + + public static > Observable min( + Observable source) { + return source.reduce(new Func2() { + @Override + public T call(T acc, T value) { + if (acc.compareTo(value) < 0) { + return acc; + } + return value; + } + }); + } + + public static Observable min(Observable source, + final Comparator comparator) { + return source.reduce(new Func2() { + @Override + public T call(T acc, T value) { + if (comparator.compare(acc, value) < 0) { + return acc; + } + return value; + } + }); + } + + public static > Observable> minBy( + Observable source, final Func1 selector) { + return source.reduce(new ArrayList(), + new Func2, T, List>() { + + @Override + public List call(List acc, T value) { + if (acc.isEmpty()) { + acc.add(value); + } else { + int flag = selector.call(acc.get(0)).compareTo( + selector.call(value)); + if (flag > 0) { + acc.clear(); + acc.add(value); + } else if (flag == 0) { + acc.add(value); + } + } + return acc; + } + }); + } + + public static Observable> minBy(Observable source, + final Func1 selector, final Comparator comparator) { + return source.reduce(new ArrayList(), + new Func2, T, List>() { + + @Override + public List call(List acc, T value) { + if (acc.isEmpty()) { + acc.add(value); + } else { + int flag = comparator.compare( + selector.call(acc.get(0)), + selector.call(value)); + if (flag > 0) { + acc.clear(); + acc.add(value); + } else if (flag == 0) { + acc.add(value); + } + } + return acc; + } + }); + } + + public static class UnitTest { + + @Test + public void testMin() { + Observable observable = OperationMin.min(Observable.from( + 2, 3, 1, 4)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(1); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinWithEmpty() { + Observable observable = OperationMin.min(Observable + . empty()); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + any(UnsupportedOperationException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinWithComparator() { + Observable observable = OperationMin.min( + Observable.from(2, 3, 1, 4), new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(4); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinWithComparatorAndEmpty() { + Observable observable = OperationMin.min( + Observable. empty(), new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + any(UnsupportedOperationException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinBy() { + Observable> observable = OperationMin.minBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext( + Arrays.asList("2", "4", "6")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinByWithEmpty() { + Observable> observable = OperationMin.minBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinByWithComparator() { + Observable> observable = OperationMin.minBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext( + Arrays.asList("1", "3", "5")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinByWithComparatorAndEmpty() { + Observable> observable = OperationMin.minBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + } + +} From 976ef36a9559a48a38d8f621902bf7a1d5ff1f3c Mon Sep 17 00:00:00 2001 From: zsxwing Date: Tue, 29 Oct 2013 11:08:31 +0800 Subject: [PATCH 247/333] Added the min and max operators in Observable and comments --- rxjava-core/src/main/java/rx/Observable.java | 110 ++++++++++++++++++ .../main/java/rx/operators/OperationMax.java | 3 + .../main/java/rx/operators/OperationMin.java | 3 + 3 files changed, 116 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 3dcff7fb69..fe233a5574 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -52,8 +52,10 @@ import rx.operators.OperationLast; import rx.operators.OperationMap; import rx.operators.OperationMaterialize; +import rx.operators.OperationMax; import rx.operators.OperationMerge; import rx.operators.OperationMergeDelayError; +import rx.operators.OperationMin; import rx.operators.OperationMulticast; import rx.operators.OperationObserveOn; import rx.operators.OperationOnErrorResumeNextViaFunction; @@ -3631,6 +3633,114 @@ public static Observable averageDoubles(Observable source) { return OperationAverage.averageDoubles(source); } + /** + * Returns the minimum element in an observable sequence. + * For an empty source, it causes an {@link UnsupportedOperationException}. + * + * @param source + * an observable sequence to determine the minimum element of. + * @return an observable emitting the minimum element. + * @see MSDN: Observable.Min + */ + public static > Observable min(Observable source) { + return OperationMin.min(source); + } + + /** + * Returns the minimum element in an observable sequence according to the specified comparator. + * For an empty source, it causes an {@link UnsupportedOperationException}. + * + * @param comparator + * the comparer used to compare elements. + * @return an observable emitting the minimum value according to the specified comparator. + * @see MSDN: Observable.Min + */ + public Observable min(Comparator comparator) { + return OperationMin.min(this, comparator); + } + + /** + * Returns the elements in an observable sequence with the minimum key value. + * For an empty source, it returns an observable emitting an empty List. + * + * @param selector + * the key selector function. + * @return an observable emitting a List of the elements with the minimum key value. + * @see MSDN: Observable.MinBy + */ + public > Observable> minBy(Func1 selector) { + return OperationMin.minBy(this, selector); + } + + /** + * Returns the elements in an observable sequence with the minimum key value according to the specified comparator. + * For an empty source, it returns an observable emitting an empty List. + * + * @param selector + * the key selector function. + * @param comparator + * the comparator used to compare key values. + * @return an observable emitting a List of the elements with the minimum key value according to the specified comparator. + * @see MSDN: Observable.MinBy + */ + public Observable> minBy(Func1 selector, Comparator comparator) { + return OperationMin.minBy(this, selector, comparator); + } + + /** + * Returns the maximum element in an observable sequence. + * For an empty source, it causes an {@link UnsupportedOperationException}. + * + * @param source + * an observable sequence to determine the maximum element of. + * @return an observable emitting the maximum element. + * @see MSDN: Observable.Max + */ + public static > Observable max(Observable source) { + return OperationMax.max(source); + } + + /** + * Returns the maximum element in an observable sequence according to the specified comparator. + * For an empty source, it causes an {@link UnsupportedOperationException}. + * + * @param comparator + * the comparer used to compare elements. + * @return an observable emitting the maximum value according to the specified comparator. + * @see MSDN: Observable.Max + */ + public Observable max(Comparator comparator) { + return OperationMax.max(this, comparator); + } + + /** + * Returns the elements in an observable sequence with the maximum key value. + * For an empty source, it returns an observable emitting an empty List. + * + * @param selector + * the key selector function. + * @return an observable emitting a List of the elements with the maximum key value. + * @see MSDN: Observable.MaxBy + */ + public > Observable> maxBy(Func1 selector) { + return OperationMax.maxBy(this, selector); + } + + /** + * Returns the elements in an observable sequence with the maximum key value according to the specified comparator. + * For an empty source, it returns an observable emitting an empty List. + * + * @param selector + * the key selector function. + * @param comparator + * the comparator used to compare key values. + * @return an observable emitting a List of the elements with the maximum key value according to the specified comparator. + * @see MSDN: Observable.MaxBy + */ + public Observable> maxBy(Func1 selector, Comparator comparator) { + return OperationMax.maxBy(this, selector, comparator); + } + /** * Returns a {@link ConnectableObservable} that shares a single subscription to the underlying * Observable that will replay all of its items and notifications to any future {@link Observer}. diff --git a/rxjava-core/src/main/java/rx/operators/OperationMax.java b/rxjava-core/src/main/java/rx/operators/OperationMax.java index 21a7b6105a..1f462ed3fb 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMax.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMax.java @@ -33,6 +33,9 @@ import rx.util.functions.Func1; import rx.util.functions.Func2; +/** + * Returns the maximum element in an observable sequence. + */ public class OperationMax { public static > Observable max( diff --git a/rxjava-core/src/main/java/rx/operators/OperationMin.java b/rxjava-core/src/main/java/rx/operators/OperationMin.java index 0b08a2f9ae..632cd37ce5 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMin.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMin.java @@ -33,6 +33,9 @@ import rx.util.functions.Func1; import rx.util.functions.Func2; +/** + * Returns the minimum element in an observable sequence. + */ public class OperationMin { public static > Observable min( From 1db734986e8fac52dee9e0223c54911775abe8c1 Mon Sep 17 00:00:00 2001 From: Scott Fleckenstein Date: Thu, 7 Nov 2013 18:32:36 -0800 Subject: [PATCH 248/333] Adds beginnings of doOnEach operator --- rxjava-core/src/main/java/rx/Observable.java | 17 +++ .../java/rx/operators/OperationDoOnEach.java | 66 +++++++++++ .../rx/operators/OperationDoOnEachTest.java | 108 ++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationDoOnEach.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationDoOnEachTest.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 6bcd3e9f77..dd357eeda0 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -43,6 +43,7 @@ import rx.operators.OperationDematerialize; import rx.operators.OperationDistinct; import rx.operators.OperationDistinctUntilChanged; +import rx.operators.OperationDoOnEach; import rx.operators.OperationElementAt; import rx.operators.OperationFilter; import rx.operators.OperationFinally; @@ -4777,6 +4778,22 @@ public static Observable amb(Iterable> return create(OperationAmb.amb(sources)); } + + /** + * Invokes an action for each element in the observable sequence. + * + * @param func + * The action to invoke for each element in the source sequence. + * + * @return + * The source sequence with the side-effecting behavior applied. + * @see MSDN: Observable.Amb + */ + public Observable doOnEach(Observer observer) { + return create(OperationDoOnEach.doOnEach(this, observer)); + } + + /** * Whether a given {@link Function} is an internal implementation inside rx.* packages or not. *

    diff --git a/rxjava-core/src/main/java/rx/operators/OperationDoOnEach.java b/rxjava-core/src/main/java/rx/operators/OperationDoOnEach.java new file mode 100644 index 0000000000..acd841fc2f --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationDoOnEach.java @@ -0,0 +1,66 @@ +/** + * Copyright 2013 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.operators; + +import rx.Observable; +import rx.Observer; +import rx.Observable.OnSubscribeFunc; +import rx.Subscription; + +/** + * Converts the elements of an observable sequence to the specified type. + */ +public class OperationDoOnEach { + public static OnSubscribeFunc doOnEach(Observable source, Observer observer) { + return new DoOnEachObservable(source, observer); + } + + private static class DoOnEachObservable implements OnSubscribeFunc { + + private final Observable source; + private final Observer doOnEachObserver; + + public DoOnEachObservable(Observable source, Observer doOnEachObserver) { + this.source = source; + this.doOnEachObserver = doOnEachObserver; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + return source.subscribe(new Observer() { + @Override + public void onCompleted() { + doOnEachObserver.onCompleted(); + observer.onCompleted(); + } + + @Override + public void onError(Throwable e) { + doOnEachObserver.onError(e); + observer.onError(e); + } + + @Override + public void onNext(T value) { + doOnEachObserver.onNext(value); + observer.onNext(value); + } + + }); + } + + } +} \ No newline at end of file diff --git a/rxjava-core/src/test/java/rx/operators/OperationDoOnEachTest.java b/rxjava-core/src/test/java/rx/operators/OperationDoOnEachTest.java new file mode 100644 index 0000000000..4bf017761c --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationDoOnEachTest.java @@ -0,0 +1,108 @@ +/** + * Copyright 2013 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.operators; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperationMap.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import rx.Observable; +import rx.Observer; +import rx.concurrency.Schedulers; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +public class OperationDoOnEachTest { + + @Mock + Observer subscribedObserver; + @Mock + Observer sideEffectObserver; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testDoOnEach() { + Observable base = Observable.from("a", "b", "c"); + Observable doOnEach = base.doOnEach(sideEffectObserver); + + doOnEach.subscribe(subscribedObserver); + + // ensure the leaf observer is still getting called + verify(subscribedObserver, never()).onError(any(Throwable.class)); + verify(subscribedObserver, times(1)).onNext("a"); + verify(subscribedObserver, times(1)).onNext("b"); + verify(subscribedObserver, times(1)).onNext("c"); + verify(subscribedObserver, times(1)).onCompleted(); + + // ensure our injected observer is getting called + verify(sideEffectObserver, never()).onError(any(Throwable.class)); + verify(sideEffectObserver, times(1)).onNext("a"); + verify(sideEffectObserver, times(1)).onNext("b"); + verify(sideEffectObserver, times(1)).onNext("c"); + verify(sideEffectObserver, times(1)).onCompleted(); + } + + + + @Test + public void testDoOnEachWithError() { + Observable base = Observable.from("one", "fail", "two", "three", "fail"); + Observable errs = base.map(new Func1() { + @Override + public String call(String s) { + if ("fail".equals(s)) { + throw new RuntimeException("Forced Failure"); + } + return s; + } + }); + + Observable doOnEach = errs.doOnEach(sideEffectObserver); + + + doOnEach.subscribe(subscribedObserver); + verify(subscribedObserver, times(1)).onNext("one"); + verify(subscribedObserver, never()).onNext("two"); + verify(subscribedObserver, never()).onNext("three"); + verify(subscribedObserver, never()).onCompleted(); + verify(subscribedObserver, times(1)).onError(any(Throwable.class)); + + + verify(sideEffectObserver, times(1)).onNext("one"); + verify(sideEffectObserver, never()).onNext("two"); + verify(sideEffectObserver, never()).onNext("three"); + verify(sideEffectObserver, never()).onCompleted(); + verify(sideEffectObserver, times(1)).onError(any(Throwable.class)); + } + + +} From 04edc95f4928d93c37ffb93029859e0e72c00065 Mon Sep 17 00:00:00 2001 From: Scott Fleckenstein Date: Thu, 7 Nov 2013 18:50:38 -0800 Subject: [PATCH 249/333] Adds other overrides for doOnEach --- rxjava-core/src/main/java/rx/Observable.java | 136 ++++++++++++++++++- 1 file changed, 134 insertions(+), 2 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index dd357eeda0..b4bfd258f1 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4782,17 +4782,149 @@ public static Observable amb(Iterable> /** * Invokes an action for each element in the observable sequence. * - * @param func + * @param observer * The action to invoke for each element in the source sequence. * * @return * The source sequence with the side-effecting behavior applied. - * @see MSDN: Observable.Amb + * @see MSDN: Observable.Do */ public Observable doOnEach(Observer observer) { return create(OperationDoOnEach.doOnEach(this, observer)); } + /** + * Invokes an action for each element in the observable sequence. + * + * @param onNext + * The action to invoke for each element in the source sequence. + * + * @return + * The source sequence with the side-effecting behavior applied. + * @see MSDN: Observable.Do + */ + public Observable doOnEach(final Action1 onNext) { + Observer observer = new Observer() { + @Override + public void onCompleted() {} + + @Override + public void onError(Throwable e) {} + + @Override + public void onNext(T args) { + onNext.call(args); + } + + }; + + + return create(OperationDoOnEach.doOnEach(this, observer)); + } + + /** + * Invokes an action for each element in the observable sequence. + * + * @param onNext + * The action to invoke for each element in the source sequence. + * @param onCompleted + * The action to invoke when the source sequence is completed. + * + * @return + * The source sequence with the side-effecting behavior applied. + * @see MSDN: Observable.Do + */ + public Observable doOnEach(final Action1 onNext, final Action0 onCompleted) { + Observer observer = new Observer() { + @Override + public void onCompleted() { + onCompleted.call(); + } + + @Override + public void onError(Throwable e) {} + + @Override + public void onNext(T args) { + onNext.call(args); + } + + }; + + + return create(OperationDoOnEach.doOnEach(this, observer)); + } + + /** + * Invokes an action for each element in the observable sequence. + * + * @param onNext + * The action to invoke for each element in the source sequence. + * @param onError + * The action to invoke when the source sequence calls onError. + * + * @return + * The source sequence with the side-effecting behavior applied. + * @see MSDN: Observable.Do + */ + public Observable doOnEach(final Action1 onNext, final Action1 onError) { + Observer observer = new Observer() { + @Override + public void onCompleted() {} + + @Override + public void onError(Throwable e) { + onError.call(e); + } + + @Override + public void onNext(T args) { + onNext.call(args); + } + + }; + + + return create(OperationDoOnEach.doOnEach(this, observer)); + } + + + /** + * Invokes an action for each element in the observable sequence. + * + * @param onNext + * The action to invoke for each element in the source sequence. + * @param onError + * The action to invoke when the source sequence calls onError. + * @param onCompleted + * The action to invoke when the source sequence is completed. + * + * @return + * The source sequence with the side-effecting behavior applied. + * @see MSDN: Observable.Do + */ + public Observable doOnEach(final Action1 onNext, final Action1 onError, final Action0 onCompleted) { + Observer observer = new Observer() { + @Override + public void onCompleted() { + onCompleted.call(); + } + + @Override + public void onError(Throwable e) { + onError.call(e); + } + + @Override + public void onNext(T args) { + onNext.call(args); + } + + }; + + + return create(OperationDoOnEach.doOnEach(this, observer)); + } /** * Whether a given {@link Function} is an internal implementation inside rx.* packages or not. From 75b2ddd6fb2b694c7b16db5a2f057867597bbec9 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Fri, 8 Nov 2013 11:15:47 +0800 Subject: [PATCH 250/333] Separating unit tests out and update the comments --- rxjava-core/src/main/java/rx/Observable.java | 17 +- .../main/java/rx/operators/OperationMax.java | 177 ----------------- .../main/java/rx/operators/OperationMin.java | 177 ----------------- .../java/rx/operators/OperationMaxTest.java | 182 ++++++++++++++++++ .../java/rx/operators/OperationMinTest.java | 182 ++++++++++++++++++ 5 files changed, 377 insertions(+), 358 deletions(-) create mode 100644 rxjava-core/src/test/java/rx/operators/OperationMaxTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationMinTest.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index fe233a5574..3fbd7e6d37 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; @@ -3635,11 +3636,13 @@ public static Observable averageDoubles(Observable source) { /** * Returns the minimum element in an observable sequence. - * For an empty source, it causes an {@link UnsupportedOperationException}. + * For an empty source, it causes an {@link IllegalArgumentException}. * * @param source * an observable sequence to determine the minimum element of. * @return an observable emitting the minimum element. + * @throws IllegalArgumentException + * if the source is empty * @see MSDN: Observable.Min */ public static > Observable min(Observable source) { @@ -3648,11 +3651,13 @@ public static > Observable min(Observable source) /** * Returns the minimum element in an observable sequence according to the specified comparator. - * For an empty source, it causes an {@link UnsupportedOperationException}. + * For an empty source, it causes an {@link IllegalArgumentException}. * * @param comparator * the comparer used to compare elements. * @return an observable emitting the minimum value according to the specified comparator. + * @throws IllegalArgumentException + * if the source is empty * @see MSDN: Observable.Min */ public Observable min(Comparator comparator) { @@ -3689,11 +3694,13 @@ public Observable> minBy(Func1 selector, Comparator compara /** * Returns the maximum element in an observable sequence. - * For an empty source, it causes an {@link UnsupportedOperationException}. + * For an empty source, it causes an {@link IllegalArgumentException}. * * @param source * an observable sequence to determine the maximum element of. * @return an observable emitting the maximum element. + * @throws IllegalArgumentException + * if the source is empty. * @see MSDN: Observable.Max */ public static > Observable max(Observable source) { @@ -3702,11 +3709,13 @@ public static > Observable max(Observable source) /** * Returns the maximum element in an observable sequence according to the specified comparator. - * For an empty source, it causes an {@link UnsupportedOperationException}. + * For an empty source, it causes an {@link IllegalArgumentException}. * * @param comparator * the comparer used to compare elements. * @return an observable emitting the maximum value according to the specified comparator. + * @throws IllegalArgumentException + * if the source is empty. * @see MSDN: Observable.Max */ public Observable max(Comparator comparator) { diff --git a/rxjava-core/src/main/java/rx/operators/OperationMax.java b/rxjava-core/src/main/java/rx/operators/OperationMax.java index 1f462ed3fb..bb22f8a33c 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMax.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMax.java @@ -15,21 +15,11 @@ */ package rx.operators; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; - import java.util.ArrayList; -import java.util.Arrays; import java.util.Comparator; import java.util.List; -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; -import rx.Observer; import rx.util.functions.Func1; import rx.util.functions.Func2; @@ -113,171 +103,4 @@ public List call(List acc, T value) { }); } - public static class UnitTest { - - @Test - public void testMax() { - Observable observable = OperationMax.max(Observable.from( - 2, 3, 1, 4)); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(4); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxWithEmpty() { - Observable observable = OperationMax.max(Observable - . empty()); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError( - any(UnsupportedOperationException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxWithComparator() { - Observable observable = OperationMax.max( - Observable.from(2, 3, 1, 4), new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(1); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxWithComparatorAndEmpty() { - Observable observable = OperationMax.max( - Observable. empty(), new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError( - any(UnsupportedOperationException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxBy() { - Observable> observable = OperationMax.maxBy( - Observable.from("1", "2", "3", "4", "5", "6"), - new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext( - Arrays.asList("1", "3", "5")); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxByWithEmpty() { - Observable> observable = OperationMax.maxBy( - Observable. empty(), new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(new ArrayList()); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxByWithComparator() { - Observable> observable = OperationMax.maxBy( - Observable.from("1", "2", "3", "4", "5", "6"), - new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }, new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext( - Arrays.asList("2", "4", "6")); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxByWithComparatorAndEmpty() { - Observable> observable = OperationMax.maxBy( - Observable. empty(), new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }, new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(new ArrayList()); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - } - } diff --git a/rxjava-core/src/main/java/rx/operators/OperationMin.java b/rxjava-core/src/main/java/rx/operators/OperationMin.java index 632cd37ce5..13b1188f03 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMin.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMin.java @@ -15,21 +15,11 @@ */ package rx.operators; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; - import java.util.ArrayList; -import java.util.Arrays; import java.util.Comparator; import java.util.List; -import org.junit.Test; -import org.mockito.InOrder; - import rx.Observable; -import rx.Observer; import rx.util.functions.Func1; import rx.util.functions.Func2; @@ -113,171 +103,4 @@ public List call(List acc, T value) { }); } - public static class UnitTest { - - @Test - public void testMin() { - Observable observable = OperationMin.min(Observable.from( - 2, 3, 1, 4)); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(1); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinWithEmpty() { - Observable observable = OperationMin.min(Observable - . empty()); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError( - any(UnsupportedOperationException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinWithComparator() { - Observable observable = OperationMin.min( - Observable.from(2, 3, 1, 4), new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(4); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinWithComparatorAndEmpty() { - Observable observable = OperationMin.min( - Observable. empty(), new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError( - any(UnsupportedOperationException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinBy() { - Observable> observable = OperationMin.minBy( - Observable.from("1", "2", "3", "4", "5", "6"), - new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext( - Arrays.asList("2", "4", "6")); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinByWithEmpty() { - Observable> observable = OperationMin.minBy( - Observable. empty(), new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(new ArrayList()); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinByWithComparator() { - Observable> observable = OperationMin.minBy( - Observable.from("1", "2", "3", "4", "5", "6"), - new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }, new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext( - Arrays.asList("1", "3", "5")); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinByWithComparatorAndEmpty() { - Observable> observable = OperationMin.minBy( - Observable. empty(), new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }, new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(new ArrayList()); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - } - } diff --git a/rxjava-core/src/test/java/rx/operators/OperationMaxTest.java b/rxjava-core/src/test/java/rx/operators/OperationMaxTest.java new file mode 100644 index 0000000000..03c3ae5abd --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationMaxTest.java @@ -0,0 +1,182 @@ +package rx.operators; + +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import org.junit.Test; +import org.mockito.InOrder; + +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; + +public class OperationMaxTest { + @Test + public void testMax() { + Observable observable = OperationMax.max(Observable.from(2, 3, + 1, 4)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(4); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxWithEmpty() { + Observable observable = OperationMax.max(Observable + . empty()); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + isA(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxWithComparator() { + Observable observable = OperationMax.max( + Observable.from(2, 3, 1, 4), new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(1); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxWithComparatorAndEmpty() { + Observable observable = OperationMax.max( + Observable. empty(), new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + isA(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxBy() { + Observable> observable = OperationMax.maxBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(Arrays.asList("1", "3", "5")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxByWithEmpty() { + Observable> observable = OperationMax.maxBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxByWithComparator() { + Observable> observable = OperationMax.maxBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(Arrays.asList("2", "4", "6")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxByWithComparatorAndEmpty() { + Observable> observable = OperationMax.maxBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationMinTest.java b/rxjava-core/src/test/java/rx/operators/OperationMinTest.java new file mode 100644 index 0000000000..971baf1ddb --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationMinTest.java @@ -0,0 +1,182 @@ +package rx.operators; + +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import org.junit.Test; +import org.mockito.InOrder; + +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; + +public class OperationMinTest { + @Test + public void testMin() { + Observable observable = OperationMin.min(Observable.from(2, 3, + 1, 4)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(1); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinWithEmpty() { + Observable observable = OperationMin.min(Observable + . empty()); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + isA(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinWithComparator() { + Observable observable = OperationMin.min( + Observable.from(2, 3, 1, 4), new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(4); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinWithComparatorAndEmpty() { + Observable observable = OperationMin.min( + Observable. empty(), new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + isA(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinBy() { + Observable> observable = OperationMin.minBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(Arrays.asList("2", "4", "6")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinByWithEmpty() { + Observable> observable = OperationMin.minBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinByWithComparator() { + Observable> observable = OperationMin.minBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(Arrays.asList("1", "3", "5")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinByWithComparatorAndEmpty() { + Observable> observable = OperationMin.minBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } +} From 205a16b61dad6c148e030eca956329a624d452d3 Mon Sep 17 00:00:00 2001 From: Scott Fleckenstein Date: Thu, 7 Nov 2013 19:20:07 -0800 Subject: [PATCH 251/333] Adds scala adapters for doOnEach operator --- .../main/scala/rx/lang/scala/Observable.scala | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index d845a65fa5..8dd5d1b9b2 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1807,6 +1807,28 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) def withFilter(p: T => Boolean): WithFilter[T] = { new WithFilter[T](p, asJava) } + + + def doOnEach(observer: Observer[T]): Observable[T] = { + Observable[T](asJava.doOnEach(observer)) + } + + def doOnEach(onNext: T => Unit): Observable[T] = { + Observable[T](asJava.doOnEach(onNext)) + } + + def doOnEach(onNext: T => Unit, onComplete: () => Unit): Observable[T] = { + Observable[T](asJava.doOnEach(onNext, onComplete)) + } + + def doOnEach(onNext: T => Unit, onError: Throwable => Unit): Observable[T] = { + Observable[T](asJava.doOnEach(onNext, onError)) + } + + def doOnEach(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit): Observable[T] = { + Observable[T](asJava.doOnEach(onNext, onError, onComplete)) + } + } From 1b0deef23dba0c42b4c7ff989bc9bc2f8b8a7513 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 7 Nov 2013 19:54:13 -0800 Subject: [PATCH 252/333] Version 0.14.9 --- CHANGES.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index cb88a7c064..63e7409f14 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,14 @@ # RxJava Releases # +### Version 0.14.9 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.9%22)) ### + +* [Pull 477](https://github.com/Netflix/RxJava/pull/477) BugFix: CompositeSubscription +* [Pull 476](https://github.com/Netflix/RxJava/pull/476) BugFix: Don't emit null onComplete when no onNext received in AsyncSubject +* [Pull 474](https://github.com/Netflix/RxJava/pull/474) BugFix: Reduce an empty observable +* [Pull 474](https://github.com/Netflix/RxJava/pull/474) BugFix: non-deterministic unit test +* [Pull 472](https://github.com/Netflix/RxJava/pull/472) BugFix: Issue 431 Unsubscribe with Schedulers.newThread +* [Pull 470](https://github.com/Netflix/RxJava/pull/470) Operator: Last + ### Version 0.14.8 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.8%22)) ### * [Pull 460](https://github.com/Netflix/RxJava/pull/460) Operator: Amb From 1e37030f62f1be14e8d7d42fd0cfa28d98d8815b Mon Sep 17 00:00:00 2001 From: zsxwing Date: Fri, 8 Nov 2013 13:42:31 +0800 Subject: [PATCH 253/333] BugFix: Throw an IllegalArgumentException instead of ArithmeticException if the observable is empty --- rxjava-core/src/main/java/rx/Observable.java | 4 +++- .../main/java/rx/operators/OperationAverage.java | 14 ++++++++++---- .../java/rx/operators/OperationAverageTest.java | 8 ++++---- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 3dcff7fb69..0be4e4e74a 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -3593,7 +3593,7 @@ public static Observable sumDoubles(Observable source) { /** * Returns an Observable that computes the average of all elements in the source Observable. - * For an empty source, it causes an ArithmeticException. + * For an empty source, it causes an IllegalArgumentException. *

    * * @@ -3601,6 +3601,8 @@ public static Observable sumDoubles(Observable source) { * Source observable to compute the average of. * @return an Observable emitting the averageof all the elements of the source Observable * as its single item. + * @throws IllegalArgumentException + * if Observable sequence is empty. * @see MSDN: Observable.Average */ public static Observable average(Observable source) { diff --git a/rxjava-core/src/main/java/rx/operators/OperationAverage.java b/rxjava-core/src/main/java/rx/operators/OperationAverage.java index 3fbbd1f05e..35abc99eb5 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAverage.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAverage.java @@ -44,7 +44,10 @@ public Tuple2 call(Tuple2 accu, Integer next) { }).map(new Func1, Integer>() { @Override public Integer call(Tuple2 result) { - return result.current / result.count; // may throw DivisionByZero, this should be correct... + if (result.count == 0) { + throw new IllegalArgumentException("Sequence contains no elements"); + } + return result.current / result.count; } }); } @@ -58,7 +61,10 @@ public Tuple2 call(Tuple2 accu, Long next) { }).map(new Func1, Long>() { @Override public Long call(Tuple2 result) { - return result.current / result.count; // may throw DivisionByZero, this should be correct... + if (result.count == 0) { + throw new IllegalArgumentException("Sequence contains no elements"); + } + return result.current / result.count; } }); } @@ -73,7 +79,7 @@ public Tuple2 call(Tuple2 accu, Float next) { @Override public Float call(Tuple2 result) { if (result.count == 0) { - throw new ArithmeticException("divide by zero"); + throw new IllegalArgumentException("Sequence contains no elements"); } return result.current / result.count; } @@ -90,7 +96,7 @@ public Tuple2 call(Tuple2 accu, Double next) { @Override public Double call(Tuple2 result) { if (result.count == 0) { - throw new ArithmeticException("divide by zero"); + throw new IllegalArgumentException("Sequence contains no elements"); } return result.current / result.count; } diff --git a/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java b/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java index a8ffa4706b..357743da17 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationAverageTest.java @@ -52,7 +52,7 @@ public void testEmptyAverage() throws Throwable { average(src).subscribe(w); verify(w, never()).onNext(anyInt()); - verify(w, times(1)).onError(any(ArithmeticException.class)); + verify(w, times(1)).onError(isA(IllegalArgumentException.class)); verify(w, never()).onCompleted(); } @@ -73,7 +73,7 @@ public void testEmptyAverageLongs() throws Throwable { averageLongs(src).subscribe(wl); verify(wl, never()).onNext(anyLong()); - verify(wl, times(1)).onError(any(ArithmeticException.class)); + verify(wl, times(1)).onError(isA(IllegalArgumentException.class)); verify(wl, never()).onCompleted(); } @@ -94,7 +94,7 @@ public void testEmptyAverageFloats() throws Throwable { averageFloats(src).subscribe(wf); verify(wf, never()).onNext(anyFloat()); - verify(wf, times(1)).onError(any(ArithmeticException.class)); + verify(wf, times(1)).onError(isA(IllegalArgumentException.class)); verify(wf, never()).onCompleted(); } @@ -115,7 +115,7 @@ public void testEmptyAverageDoubles() throws Throwable { averageDoubles(src).subscribe(wd); verify(wd, never()).onNext(anyDouble()); - verify(wd, times(1)).onError(any(ArithmeticException.class)); + verify(wd, times(1)).onError(isA(IllegalArgumentException.class)); verify(wd, never()).onCompleted(); } } From 2eded0a0ac73af0bccdbd256f0460d6a63ecfe41 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 11 Nov 2013 14:32:15 +0800 Subject: [PATCH 254/333] Implement the 'Using' operator --- rxjava-core/src/main/java/rx/Observable.java | 16 ++ .../java/rx/operators/OperationUsing.java | 59 +++++ .../java/rx/operators/OperationUsingTest.java | 219 ++++++++++++++++++ 3 files changed, 294 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationUsing.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationUsingTest.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 3dcff7fb69..75d5842a76 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -83,6 +83,7 @@ import rx.operators.OperationToObservableIterable; import rx.operators.OperationToObservableList; import rx.operators.OperationToObservableSortedList; +import rx.operators.OperationUsing; import rx.operators.OperationWindow; import rx.operators.OperationZip; import rx.operators.SafeObservableSubscription; @@ -4594,6 +4595,21 @@ public Observable> timeInterval(Scheduler scheduler) { return create(OperationTimeInterval.timeInterval(this, scheduler)); } + /** + * Constructs an observable sequence that depends on a resource object. + * + * @param resourceFactory + * The factory function to obtain a resource object. + * @param observableFactory + * The factory function to obtain an observable sequence that depends on the obtained resource. + * @return + * The observable sequence whose lifetime controls the lifetime of the dependent resource object. + * @see MSDN: Observable.Using + */ + public static Observable using(Func0 resourceFactory, Func1> observableFactory) { + return create(OperationUsing.using(resourceFactory, observableFactory)); + } + /** * Propagates the observable sequence that reacts first. * diff --git a/rxjava-core/src/main/java/rx/operators/OperationUsing.java b/rxjava-core/src/main/java/rx/operators/OperationUsing.java new file mode 100644 index 0000000000..dd0cc38d65 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationUsing.java @@ -0,0 +1,59 @@ +/** + * Copyright 2013 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.operators; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Func0; +import rx.util.functions.Func1; + +/** + * Constructs an observable sequence that depends on a resource object. + */ +public class OperationUsing { + + public static OnSubscribeFunc using( + final Func0 resourceFactory, + final Func1> observableFactory) { + return new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + Subscription resourceSubscription = Subscriptions.empty(); + try { + RESOURCE resource = resourceFactory.call(); + if (resource != null) { + resourceSubscription = resource; + } + Observable observable = observableFactory.call(resource); + SafeObservableSubscription subscription = new SafeObservableSubscription(); + // Use SafeObserver to guarantee resourceSubscription will + // be unsubscribed. + return subscription.wrap(new CompositeSubscription( + observable.subscribe(new SafeObserver( + subscription, observer)), + resourceSubscription)); + } catch (Throwable e) { + resourceSubscription.unsubscribe(); + return Observable. error(e).subscribe(observer); + } + } + }; + } +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationUsingTest.java b/rxjava-core/src/test/java/rx/operators/OperationUsingTest.java new file mode 100644 index 0000000000..42d31cea9a --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationUsingTest.java @@ -0,0 +1,219 @@ +/** + * Copyright 2013 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.operators; + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static rx.operators.OperationUsing.using; + +import org.junit.Test; +import org.mockito.InOrder; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; +import rx.util.functions.Func0; +import rx.util.functions.Func1; + +public class OperationUsingTest { + + @SuppressWarnings("serial") + private static class TestException extends RuntimeException { + } + + private static interface Resource extends Subscription { + public String getTextFromWeb(); + + @Override + public void unsubscribe(); + } + + @Test + public void testUsing() { + final Resource resource = mock(Resource.class); + when(resource.getTextFromWeb()).thenReturn("Hello world!"); + + Func0 resourceFactory = new Func0() { + @Override + public Resource call() { + return resource; + } + }; + + Func1> observableFactory = new Func1>() { + @Override + public Observable call(Resource resource) { + return Observable.from(resource.getTextFromWeb().split(" ")); + } + }; + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + Observable observable = Observable.create(using( + resourceFactory, observableFactory)); + observable.subscribe(observer); + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext("Hello"); + inOrder.verify(observer, times(1)).onNext("world!"); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + + // The resouce should be closed + verify(resource, times(1)).unsubscribe(); + } + + @Test + public void testUsingWithSubscribingTwice() { + // When subscribe is called, a new resource should be created. + Func0 resourceFactory = new Func0() { + @Override + public Resource call() { + return new Resource() { + + boolean first = true; + + @Override + public String getTextFromWeb() { + if (first) { + first = false; + return "Hello world!"; + } + return "Nothing"; + } + + @Override + public void unsubscribe() { + } + + }; + } + }; + + Func1> observableFactory = new Func1>() { + @Override + public Observable call(Resource resource) { + return Observable.from(resource.getTextFromWeb().split(" ")); + } + }; + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + Observable observable = Observable.create(using( + resourceFactory, observableFactory)); + observable.subscribe(observer); + observable.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + inOrder.verify(observer, times(1)).onNext("Hello"); + inOrder.verify(observer, times(1)).onNext("world!"); + inOrder.verify(observer, times(1)).onCompleted(); + + inOrder.verify(observer, times(1)).onNext("Hello"); + inOrder.verify(observer, times(1)).onNext("world!"); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test(expected = TestException.class) + public void testUsingWithResourceFactoryError() { + Func0 resourceFactory = new Func0() { + @Override + public Subscription call() { + throw new TestException(); + } + }; + + Func1> observableFactory = new Func1>() { + @Override + public Observable call(Subscription subscription) { + return Observable.empty(); + } + }; + + Observable.create(using(resourceFactory, observableFactory)) + .toBlockingObservable().last(); + } + + @Test + public void testUsingWithObservableFactoryError() { + final Action0 unsubscribe = mock(Action0.class); + Func0 resourceFactory = new Func0() { + @Override + public Subscription call() { + return Subscriptions.create(unsubscribe); + } + }; + + Func1> observableFactory = new Func1>() { + @Override + public Observable call(Subscription subscription) { + throw new TestException(); + } + }; + + try { + Observable.create(using(resourceFactory, observableFactory)) + .toBlockingObservable().last(); + fail("Should throw a TestException when the observableFactory throws it"); + } catch (TestException e) { + // Make sure that unsubscribe is called so that users can close + // the resource if some error happens. + verify(unsubscribe, times(1)).call(); + } + } + + @Test + public void testUsingWithObservableFactoryErrorInOnSubscribe() { + final Action0 unsubscribe = mock(Action0.class); + Func0 resourceFactory = new Func0() { + @Override + public Subscription call() { + return Subscriptions.create(unsubscribe); + } + }; + + Func1> observableFactory = new Func1>() { + @Override + public Observable call(Subscription subscription) { + return Observable.create(new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer t1) { + throw new TestException(); + } + }); + } + }; + + try { + Observable.create(using(resourceFactory, observableFactory)) + .toBlockingObservable().last(); + fail("Should throw a TestException when the observableFactory throws it"); + } catch (TestException e) { + // Make sure that unsubscribe is called so that users can close + // the resource if some error happens. + verify(unsubscribe, times(1)).call(); + } + } +} From db04d566dbb6911bfbcf13d69b4469964ea50847 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 11 Nov 2013 19:40:36 +0800 Subject: [PATCH 255/333] Use the '+1/-1' way to implement the min and max operators --- rxjava-core/src/main/java/rx/Observable.java | 23 +- .../main/java/rx/operators/OperationMax.java | 106 ------ ...OperationMin.java => OperationMinMax.java} | 77 +++- .../java/rx/operators/OperationMaxTest.java | 182 --------- .../rx/operators/OperationMinMaxTest.java | 359 ++++++++++++++++++ .../java/rx/operators/OperationMinTest.java | 182 --------- 6 files changed, 431 insertions(+), 498 deletions(-) delete mode 100644 rxjava-core/src/main/java/rx/operators/OperationMax.java rename rxjava-core/src/main/java/rx/operators/{OperationMin.java => OperationMinMax.java} (59%) delete mode 100644 rxjava-core/src/test/java/rx/operators/OperationMaxTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationMinMaxTest.java delete mode 100644 rxjava-core/src/test/java/rx/operators/OperationMinTest.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 3fbd7e6d37..08c0b47192 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -53,10 +53,9 @@ import rx.operators.OperationLast; import rx.operators.OperationMap; import rx.operators.OperationMaterialize; -import rx.operators.OperationMax; import rx.operators.OperationMerge; import rx.operators.OperationMergeDelayError; -import rx.operators.OperationMin; +import rx.operators.OperationMinMax; import rx.operators.OperationMulticast; import rx.operators.OperationObserveOn; import rx.operators.OperationOnErrorResumeNextViaFunction; @@ -3636,6 +3635,7 @@ public static Observable averageDoubles(Observable source) { /** * Returns the minimum element in an observable sequence. + * If there are more than one minimum elements, returns the last one. * For an empty source, it causes an {@link IllegalArgumentException}. * * @param source @@ -3646,11 +3646,12 @@ public static Observable averageDoubles(Observable source) { * @see MSDN: Observable.Min */ public static > Observable min(Observable source) { - return OperationMin.min(source); + return OperationMinMax.min(source); } /** * Returns the minimum element in an observable sequence according to the specified comparator. + * If there are more than one minimum elements, returns the last one. * For an empty source, it causes an {@link IllegalArgumentException}. * * @param comparator @@ -3661,7 +3662,7 @@ public static > Observable min(Observable source) * @see MSDN: Observable.Min */ public Observable min(Comparator comparator) { - return OperationMin.min(this, comparator); + return OperationMinMax.min(this, comparator); } /** @@ -3674,7 +3675,7 @@ public Observable min(Comparator comparator) { * @see MSDN: Observable.MinBy */ public > Observable> minBy(Func1 selector) { - return OperationMin.minBy(this, selector); + return OperationMinMax.minBy(this, selector); } /** @@ -3689,11 +3690,12 @@ public > Observable> minBy(Func1 selector) * @see MSDN: Observable.MinBy */ public Observable> minBy(Func1 selector, Comparator comparator) { - return OperationMin.minBy(this, selector, comparator); + return OperationMinMax.minBy(this, selector, comparator); } /** * Returns the maximum element in an observable sequence. + * If there are more than one maximum elements, returns the last one. * For an empty source, it causes an {@link IllegalArgumentException}. * * @param source @@ -3704,11 +3706,12 @@ public Observable> minBy(Func1 selector, Comparator compara * @see MSDN: Observable.Max */ public static > Observable max(Observable source) { - return OperationMax.max(source); + return OperationMinMax.max(source); } /** * Returns the maximum element in an observable sequence according to the specified comparator. + * If there are more than one maximum elements, returns the last one. * For an empty source, it causes an {@link IllegalArgumentException}. * * @param comparator @@ -3719,7 +3722,7 @@ public static > Observable max(Observable source) * @see MSDN: Observable.Max */ public Observable max(Comparator comparator) { - return OperationMax.max(this, comparator); + return OperationMinMax.max(this, comparator); } /** @@ -3732,7 +3735,7 @@ public Observable max(Comparator comparator) { * @see MSDN: Observable.MaxBy */ public > Observable> maxBy(Func1 selector) { - return OperationMax.maxBy(this, selector); + return OperationMinMax.maxBy(this, selector); } /** @@ -3747,7 +3750,7 @@ public > Observable> maxBy(Func1 selector) * @see MSDN: Observable.MaxBy */ public Observable> maxBy(Func1 selector, Comparator comparator) { - return OperationMax.maxBy(this, selector, comparator); + return OperationMinMax.maxBy(this, selector, comparator); } /** diff --git a/rxjava-core/src/main/java/rx/operators/OperationMax.java b/rxjava-core/src/main/java/rx/operators/OperationMax.java deleted file mode 100644 index bb22f8a33c..0000000000 --- a/rxjava-core/src/main/java/rx/operators/OperationMax.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright 2013 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.operators; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -import rx.Observable; -import rx.util.functions.Func1; -import rx.util.functions.Func2; - -/** - * Returns the maximum element in an observable sequence. - */ -public class OperationMax { - - public static > Observable max( - Observable source) { - return source.reduce(new Func2() { - @Override - public T call(T acc, T value) { - if (acc.compareTo(value) > 0) { - return acc; - } - return value; - } - }); - } - - public static Observable max(Observable source, - final Comparator comparator) { - return source.reduce(new Func2() { - @Override - public T call(T acc, T value) { - if (comparator.compare(acc, value) > 0) { - return acc; - } - return value; - } - }); - } - - public static > Observable> maxBy( - Observable source, final Func1 selector) { - return source.reduce(new ArrayList(), - new Func2, T, List>() { - - @Override - public List call(List acc, T value) { - if (acc.isEmpty()) { - acc.add(value); - } else { - int flag = selector.call(acc.get(0)).compareTo( - selector.call(value)); - if (flag < 0) { - acc.clear(); - acc.add(value); - } else if (flag == 0) { - acc.add(value); - } - } - return acc; - } - }); - } - - public static Observable> maxBy(Observable source, - final Func1 selector, final Comparator comparator) { - return source.reduce(new ArrayList(), - new Func2, T, List>() { - - @Override - public List call(List acc, T value) { - if (acc.isEmpty()) { - acc.add(value); - } else { - int flag = comparator.compare( - selector.call(acc.get(0)), - selector.call(value)); - if (flag < 0) { - acc.clear(); - acc.add(value); - } else if (flag == 0) { - acc.add(value); - } - } - return acc; - } - }); - } - -} diff --git a/rxjava-core/src/main/java/rx/operators/OperationMin.java b/rxjava-core/src/main/java/rx/operators/OperationMinMax.java similarity index 59% rename from rxjava-core/src/main/java/rx/operators/OperationMin.java rename to rxjava-core/src/main/java/rx/operators/OperationMinMax.java index 13b1188f03..ba587751e8 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMin.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMinMax.java @@ -26,14 +26,54 @@ /** * Returns the minimum element in an observable sequence. */ -public class OperationMin { +public class OperationMinMax { public static > Observable min( Observable source) { + return minMax(source, -1L); + } + + public static Observable min(Observable source, + final Comparator comparator) { + return minMax(source, comparator, -1L); + } + + public static > Observable> minBy( + Observable source, final Func1 selector) { + return minMaxBy(source, selector, -1L); + } + + public static Observable> minBy(Observable source, + final Func1 selector, final Comparator comparator) { + return minMaxBy(source, selector, comparator, -1L); + } + + public static > Observable max( + Observable source) { + return minMax(source, 1L); + } + + public static Observable max(Observable source, + final Comparator comparator) { + return minMax(source, comparator, 1L); + } + + public static > Observable> maxBy( + Observable source, final Func1 selector) { + return minMaxBy(source, selector, 1L); + } + + public static Observable> maxBy(Observable source, + final Func1 selector, final Comparator comparator) { + return minMaxBy(source, selector, comparator, 1L); + } + + private static > Observable minMax( + Observable source, final long flag) { return source.reduce(new Func2() { @Override public T call(T acc, T value) { - if (acc.compareTo(value) < 0) { + if (flag * acc.compareTo(value) > 0) { return acc; } return value; @@ -41,12 +81,12 @@ public T call(T acc, T value) { }); } - public static Observable min(Observable source, - final Comparator comparator) { + private static Observable minMax(Observable source, + final Comparator comparator, final long flag) { return source.reduce(new Func2() { @Override public T call(T acc, T value) { - if (comparator.compare(acc, value) < 0) { + if (flag * comparator.compare(acc, value) > 0) { return acc; } return value; @@ -54,8 +94,8 @@ public T call(T acc, T value) { }); } - public static > Observable> minBy( - Observable source, final Func1 selector) { + private static > Observable> minMaxBy( + Observable source, final Func1 selector, final long flag) { return source.reduce(new ArrayList(), new Func2, T, List>() { @@ -64,12 +104,12 @@ public List call(List acc, T value) { if (acc.isEmpty()) { acc.add(value); } else { - int flag = selector.call(acc.get(0)).compareTo( - selector.call(value)); - if (flag > 0) { - acc.clear(); + int compareResult = selector.call(acc.get(0)) + .compareTo(selector.call(value)); + if (compareResult == 0) { acc.add(value); - } else if (flag == 0) { + } else if (flag * compareResult < 0) { + acc.clear(); acc.add(value); } } @@ -78,8 +118,9 @@ public List call(List acc, T value) { }); } - public static Observable> minBy(Observable source, - final Func1 selector, final Comparator comparator) { + private static Observable> minMaxBy(Observable source, + final Func1 selector, final Comparator comparator, + final long flag) { return source.reduce(new ArrayList(), new Func2, T, List>() { @@ -88,13 +129,13 @@ public List call(List acc, T value) { if (acc.isEmpty()) { acc.add(value); } else { - int flag = comparator.compare( + int compareResult = comparator.compare( selector.call(acc.get(0)), selector.call(value)); - if (flag > 0) { - acc.clear(); + if (compareResult == 0) { acc.add(value); - } else if (flag == 0) { + } else if (flag * compareResult < 0) { + acc.clear(); acc.add(value); } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationMaxTest.java b/rxjava-core/src/test/java/rx/operators/OperationMaxTest.java deleted file mode 100644 index 03c3ae5abd..0000000000 --- a/rxjava-core/src/test/java/rx/operators/OperationMaxTest.java +++ /dev/null @@ -1,182 +0,0 @@ -package rx.operators; - -import static org.mockito.Matchers.isA; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - -import org.junit.Test; -import org.mockito.InOrder; - -import rx.Observable; -import rx.Observer; -import rx.util.functions.Func1; - -public class OperationMaxTest { - @Test - public void testMax() { - Observable observable = OperationMax.max(Observable.from(2, 3, - 1, 4)); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(4); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxWithEmpty() { - Observable observable = OperationMax.max(Observable - . empty()); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError( - isA(IllegalArgumentException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxWithComparator() { - Observable observable = OperationMax.max( - Observable.from(2, 3, 1, 4), new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(1); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxWithComparatorAndEmpty() { - Observable observable = OperationMax.max( - Observable. empty(), new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError( - isA(IllegalArgumentException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxBy() { - Observable> observable = OperationMax.maxBy( - Observable.from("1", "2", "3", "4", "5", "6"), - new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(Arrays.asList("1", "3", "5")); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxByWithEmpty() { - Observable> observable = OperationMax.maxBy( - Observable. empty(), new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(new ArrayList()); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxByWithComparator() { - Observable> observable = OperationMax.maxBy( - Observable.from("1", "2", "3", "4", "5", "6"), - new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }, new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(Arrays.asList("2", "4", "6")); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMaxByWithComparatorAndEmpty() { - Observable> observable = OperationMax.maxBy( - Observable. empty(), new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }, new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(new ArrayList()); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } -} diff --git a/rxjava-core/src/test/java/rx/operators/OperationMinMaxTest.java b/rxjava-core/src/test/java/rx/operators/OperationMinMaxTest.java new file mode 100644 index 0000000000..f2deff9e57 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationMinMaxTest.java @@ -0,0 +1,359 @@ +/** + * Copyright 2013 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.operators; + +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static rx.operators.OperationMinMax.max; +import static rx.operators.OperationMinMax.maxBy; +import static rx.operators.OperationMinMax.min; +import static rx.operators.OperationMinMax.minBy; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import org.junit.Test; +import org.mockito.InOrder; + +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func1; + +public class OperationMinMaxTest { + @Test + public void testMin() { + Observable observable = min(Observable.from(2, 3, 1, 4)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(1); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinWithEmpty() { + Observable observable = min(Observable. empty()); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + isA(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinWithComparator() { + Observable observable = min(Observable.from(2, 3, 1, 4), + new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(4); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinWithComparatorAndEmpty() { + Observable observable = min(Observable. empty(), + new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + isA(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinBy() { + Observable> observable = minBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(Arrays.asList("2", "4", "6")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinByWithEmpty() { + Observable> observable = minBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinByWithComparator() { + Observable> observable = minBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(Arrays.asList("1", "3", "5")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMinByWithComparatorAndEmpty() { + Observable> observable = minBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMax() { + Observable observable = max(Observable.from(2, 3, 1, 4)); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(4); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxWithEmpty() { + Observable observable = max(Observable. empty()); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + isA(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxWithComparator() { + Observable observable = max(Observable.from(2, 3, 1, 4), + new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(1); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxWithComparatorAndEmpty() { + Observable observable = max(Observable. empty(), + new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer observer = (Observer) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onError( + isA(IllegalArgumentException.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxBy() { + Observable> observable = maxBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(Arrays.asList("1", "3", "5")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxByWithEmpty() { + Observable> observable = maxBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxByWithComparator() { + Observable> observable = maxBy( + Observable.from("1", "2", "3", "4", "5", "6"), + new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(Arrays.asList("2", "4", "6")); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testMaxByWithComparatorAndEmpty() { + Observable> observable = maxBy( + Observable. empty(), new Func1() { + @Override + public Integer call(String t1) { + return Integer.parseInt(t1) % 2; + } + }, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); + + @SuppressWarnings("unchecked") + Observer> observer = (Observer>) mock(Observer.class); + + observable.subscribe(observer); + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(new ArrayList()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationMinTest.java b/rxjava-core/src/test/java/rx/operators/OperationMinTest.java deleted file mode 100644 index 971baf1ddb..0000000000 --- a/rxjava-core/src/test/java/rx/operators/OperationMinTest.java +++ /dev/null @@ -1,182 +0,0 @@ -package rx.operators; - -import static org.mockito.Matchers.isA; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - -import org.junit.Test; -import org.mockito.InOrder; - -import rx.Observable; -import rx.Observer; -import rx.util.functions.Func1; - -public class OperationMinTest { - @Test - public void testMin() { - Observable observable = OperationMin.min(Observable.from(2, 3, - 1, 4)); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(1); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinWithEmpty() { - Observable observable = OperationMin.min(Observable - . empty()); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError( - isA(IllegalArgumentException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinWithComparator() { - Observable observable = OperationMin.min( - Observable.from(2, 3, 1, 4), new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(4); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinWithComparatorAndEmpty() { - Observable observable = OperationMin.min( - Observable. empty(), new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer observer = (Observer) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onError( - isA(IllegalArgumentException.class)); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinBy() { - Observable> observable = OperationMin.minBy( - Observable.from("1", "2", "3", "4", "5", "6"), - new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(Arrays.asList("2", "4", "6")); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinByWithEmpty() { - Observable> observable = OperationMin.minBy( - Observable. empty(), new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(new ArrayList()); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinByWithComparator() { - Observable> observable = OperationMin.minBy( - Observable.from("1", "2", "3", "4", "5", "6"), - new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }, new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(Arrays.asList("1", "3", "5")); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testMinByWithComparatorAndEmpty() { - Observable> observable = OperationMin.minBy( - Observable. empty(), new Func1() { - @Override - public Integer call(String t1) { - return Integer.parseInt(t1) % 2; - } - }, new Comparator() { - @Override - public int compare(Integer o1, Integer o2) { - return o2 - o1; - } - }); - - @SuppressWarnings("unchecked") - Observer> observer = (Observer>) mock(Observer.class); - - observable.subscribe(observer); - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(new ArrayList()); - inOrder.verify(observer, times(1)).onCompleted(); - inOrder.verifyNoMoreInteractions(); - } -} From 36875309b2c4e6276964e013819ba513e23eea24 Mon Sep 17 00:00:00 2001 From: Scott Fleckenstein Date: Mon, 11 Nov 2013 15:01:09 -0800 Subject: [PATCH 256/333] Wraps DoOnEach in a SafeObserver This commit leverages the SafeObserver facility to get the desired behavior in the face of exceptions. Specifically, if any of the operations performed within the doOnEach handler raises an exception, that exception will propagate through the observable chain. --- .../java/rx/operators/OperationDoOnEach.java | 16 +++++++------- .../rx/operators/OperationDoOnEachTest.java | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationDoOnEach.java b/rxjava-core/src/main/java/rx/operators/OperationDoOnEach.java index acd841fc2f..1b0aafb578 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDoOnEach.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDoOnEach.java @@ -24,23 +24,24 @@ * Converts the elements of an observable sequence to the specified type. */ public class OperationDoOnEach { - public static OnSubscribeFunc doOnEach(Observable source, Observer observer) { - return new DoOnEachObservable(source, observer); + public static OnSubscribeFunc doOnEach(Observable sequence, Observer observer) { + return new DoOnEachObservable(sequence, observer); } private static class DoOnEachObservable implements OnSubscribeFunc { - private final Observable source; + private final Observable sequence; private final Observer doOnEachObserver; - public DoOnEachObservable(Observable source, Observer doOnEachObserver) { - this.source = source; + public DoOnEachObservable(Observable sequence, Observer doOnEachObserver) { + this.sequence = sequence; this.doOnEachObserver = doOnEachObserver; } @Override public Subscription onSubscribe(final Observer observer) { - return source.subscribe(new Observer() { + final SafeObservableSubscription subscription = new SafeObservableSubscription(); + return subscription.wrap(sequence.subscribe(new SafeObserver(subscription, new Observer() { @Override public void onCompleted() { doOnEachObserver.onCompleted(); @@ -58,8 +59,7 @@ public void onNext(T value) { doOnEachObserver.onNext(value); observer.onNext(value); } - - }); + }))); } } diff --git a/rxjava-core/src/test/java/rx/operators/OperationDoOnEachTest.java b/rxjava-core/src/test/java/rx/operators/OperationDoOnEachTest.java index 4bf017761c..6c1407ebea 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationDoOnEachTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationDoOnEachTest.java @@ -36,6 +36,7 @@ import rx.concurrency.Schedulers; import rx.util.functions.Func1; import rx.util.functions.Func2; +import rx.util.functions.Action1; public class OperationDoOnEachTest { @@ -104,5 +105,25 @@ public String call(String s) { verify(sideEffectObserver, times(1)).onError(any(Throwable.class)); } + @Test + public void testDoOnEachWithErrorInCallback() { + Observable base = Observable.from("one", "two", "fail", "three"); + Observable doOnEach = base.doOnEach(new Action1() { + @Override + public void call(String s) { + if ("fail".equals(s)) { + throw new RuntimeException("Forced Failure"); + } + } + }); + + doOnEach.subscribe(subscribedObserver); + verify(subscribedObserver, times(1)).onNext("one"); + verify(subscribedObserver, times(1)).onNext("two"); + verify(subscribedObserver, never()).onNext("three"); + verify(subscribedObserver, never()).onCompleted(); + verify(subscribedObserver, times(1)).onError(any(Throwable.class)); + + } } From 2d954485a9bc5ed60859d481ece1617debecc7d7 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Tue, 12 Nov 2013 23:33:12 +0000 Subject: [PATCH 257/333] [Gradle Release Plugin] - pre tag commit: '0.14.10'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 6867e2eeab..b57451d808 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.10-SNAPSHOT +version=0.14.10 From 5112a7073c01025ad8029003d9cd46c41f0dc3c2 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Tue, 12 Nov 2013 23:33:15 +0000 Subject: [PATCH 258/333] [Gradle Release Plugin] - new version commit: '0.14.11-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b57451d808..5f289abc41 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.10 +version=0.14.11-SNAPSHOT From ac650b55b17972dedc3ee89a25e8628541392264 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 12 Nov 2013 15:42:58 -0800 Subject: [PATCH 259/333] Version 0.14.10 --- CHANGES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 63e7409f14..c481155149 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,13 @@ # RxJava Releases # +### Version 0.14.10 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.10%22)) ### + +* [Pull 481](https://github.com/Netflix/RxJava/pull/481) Operator: Using +* [Pull 480](https://github.com/Netflix/RxJava/pull/480) BugFix: Emit an IllegalArgumentException instead of ArithmeticException if the observable is empty +* [Pull 479](https://github.com/Netflix/RxJava/pull/479) Operator: DoOnEach +* [Pull 478](https://github.com/Netflix/RxJava/pull/478) Operator: Min, MinBy, Max, MaxBy +* [Pull 463](https://github.com/Netflix/RxJava/pull/463) Add Timeout Overloads + ### Version 0.14.9 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.9%22)) ### * [Pull 477](https://github.com/Netflix/RxJava/pull/477) BugFix: CompositeSubscription From 470bb89ba0ec9e0ccd1f4113804dfd9e8998ea41 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 13 Nov 2013 15:34:52 -0800 Subject: [PATCH 260/333] DoOn Tweaks - do not have 2 method overloads with similar method signatures, dynamic languages can not negotiate method dispatch using function arity - add doOnCompleted and doOnError methods instead of different doOnEach overloads --- rxjava-core/src/main/java/rx/Observable.java | 45 ++++++++--- .../src/test/java/rx/ObservableDoOnTest.java | 80 +++++++++++++++++++ 2 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 rxjava-core/src/test/java/rx/ObservableDoOnTest.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 960de28ad6..368a307191 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -5023,20 +5023,47 @@ public void onNext(T args) { return create(OperationDoOnEach.doOnEach(this, observer)); } + + /** + * Invokes an action if onError is emitted from the observable sequence. + * + * @param onError + * The action to invoke if onError is invoked. + * + * @return + * The source sequence with the side-effecting behavior applied. + * @see MSDN: Observable.Do + */ + public Observable doOnError(final Action1 onError) { + Observer observer = new Observer() { + @Override + public void onCompleted() {} + + @Override + public void onError(Throwable e) { + onError.call(e); + } + @Override + public void onNext(T args) { } + + }; + + + return create(OperationDoOnEach.doOnEach(this, observer)); + } + /** - * Invokes an action for each element in the observable sequence. + * Invokes an action when onCompleted is emitted from the observable sequence. * - * @param onNext - * The action to invoke for each element in the source sequence. * @param onCompleted - * The action to invoke when the source sequence is completed. + * The action to invoke when onCompleted is emitted. * * @return * The source sequence with the side-effecting behavior applied. - * @see MSDN: Observable.Do + * @see MSDN: Observable.Do */ - public Observable doOnEach(final Action1 onNext, final Action0 onCompleted) { + public Observable doOnCompleted(final Action0 onCompleted) { Observer observer = new Observer() { @Override public void onCompleted() { @@ -5044,12 +5071,10 @@ public void onCompleted() { } @Override - public void onError(Throwable e) {} + public void onError(Throwable e) { } @Override - public void onNext(T args) { - onNext.call(args); - } + public void onNext(T args) { } }; diff --git a/rxjava-core/src/test/java/rx/ObservableDoOnTest.java b/rxjava-core/src/test/java/rx/ObservableDoOnTest.java new file mode 100644 index 0000000000..03aa8f53a4 --- /dev/null +++ b/rxjava-core/src/test/java/rx/ObservableDoOnTest.java @@ -0,0 +1,80 @@ +/** + * Copyright 2013 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.*; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.Test; + +import rx.util.functions.Action0; +import rx.util.functions.Action1; + +public class ObservableDoOnTest { + + @Test + public void testDoOnEach() { + final AtomicReference r = new AtomicReference(); + String output = Observable.from("one").doOnEach(new Action1() { + + @Override + public void call(String v) { + r.set(v); + } + }).toBlockingObservable().single(); + + assertEquals("one", output); + assertEquals("one", r.get()); + } + + @Test + public void testDoOnError() { + final AtomicReference r = new AtomicReference(); + Throwable t = null; + try { + Observable. error(new RuntimeException("an error")).doOnError(new Action1() { + + @Override + public void call(Throwable v) { + r.set(v); + } + }).toBlockingObservable().single(); + fail("expected exception, not a return value"); + } catch (Throwable e) { + t = e; + } + + assertNotNull(t); + assertEquals(t, r.get()); + } + + @Test + public void testDoOnCompleted() { + final AtomicBoolean r = new AtomicBoolean(); + String output = Observable.from("one").doOnCompleted(new Action0() { + + @Override + public void call() { + r.set(true); + } + }).toBlockingObservable().single(); + + assertEquals("one", output); + assertTrue(r.get()); + } +} From 95717fc3c479456523e8596ec69dda35532938e7 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Wed, 13 Nov 2013 15:40:39 -0800 Subject: [PATCH 261/333] Remove a doOnEach overload See https://github.com/Netflix/RxJava/pull/483 for more info --- .../src/main/scala/rx/lang/scala/Observable.scala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 8dd5d1b9b2..29111f4cf7 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1817,10 +1817,6 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) Observable[T](asJava.doOnEach(onNext)) } - def doOnEach(onNext: T => Unit, onComplete: () => Unit): Observable[T] = { - Observable[T](asJava.doOnEach(onNext, onComplete)) - } - def doOnEach(onNext: T => Unit, onError: Throwable => Unit): Observable[T] = { Observable[T](asJava.doOnEach(onNext, onError)) } From 779272529b7e4e0d077d96fac333859fe7ff32df Mon Sep 17 00:00:00 2001 From: "David M. Gross" Date: Fri, 15 Nov 2013 18:49:42 -0800 Subject: [PATCH 262/333] adding marble diagrams to javadocs; standardizing formatting & terminology --- rxjava-core/src/main/java/rx/Observable.java | 4995 +++++++++--------- 1 file changed, 2568 insertions(+), 2427 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 368a307191..b9ae791382 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -1,17 +1,17 @@ /** * Copyright 2013 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 + * 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. + * 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; @@ -123,17 +123,18 @@ /** * The Observable interface that implements the Reactive Pattern. *

    - * This interface provides overloaded methods for subscribing as well as delegate methods to the - * various operators. + * This interface provides overloaded methods for subscribing as well as + * delegate methods to the various operators. *

    - * The documentation for this interface makes use of marble diagrams. The following legend explains - * these diagrams: + * The documentation for this interface makes use of marble diagrams. The + * following legend explains these diagrams: *

    * *

    - * For more information see the RxJava Wiki + * For more information see the + * RxJava Wiki * - * @param + * @param the type of the item emitted by the Observable */ public class Observable { @@ -145,7 +146,8 @@ public class Observable { private final OnSubscribeFunc onSubscribe; /** - * Function interface for work to be performed when an {@link Observable} is subscribed to via {@link Observable#subscribe(Observer)} + * Function interface for work to be performed when an {@link Observable} + * is subscribed to via {@link Observable#subscribe(Observer)} * * @param */ @@ -158,11 +160,12 @@ public static interface OnSubscribeFunc extends Function { /** * Observable with Function to execute when subscribed to. *

    - * NOTE: Use {@link #create(OnSubscribeFunc)} to create an Observable instead of this constructor unless you - * specifically have a need for inheritance. + * NOTE: Use {@link #create(OnSubscribeFunc)} to create an Observable + * instead of this constructor unless you specifically have a need for + * inheritance. * - * @param onSubscribe - * {@link OnSubscribeFunc} to be executed when {@link #subscribe(Observer)} is called. + * @param onSubscribe {@link OnSubscribeFunc} to be executed when + * {@link #subscribe(Observer)} is called */ protected Observable(OnSubscribeFunc onSubscribe) { this.onSubscribe = onSubscribe; @@ -171,31 +174,35 @@ protected Observable(OnSubscribeFunc onSubscribe) { private final static RxJavaObservableExecutionHook hook = RxJavaPlugins.getInstance().getObservableExecutionHook(); /** - * An {@link Observer} must call an Observable's {@code subscribe} method in order to - * receive items and notifications from the Observable. + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. *

    * A typical implementation of {@code subscribe} does the following: - *

    - * It stores a reference to the Observer in a collection object, such as a {@code List} object. - *

    - * It returns a reference to the {@link Subscription} interface. This enables Observers to - * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's {@link Observer#onCompleted onCompleted} method. - *

    - * An Observable<T> instance is responsible for accepting all subscriptions - * and notifying all Observers. Unless the documentation for a particular - * Observable<T> implementation indicates otherwise, Observers should make no - * assumptions about the order in which multiple Observers will receive their notifications. + *

      + *
    1. It stores a reference to the Observer in a collection object, such as + * a {@code List} object.
    2. + *
    3. It returns a reference to the {@link Subscription} interface. This + * enables Observers to unsubscribe, that is, to stop receiving items + * and notifications before the Observable stops sending them, which + * also invokes the Observer's {@link Observer#onCompleted onCompleted} + * method.
    4. + *

    + * An Observable<T> instance is responsible for accepting + * all subscriptions and notifying all Observers. Unless the documentation + * for a particular Observable<T> implementation + * indicates otherwise, Observers should make no assumptions about the order + * in which multiple Observers will receive their notifications. *

    * For more information see the * RxJava Wiki * - * @param observer - * the observer - * @return a {@link Subscription} reference with which the {@link Observer} can stop receiving items - * before the Observable has finished sending them - * @throws IllegalArgumentException - * if the {@link Observer} provided as the argument to {@code subscribe()} is {@code null} + * @param observer the Observer + * @return a {@link Subscription} reference with which the {@link Observer} + * can stop receiving items before the Observable has finished + * sending them + * @throws IllegalArgumentException if the {@link Observer} provided as the + * argument to {@code subscribe()} is + * {@code null} */ public Subscription subscribe(Observer observer) { // allow the hook to intercept and/or decorate @@ -248,41 +255,47 @@ public Subscription subscribe(Observer observer) { } /** - * An {@link Observer} must call an Observable's {@code subscribe} method in order to - * receive items and notifications from the Observable. + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. *

    * A typical implementation of {@code subscribe} does the following: - *

    - * It stores a reference to the Observer in a collection object, such as a {@code List} object. - *

    - * It returns a reference to the {@link Subscription} interface. This enables Observers to - * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's {@link Observer#onCompleted onCompleted} method. - *

    - * An {@code Observable} instance is responsible for accepting all subscriptions - * and notifying all Observers. Unless the documentation for a particular {@code Observable} implementation indicates otherwise, Observers should make no - * assumptions about the order in which multiple Observers will receive their notifications. + *

      + *
    1. It stores a reference to the Observer in a collection object, such as + * a {@code List} object.
    2. + *
    3. It returns a reference to the {@link Subscription} interface. This + * enables Observers to unsubscribe, that is, to stop receiving items + * and notifications before the Observable stops sending them, which + * also invokes the Observer's {@link Observer#onCompleted onCompleted} + * method.
    4. + *

    + * An {@code Observable} instance is responsible for accepting all + * subscriptions and notifying all Observers. Unless the documentation for a + * particular {@code Observable} implementation indicates otherwise, + * Observers should make no assumptions about the order in which multiple + * Observers will receive their notifications. *

    * For more information see the * RxJava Wiki * - * @param observer - * the observer - * @param scheduler - * the {@link Scheduler} on which Observers subscribe to the Observable - * @return a {@link Subscription} reference with which Observers can stop receiving items and - * notifications before the Observable has finished sending them - * @throws IllegalArgumentException - * if an argument to {@code subscribe()} is {@code null} + * @param observer the Observer + * @param scheduler the {@link Scheduler} on which Observers subscribe to + * the Observable + * @return a {@link Subscription} reference with which Observers can stop + * receiving items and notifications before the Observable has + * finished sending them + * @throws IllegalArgumentException if an argument to {@code subscribe()} + * is {@code null} */ public Subscription subscribe(Observer observer, Scheduler scheduler) { return subscribeOn(scheduler).subscribe(observer); } /** - * Used for protecting against errors being thrown from Observer implementations and ensuring onNext/onError/onCompleted contract compliance. + * Protects against errors being thrown from Observer implementations and + * ensures onNext/onError/onCompleted contract compliance. *

    - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" + * See https://github.com/Netflix/RxJava/issues/216 for a discussion on + * "Guideline 6.4: Protect calls to user code from within an operator" */ private Subscription protectivelyWrapAndSubscribe(Observer o) { SafeObservableSubscription subscription = new SafeObservableSubscription(); @@ -290,10 +303,11 @@ private Subscription protectivelyWrapAndSubscribe(Observer o) { } /** - * An {@link Observer} must call an Observable's {@code subscribe} method in order to - * receive items and notifications from the Observable. + * An {@link Observer} must call an Observable's {@code subscribe} method + * in order to receive items and notifications from the Observable. * * @param onNext + * @return */ public Subscription subscribe(final Action1 onNext) { if (onNext == null) { @@ -327,16 +341,25 @@ public void onNext(T args) { } /** - * An {@link Observer} must call an Observable's {@code subscribe} method in order to - * receive items and notifications from the Observable. + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. * * @param onNext * @param scheduler + * @return */ public Subscription subscribe(final Action1 onNext, Scheduler scheduler) { return subscribeOn(scheduler).subscribe(onNext); } + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. + * + * @param onNext + * @param onError + * @return + */ public Subscription subscribe(final Action1 onNext, final Action1 onError) { if (onNext == null) { throw new IllegalArgumentException("onNext can not be null"); @@ -372,24 +395,26 @@ public void onNext(T args) { } /** - * An {@link Observer} must call an Observable's {@code subscribe} method in order to - * receive items and notifications from the Observable. + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. * * @param onNext * @param onError * @param scheduler + * @return */ public Subscription subscribe(final Action1 onNext, final Action1 onError, Scheduler scheduler) { return subscribeOn(scheduler).subscribe(onNext, onError); } /** - * An {@link Observer} must call an Observable's {@code subscribe} method in order to - * receive items and notifications from the Observable. + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. * * @param onNext * @param onError * @param onComplete + * @return */ public Subscription subscribe(final Action1 onNext, final Action1 onError, final Action0 onComplete) { if (onNext == null) { @@ -429,36 +454,37 @@ public void onNext(T args) { } /** - * An {@link Observer} must call an Observable's {@code subscribe} method in order to - * receive items and notifications from the Observable. + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. * * @param onNext * @param onError * @param onComplete * @param scheduler + * @return */ public Subscription subscribe(final Action1 onNext, final Action1 onError, final Action0 onComplete, Scheduler scheduler) { return subscribeOn(scheduler).subscribe(onNext, onError, onComplete); } /** - * Returns a {@link ConnectableObservable} that upon connection causes the source Observable to - * push results into the specified subject. + * Returns a {@link ConnectableObservable} that upon connection causes the + * source Observable to push results into the specified subject. * - * @param subject - * the {@link Subject} for the {@link ConnectableObservable} to push source items - * into - * @param - * result type - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * push results into the specified {@link Subject} + * @param subject the {@link Subject} for the {@link ConnectableObservable} + * to push source items into + * @param result type + * @return a {@link ConnectableObservable} that upon connection causes the + * source Observable to push results into the specified + * {@link Subject} */ public ConnectableObservable multicast(Subject subject) { return OperationMulticast.multicast(this, subject); } /** - * Allow the {@link RxJavaErrorHandler} to receive the exception from onError. + * Allow the {@link RxJavaErrorHandler} to receive the exception from + * onError. * * @param e */ @@ -472,8 +498,7 @@ private void handleError(Throwable e) { * * This Observable is useful primarily for testing purposes. * - * @param - * the type of item emitted by the Observable + * @param the type of item emitted by the Observable */ private static class NeverObservable extends Observable { public NeverObservable() { @@ -489,10 +514,10 @@ public Subscription onSubscribe(Observer t1) { } /** - * an Observable that invokes {@link Observer#onError onError} when the {@link Observer} subscribes to it. + * An Observable that invokes {@link Observer#onError onError} when the + * {@link Observer} subscribes to it. * - * @param - * the type of item emitted by the Observable + * @param the type of item emitted by the Observable */ private static class ThrowObservable extends Observable { @@ -500,10 +525,10 @@ public ThrowObservable(final Throwable exception) { super(new OnSubscribeFunc() { /** - * Accepts an {@link Observer} and calls its {@link Observer#onError onError} method. + * Accepts an {@link Observer} and calls its + * {@link Observer#onError onError} method. * - * @param observer - * an {@link Observer} of this Observable + * @param observer an {@link Observer} of this Observable * @return a reference to the subscription */ @Override @@ -518,97 +543,103 @@ public Subscription onSubscribe(Observer observer) { } /** - * Creates an Observable that will execute the given function when an {@link Observer} subscribes to it. + * Creates an Observable that will execute the given function when an + * {@link Observer} subscribes to it. *

    * *

    - * Write the function you pass to create so that it behaves as an Observable: It - * should invoke the Observer's {@link Observer#onNext onNext}, {@link Observer#onError onError}, and {@link Observer#onCompleted onCompleted} methods - * appropriately. + * Write the function you pass to create so that it behaves as + * an Observable: It should invoke the Observer's + * {@link Observer#onNext onNext}, {@link Observer#onError onError}, and + * {@link Observer#onCompleted onCompleted} methods appropriately. *

    - * A well-formed Observable must invoke either the Observer's onCompleted method - * exactly once or its onError method exactly once. + * A well-formed Observable must invoke either the Observer's + * onCompleted method exactly once or its onError + * method exactly once. *

    - * See Rx Design Guidelines (PDF) - * for detailed information. + * See Rx Design + * Guidelines (PDF) for detailed information. * - * @param - * the type of the items that this Observable emits - * @param func - * a function that accepts an {@code Observer}, invokes its {@code onNext}, {@code onError}, and {@code onCompleted} methods - * as appropriate, and returns a {@link Subscription} to allow the Observer to - * canceling the subscription - * @return an Observable that, when an {@link Observer} subscribes to it, will execute the given - * function + * @param the type of the items that this Observable emits + * @param func a function that accepts an {@code Observer}, invokes its + * {@code onNext}, {@code onError}, and {@code onCompleted} + * methods as appropriate, and returns a {@link Subscription} to + * allow the Observer to cancel the subscription + * @return an Observable that, when an {@link Observer} subscribes to it, + * will execute the given function */ public static Observable create(OnSubscribeFunc func) { return new Observable(func); } /** - * Returns an Observable that emits no data to the {@link Observer} and immediately invokes - * its {@link Observer#onCompleted onCompleted} method. + * Returns an Observable that emits no data to the {@link Observer} and + * immediately invokes its {@link Observer#onCompleted onCompleted} method. *

    * * - * @param - * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that returns no data to the {@link Observer} and immediately invokes - * the {@link Observer}'s {@link Observer#onCompleted() onCompleted} method - * @see MSDN: Observable.Empty Method + * @param the type of the items (ostensibly) emitted by the Observable + * @return an Observable that returns no data to the {@link Observer} and + * immediately invokes the {@link Observer}'s + * {@link Observer#onCompleted() onCompleted} method + * @see MSDN: Observable.Empty Method */ public static Observable empty() { return from(new ArrayList()); } /** - * Returns an Observable that emits no data to the {@link Observer} and immediately invokes - * its {@link Observer#onCompleted onCompleted} method with the specified scheduler. + * Returns an Observable that emits no data to the {@link Observer} and + * immediately invokes its {@link Observer#onCompleted onCompleted} method + * with the specified scheduler. *

    - * + * * - * @param scheduler - * the scheduler to call the {@link Observer#onCompleted onCompleted} method. - * @param - * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that returns no data to the {@link Observer} and immediately invokes - * the {@link Observer}'s {@link Observer#onCompleted() onCompleted} method with - * the specified scheduler. - * @see MSDN: Observable.Empty Method (IScheduler) + * @param scheduler the scheduler to call the + {@link Observer#onCompleted onCompleted} method + * @param the type of the items (ostensibly) emitted by the Observable + * @return an Observable that returns no data to the {@link Observer} and + * immediately invokes the {@link Observer}'s + * {@link Observer#onCompleted() onCompleted} method with the + * specified scheduler + * @see MSDN: Observable.Empty Method (IScheduler) */ public static Observable empty(Scheduler scheduler) { return Observable. empty().subscribeOn(scheduler); } /** - * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it + * Returns an Observable that invokes an {@link Observer}'s + * {@link Observer#onError onError} method when the Observer subscribes to + * it. *

    * * - * @param exception - * the particular error to report - * @param - * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it - * @see MSDN: Observable.Throw Method + * @param exception the particular error to report + * @param the type of the items (ostensibly) emitted by the Observable + * @return an Observable that invokes the {@link Observer}'s + * {@link Observer#onError onError} method when the Observer + * subscribes to it + * @see MSDN: Observable.Throw Method */ public static Observable error(Throwable exception) { return new ThrowObservable(exception); } /** - * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method with the specified scheduler. + * Returns an Observable that invokes an {@link Observer}'s + * {@link Observer#onError onError} method with the specified scheduler. *

    - * + * * - * @param exception - * the particular error to report - * @param scheduler - * the scheduler to call the {@link Observer#onError onError} method. - * @param - * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} method with the specified scheduler. - * @see MSDN: Observable.Throw Method + * @param exception the particular error to report + * @param scheduler the scheduler to call the + * {@link Observer#onError onError} method + * @param the type of the items (ostensibly) emitted by the Observable + * @return an Observable that invokes the {@link Observer}'s + * {@link Observer#onError onError} method with the specified + * scheduler + * @see MSDN: Observable.Throw Method */ public static Observable error(Throwable exception, Scheduler scheduler) { return Observable. error(exception).subscribeOn(scheduler); @@ -618,16 +649,17 @@ public static Observable error(Throwable exception, Scheduler scheduler) * Converts an {@link Iterable} sequence into an Observable. *

    * + *

    + * Note: the entire iterable sequence is immediately emitted each time an + * {@link Observer} subscribes. Since this occurs before the + * {@link Subscription} is returned, it is not possible to unsubscribe from + * the sequence before it completes. * - *

    Implementation note: the entire iterable sequence will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param iterable - * the source {@link Iterable} sequence - * @param - * the type of items in the {@link Iterable} sequence and the type of items to be - * emitted by the resulting Observable - * @return an Observable that emits each item in the source {@link Iterable} sequence + * @param iterable the source {@link Iterable} sequence + * @param the type of items in the {@link Iterable} sequence and the + * type of items to be emitted by the resulting Observable + * @return an Observable that emits each item in the source {@link Iterable} + * sequence */ public static Observable from(Iterable iterable) { return create(OperationToObservableIterable.toObservableIterable(iterable)); @@ -637,35 +669,35 @@ public static Observable from(Iterable iterable) { * Converts an Array into an Observable. *

    * + *

    + * Note: the entire array is immediately emitted each time an + * {@link Observer} subscribes. Since this occurs before the + * {@link Subscription} is returned, it is not possible to unsubscribe from + * the sequence before it completes. * - *

    Implementation note: the entire iterable sequence will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param items - * the source sequence - * @param - * the type of items in the {@link Iterable} sequence and the type of items to be + * @param items the source sequence + * @param the type of items in the Array and the type of items to be * emitted by the resulting Observable - * @return an Observable that emits each item in the source {@link Iterable} sequence + * @return an Observable that emits each item in the source Array */ public static Observable from(T[] items) { return create(OperationToObservableIterable.toObservableIterable(Arrays.asList(items))); } /** - * Converts a series of items into an Observable. + * Converts an item into an Observable that emits that item. *

    * + *

    + * Note: the item is immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. * - *

    Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array + * @param t1 the item + * @param the type of the item, and the type of the item to be + * emitted by the resulting Observable + * @return an Observable that emits the item */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -677,18 +709,17 @@ public static Observable from(T t1) { * Converts a series of items into an Observable. *

    * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. * - *

    Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the + * @param t1 first item + * @param t2 second item + * @param the type of items, and the type of items to be emitted by the * resulting Observable - * @return an Observable that emits each item in the source Array + * @return an Observable that emits each item */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -700,20 +731,18 @@ public static Observable from(T t1, T t2) { * Converts a series of items into an Observable. *

    * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. * - *

    Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param the type of items, and the type of items to be emitted by the * resulting Observable - * @return an Observable that emits each item in the source Array + * @return an Observable that emits each item */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -725,22 +754,19 @@ public static Observable from(T t1, T t2, T t3) { * Converts a series of items into an Observable. *

    * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. * - *

    Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param the type of items, and the type of items to be emitted by the * resulting Observable - * @return an Observable that emits each item in the source Array + * @return an Observable that emits each item */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -752,24 +778,20 @@ public static Observable from(T t1, T t2, T t3, T t4) { * Converts a series of items into an Observable. *

    * - * - *

    Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param the type of items, and the type of items to be emitted by the * resulting Observable - * @return an Observable that emits each item in the source Array + * @return an Observable that emits each item */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -781,26 +803,21 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5) { * Converts a series of items into an Observable. *

    * - * - *

    Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param t6 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param t6 sixth item + * @param the type of items, and the type of items to be emitted by the * resulting Observable - * @return an Observable that emits each item in the source Array + * @return an Observable that emits each item */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -812,28 +829,22 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6) { * Converts a series of items into an Observable. *

    * - * - *

    Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param t6 - * item - * @param t7 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param t6 sixth item + * @param t7 seventh item + * @param the type of items, and the type of items to be emitted by the * resulting Observable - * @return an Observable that emits each item in the source Array + * @return an Observable that emits each item */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -845,30 +856,23 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { * Converts a series of items into an Observable. *

    * - * - *

    Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param t6 - * item - * @param t7 - * item - * @param t8 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param t6 sixth item + * @param t7 seventh item + * @param t8 eighth item + * @param the type of items, and the type of items to be emitted by the * resulting Observable - * @return an Observable that emits each item in the source Array + * @return an Observable that emits each item */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -880,32 +884,24 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * Converts a series of items into an Observable. *

    * - * - *

    Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param t6 - * item - * @param t7 - * item - * @param t8 - * item - * @param t9 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param t6 sixth item + * @param t7 seventh item + * @param t8 eighth item + * @param t9 ninth item + * @param the type of items, and the type of items to be emitted by the * resulting Observable - * @return an Observable that emits each item in the source Array + * @return an Observable that emits each item */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -917,32 +913,25 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * Converts a series of items into an Observable. *

    * - * - *

    Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param t1 - * item - * @param t2 - * item - * @param t3 - * item - * @param t4 - * item - * @param t5 - * item - * @param t6 - * item - * @param t7 - * item - * @param t8 - * item - * @param t10 - * item - * @param - * the type of items in the Array, and the type of items to be emitted by the + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param t6 sixth item + * @param t7 seventh item + * @param t8 eighth item + * @param t9 ninth item + * @param t10 tenth item + * @param the type of items, and the type of items to be emitted by the * resulting Observable - * @return an Observable that emits each item in the source Array + * @return an Observable that emits each item */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -951,44 +940,43 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T } /** - * Generates an Observable that emits a sequence of integers within a specified range. + * Generates an Observable that emits a sequence of integers within a + * specified range. *

    * + *

    + * Note: the entire range is immediately emitted each time an + * {@link Observer} subscribes. Since this occurs before the + * {@link Subscription} is returned, it is not possible to unsubscribe from + * the sequence before it completes. * - *

    Implementation note: the entire range will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param start - * the value of the first integer in the sequence - * @param count - * the number of sequential integers to generate - * + * @param start the value of the first integer in the sequence + * @param count the number of sequential integers to generate * @return an Observable that emits a range of sequential integers - * - * @see Observable.Range Method (Int32, Int32) + * @see Observable.Range Method (Int32, Int32) */ public static Observable range(int start, int count) { return from(Range.createWithCount(start, count)); } /** - * Returns an Observable that calls an Observable factory to create its Observable for each - * new Observer that subscribes. That is, for each subscriber, the actuall Observable is determined - * by the factory function. + * Returns an Observable that calls an Observable factory to create its + * Observable for each new Observer that subscribes. That is, for each + * subscriber, the actuall Observable is determined by the factory function. *

    * *

    - * The defer operator allows you to defer or delay emitting items from an Observable until such - * time as an Observer subscribes to the Observable. This allows an {@link Observer} to easily - * obtain updates or a refreshed version of the sequence. + * The defer operator allows you to defer or delay emitting items from an + * Observable until such time as an Observer subscribes to the Observable. + * This allows an {@link Observer} to easily obtain updates or a refreshed + * version of the sequence. * - * @param observableFactory - * the Observable factory function to invoke for each {@link Observer} that - * subscribes to the resulting Observable - * @param - * the type of the items emitted by the Observable - * @return an Observable whose {@link Observer}s trigger an invocation of the given Observable - * factory function + * @param observableFactory the Observable factory function to invoke for + * each {@link Observer} that subscribes to the + * resulting Observable + * @param the type of the items emitted by the Observable + * @return an Observable whose {@link Observer}s trigger an invocation of + * the given Observable factory function */ public static Observable defer(Func0> observableFactory) { return create(OperationDefer.defer(observableFactory)); @@ -999,18 +987,18 @@ public static Observable defer(Func0> o *

    * *

    - * To convert any object into an Observable that emits that object, pass that object into the - * just method. + * To convert any object into an Observable that emits that object, pass + * that object into the just method. *

    - * This is similar to the {@link #from(java.lang.Object[])} method, except that - * from() will convert an {@link Iterable} object into an Observable that emits - * each of the items in the Iterable, one at a time, while the just() method - * converts an Iterable into an Observable that emits the entire Iterable as a single item. + * This is similar to the {@link #from(java.lang.Object[])} method, except + * that from() will convert an {@link Iterable} object into an + * Observable that emits each of the items in the Iterable, one at a time, + * while the just() method converts an Iterable into an + * Observable that emits the entire Iterable as a single item. * - * @param value - * the item to pass to the {@link Observer}'s {@link Observer#onNext onNext} method - * @param - * the type of that item + * @param value the item to pass to the {@link Observer}'s + * {@link Observer#onNext onNext} method + * @param the type of that item * @return an Observable that emits a single item and then completes */ public static Observable just(T value) { @@ -1021,56 +1009,55 @@ public static Observable just(T value) { } /** - * Returns an Observable that emits a single item and then completes on a specified scheduler. + * Returns an Observable that emits a single item and then completes on a + * specified scheduler. *

    * This is a scheduler version of {@link Observable#just(Object)}. * - * @param value - * the item to pass to the {@link Observer}'s {@link Observer#onNext onNext} method - * @param scheduler - * the scheduler to send the single element on - * @param - * the type of that item - * @return an Observable that emits a single item and then completes on a specified scheduler. + * @param value the item to pass to the {@link Observer}'s + * {@link Observer#onNext onNext} method + * @param the type of that item + * @param scheduler the scheduler to send the single element on + * @return an Observable that emits a single item and then completes on a + * specified scheduler */ public static Observable just(T value, Scheduler scheduler) { return just(value).observeOn(scheduler); } /** - * Flattens a sequence of Observables emitted by an Observable into one Observable, without any - * transformation. + * Flattens a sequence of Observables emitted by an Observable into one + * Observable, without any transformation. *

    * *

    - * You can combine the items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. + * You can combine the items emitted by multiple Observables so that they + * act like a single Observable, by using the {@code merge} method. * - * @param source - * an Observable that emits Observables - * @return an Observable that emits items that are the result of flattening the items emitted - * by the Observables emitted by the {@code source} Observable - * @see MSDN: Observable.Merge Method + * @param source an Observable that emits Observables + * @return an Observable that emits items that are the result of flattening + * the items emitted by the Observables emitted by the + * {@code source} Observable + * @see MSDN: Observable.Merge Method */ public static Observable merge(Observable> source) { return create(OperationMerge.merge(source)); } /** - * Flattens a series of Observables into one Observable, without any transformation. + * Flattens a series of Observables into one Observable, without any + * transformation. *

    * *

    - * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1079,22 +1066,20 @@ public static Observable merge(Observable t1, Observable * *

    - * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1103,24 +1088,21 @@ public static Observable merge(Observable t1, Observable * *

    - * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1129,26 +1111,22 @@ public static Observable merge(Observable t1, Observable * *

    - * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1157,28 +1135,23 @@ public static Observable merge(Observable t1, Observable * *

    - * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1187,30 +1160,24 @@ public static Observable merge(Observable t1, Observable * *

    - * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1219,32 +1186,25 @@ public static Observable merge(Observable t1, Observable * *

    - * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @param t8 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @param t8 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1253,34 +1213,26 @@ public static Observable merge(Observable t1, Observable * *

    - * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @param t8 - * an Observable to be merged - * @param t9 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @param t8 an Observable to be merged + * @param t9 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1289,35 +1241,33 @@ public static Observable merge(Observable t1, Observable * * - * @param observables - * an Observable of Observables - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method + * @param observables an Observable that emits Observables + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see MSDN: Observable.Concat Method */ public static Observable concat(Observable> observables) { return create(OperationConcat.concat(observables)); } /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the - * other. + * Returns an Observable that emits the items emitted by two Observables, + * one after the other. *

    * * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1326,21 +1276,19 @@ public static Observable concat(Observable t1, Observable * * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1349,22 +1297,19 @@ public static Observable concat(Observable t1, Observable * * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1373,24 +1318,20 @@ public static Observable concat(Observable t1, Observable * * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @param t5 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @param t5 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1399,26 +1340,21 @@ public static Observable concat(Observable t1, Observable * * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @param t5 - * an Observable to be concatenated - * @param t6 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @param t5 an Observable to be concatenated + * @param t6 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1427,28 +1363,22 @@ public static Observable concat(Observable t1, Observable * * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @param t5 - * an Observable to be concatenated - * @param t6 - * an Observable to be concatenated - * @param t7 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @param t5 an Observable to be concatenated + * @param t6 an Observable to be concatenated + * @param t7 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1457,30 +1387,23 @@ public static Observable concat(Observable t1, Observable * * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @param t5 - * an Observable to be concatenated - * @param t6 - * an Observable to be concatenated - * @param t7 - * an Observable to be concatenated - * @param t8 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @param t5 an Observable to be concatenated + * @param t6 an Observable to be concatenated + * @param t7 an Observable to be concatenated + * @param t8 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1489,32 +1412,24 @@ public static Observable concat(Observable t1, Observable * * - * @param t1 - * an Observable to be concatenated - * @param t2 - * an Observable to be concatenated - * @param t3 - * an Observable to be concatenated - * @param t4 - * an Observable to be concatenated - * @param t5 - * an Observable to be concatenated - * @param t6 - * an Observable to be concatenated - * @param t7 - * an Observable to be concatenated - * @param t8 - * an Observable to be concatenated - * @param t9 - * an Observable to be concatenated - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @param t5 an Observable to be concatenated + * @param t6 an Observable to be concatenated + * @param t7 an Observable to be concatenated + * @param t8 an Observable to be concatenated + * @param t9 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1523,50 +1438,54 @@ public static Observable concat(Observable t1, Observable * *

    - * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. *

    - * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. * - * @param source - * an Observable that emits Observables - * @return an Observable that emits items that are the result of flattening the items emitted by - * the Observables emitted by the {@code source} Observable - * @see MSDN: Observable.Merge Method + * @param source an Observable that emits Observables + * @return an Observable that emits items that are the result of flattening + * the items emitted by the Observables emitted by the + * {@code source} Observable + * @see MSDN: Observable.Merge Method */ public static Observable mergeDelayError(Observable> source) { return create(OperationMergeDelayError.mergeDelayError(source)); } /** - * This behaves like {@link #merge(Observable, Observable)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. + * This behaves like {@link #merge(Observable, Observable)} except that if + * any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. *

    * *

    - * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. *

    - * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1575,28 +1494,28 @@ public static Observable mergeDelayError(Observable t1, Obse } /** - * This behaves like {@link #merge(Observable, Observable, Observable)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. + * This behaves like {@link #merge(Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. *

    * *

    - * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. *

    - * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1605,30 +1524,30 @@ public static Observable mergeDelayError(Observable t1, Obse } /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. + * This behaves like + * {@link #merge(Observable, Observable, Observable, Observable)} except + * that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. *

    * *

    - * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. *

    - * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1637,32 +1556,30 @@ public static Observable mergeDelayError(Observable t1, Obse } /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. *

    * *

    - * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1671,34 +1588,31 @@ public static Observable mergeDelayError(Observable t1, Obse } /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. *

    * *

    - * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1707,36 +1621,32 @@ public static Observable mergeDelayError(Observable t1, Obse } /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. *

    * *

    - * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1745,38 +1655,33 @@ public static Observable mergeDelayError(Observable t1, Obse } /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. *

    * *

    - * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @param t8 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @param t8 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1785,40 +1690,34 @@ public static Observable mergeDelayError(Observable t1, Obse } /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables - * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. *

    * *

    - * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its - * Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items from all of the - * source Observables without being interrupted by an error notification from one of them. - * - * @param t1 - * an Observable to be merged - * @param t2 - * an Observable to be merged - * @param t3 - * an Observable to be merged - * @param t4 - * an Observable to be merged - * @param t5 - * an Observable to be merged - * @param t6 - * an Observable to be merged - * @param t7 - * an Observable to be merged - * @param t8 - * an Observable to be merged - * @param t9 - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @param t8 an Observable to be merged + * @param t9 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") // suppress because the types are checked by the method signature before using a vararg @@ -1827,31 +1726,32 @@ public static Observable mergeDelayError(Observable t1, Obse } /** - * Returns an Observable that never sends any items or notifications to an {@link Observer}. + * Returns an Observable that never sends any items or notifications to an + * {@link Observer}. *

    * *

    * This Observable is useful primarily for testing purposes. * - * @param - * the type of items (not) emitted by the Observable - * @return an Observable that never sends any items or notifications to an {@link Observer} + * @param the type of items (not) emitted by the Observable + * @return an Observable that never sends any items or notifications to an + * {@link Observer} */ public static Observable never() { return new NeverObservable(); } /** - * Given an Observable that emits Observables, creates a single Observable that - * emits the items emitted by the most recently published of those Observables. + * Given an Observable that emits Observables, creates a single Observable + * that emits the items emitted by the most recently published of those + * Observables. *

    * * - * @param sequenceOfSequences - * the source Observable that emits Observables - * @return an Observable that emits only the items emitted by the most recently published - * Observable - * @deprecated Being renamed to {@link #switchOnNext} + * @param sequenceOfSequences the source Observable that emits Observables + * @return an Observable that emits only the items emitted by the most + * recently published Observable + * @deprecated use {@link #switchOnNext} */ @Deprecated public static Observable switchDo(Observable> sequenceOfSequences) { @@ -1859,58 +1759,71 @@ public static Observable switchDo(Observable * * - * @param sequenceOfSequences - * the source Observable that emits Observables - * @return an Observable that emits only the items emitted by the most recently published - * Observable + * @param sequenceOfSequences the source Observable that emits Observables + * @return an Observable that emits only the items emitted by the most + * recently published Observable */ public static Observable switchOnNext(Observable> sequenceOfSequences) { return create(OperationSwitch.switchDo(sequenceOfSequences)); } /** - * Accepts an Observable and wraps it in another Observable that ensures that the resulting - * Observable is chronologically well-behaved. + * Accepts an Observable and wraps it in another Observable that ensures + * that the resulting Observable is chronologically well-behaved. *

    * *

    - * A well-behaved Observable does not interleave its invocations of the {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, and {@link Observer#onError onError} methods of - * its {@link Observer}s; it invokes {@code onCompleted} or {@code onError} only once; and it never invokes {@code onNext} after invoking either {@code onCompleted} or {@code onError}. - * {@code synchronize} enforces this, and the Observable it returns invokes {@code onNext} and {@code onCompleted} or {@code onError} synchronously. + * A well-behaved Observable does not interleave its invocations of the + * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, + * and {@link Observer#onError onError} methods of its {@link Observer}s; it + * invokes {@code onCompleted} or {@code onError} only once; and it never + * invokes {@code onNext} after invoking either {@code onCompleted} or + * {@code onError}. {@code synchronize} enforces this, and the Observable it + * returns invokes {@code onNext} and {@code onCompleted} or {@code onError} + * synchronously. * - * @return an Observable that is a chronologically well-behaved version of the source - * Observable, and that synchronously notifies its {@link Observer}s + * @return an Observable that is a chronologically well-behaved version of + * the source Observable, and that synchronously notifies its + * {@link Observer}s */ public Observable synchronize() { return create(OperationSynchronize.synchronize(this)); } /** - * Accepts an Observable and wraps it in another Observable that ensures that the resulting - * Observable is chronologically well-behaved. This is accomplished by acquiring a mutual-exclusion lock for the object provided as the lock parameter. + * Accepts an Observable and wraps it in another Observable that ensures + * that the resulting Observable is chronologically well-behaved. This is + * accomplished by acquiring a mutual-exclusion lock for the object + * provided as the lock parameter. *

    * *

    - * A well-behaved Observable does not interleave its invocations of the {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, and {@link Observer#onError onError} methods of - * its {@link Observer}s; it invokes {@code onCompleted} or {@code onError} only once; and it never invokes {@code onNext} after invoking either {@code onCompleted} or {@code onError}. - * {@code synchronize} enforces this, and the Observable it returns invokes {@code onNext} and {@code onCompleted} or {@code onError} synchronously. + * A well-behaved Observable does not interleave its invocations of the + * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, + * and {@link Observer#onError onError} methods of its {@link Observer}s; it + * invokes {@code onCompleted} or {@code onError} only once; and it never + * invokes {@code onNext} after invoking either {@code onCompleted} or + * {@code onError}. {@code synchronize} enforces this, and the Observable it + * returns invokes {@code onNext} and {@code onCompleted} or {@code onError} + * synchronously. * - * @param lock - * The lock object to synchronize each observer call on - * @return an Observable that is a chronologically well-behaved version of the source - * Observable, and that synchronously notifies its {@link Observer}s + * @param lock the lock object to synchronize each observer call on + * @return an Observable that is a chronologically well-behaved version of + * the source Observable, and that synchronously notifies its + * {@link Observer}s */ public Observable synchronize(Object lock) { return create(OperationSynchronize.synchronize(this, lock)); } /** - * @deprecated Replaced with instance method. + * @deprecated use {@link #synchronize()} or {@link #synchronize(Object)} */ @Deprecated public static Observable synchronize(Observable source) { @@ -1922,11 +1835,9 @@ public static Observable synchronize(Observable source) { *

    * * - * @param interval - * Interval size in time units (see below). - * @param unit - * Time units to use for the interval size. - * @return An Observable that emits an item each time interval. + * @param interval interval size in time units (see below) + * @param unit time units to use for the interval size + * @return an Observable that emits an item each time interval * @see MSDN: Observable.Interval */ public static Observable interval(long interval, TimeUnit unit) { @@ -1938,13 +1849,10 @@ public static Observable interval(long interval, TimeUnit unit) { *

    * * - * @param interval - * Interval size in time units (see below). - * @param unit - * Time units to use for the interval size. - * @param scheduler - * The scheduler to use for scheduling the items. - * @return An Observable that emits an item each time interval. + * @param interval interval size in time units (see below) + * @param unit time units to use for the interval size + * @param scheduler the scheduler to use for scheduling the items + * @return an Observable that emits an item each time interval * @see MSDN: Observable.Interval */ public static Observable interval(long interval, TimeUnit unit, Scheduler scheduler) { @@ -1952,26 +1860,27 @@ public static Observable interval(long interval, TimeUnit unit, Scheduler } /** - * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. + * Drops items emitted by an Observable that are followed by newer items + * before a timeout value expires. The timer resets on each emission. *

    - * NOTE: If events keep firing faster than the timeout then no data will be emitted. + * Note: If events keep firing faster than the timeout then no data will be + * emitted. *

    * *

    * Information on debounce vs throttle: *

    *

    * - * @param timeout - * The time each value has to be 'the most recent' of the {@link Observable} to ensure that it's not dropped. - * @param unit - * The {@link TimeUnit} for the timeout. - * - * @return An {@link Observable} which filters out values which are too quickly followed up with newer values. + * @param timeout the time each value has to be "the most recent" of the + * {@link Observable} to ensure that it's not dropped + * @param unit the {@link TimeUnit} for the timeout + * @return an {@link Observable} that filters out items that are too + * quickly followed by newer items * @see #throttleWithTimeout(long, TimeUnit) */ public Observable debounce(long timeout, TimeUnit unit) { @@ -1979,27 +1888,29 @@ public Observable debounce(long timeout, TimeUnit unit) { } /** - * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. + * Drops items emitted by an Observable that are followed by newer items + * before a timeout value expires. The timer resets on each emission. *

    - * NOTE: If events keep firing faster than the timeout then no data will be emitted. + * Note: If events keep firing faster than the timeout then no data will be + * emitted. *

    * *

    * Information on debounce vs throttle: *

    *

    * - * @param timeout - * The time each value has to be 'the most recent' of the {@link Observable} to ensure that it's not dropped. - * @param unit - * The unit of time for the specified timeout. - * @param scheduler - * The {@link Scheduler} to use internally to manage the timers which handle timeout for each event. - * @return Observable which performs the throttle operation. + * @param timeout the time each value has to be "the most recent" of the + * {@link Observable} to ensure that it's not dropped + * @param unit the unit of time for the specified timeout + * @param scheduler the {@link Scheduler} to use internally to manage the + * timers that handle the timeout for each event + * @return an {@link Observable} that filters out items that are too + * quickly followed by newer items * @see #throttleWithTimeout(long, TimeUnit, Scheduler) */ public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) { @@ -2007,26 +1918,27 @@ public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) } /** - * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. + * Drops items emitted by an Observable that are followed by newer items + * before a timeout value expires. The timer resets on each emission. *

    - * NOTE: If events keep firing faster than the timeout then no data will be emitted. + * Note: If events keep firing faster than the timeout then no data will be + * emitted. *

    * *

    * Information on debounce vs throttle: *

    *

    * - * @param timeout - * The time each value has to be 'the most recent' of the {@link Observable} to ensure that it's not dropped. - * @param unit - * The {@link TimeUnit} for the timeout. - * - * @return An {@link Observable} which filters out values which are too quickly followed up with newer values. + * @param timeout the time each value has to be "the most recent" of the + * {@link Observable} to ensure that it's not dropped + * @param unit the {@link TimeUnit} for the timeout + * @return an {@link Observable} that filters out items that are too + * quickly followed by newer items * @see #debounce(long, TimeUnit) */ public Observable throttleWithTimeout(long timeout, TimeUnit unit) { @@ -2034,19 +1946,29 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit) { } /** - * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. + * Drops items emitted by an Observable that are followed by newer items + * before a timeout value expires. The timer resets on each emission. *

    - * NOTE: If events keep firing faster than the timeout then no data will be emitted. + * Note: If events keep firing faster than the timeout then no data will be + * emitted. *

    * + *

    + * Information on debounce vs throttle: + *

    + *

    * - * @param timeout - * The time each value has to be 'the most recent' of the {@link Observable} to ensure that it's not dropped. - * @param unit - * The unit of time for the specified timeout. - * @param scheduler - * The {@link Scheduler} to use internally to manage the timers which handle timeout for each event. - * @return Observable which performs the throttle operation. + * @param timeout the time each value has to be "the most recent" of the + * {@link Observable} to ensure that it's not dropped + * @param unit the {@link TimeUnit} for the timeout + * @param scheduler the {@link Scheduler} to use internally to manage the + * timers that handle the timeout for each event + * @return an {@link Observable} that filters out items that are too + * quickly followed by newer items * @see #debounce(long, TimeUnit, Scheduler) */ public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler scheduler) { @@ -2054,53 +1976,57 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler } /** - * Throttles by skipping value until `skipDuration` passes and then emits the next received value. + * Throttles by skipping items until "skipDuration" passes and then emits + * the next received item. *

    - * This differs from {@link #throttleLast} in that this only tracks passage of time whereas {@link #throttleLast} ticks at scheduled intervals. + * This differs from {@link #throttleLast} in that this only tracks passage + * of time whereas {@link #throttleLast} ticks at scheduled intervals. *

    * * - * @param windowDuration - * Time to wait before sending another value after emitting last value. - * @param unit - * The unit of time for the specified timeout. - * @return Observable which performs the throttle operation. + * @param windowDuration time to wait before sending another item after + * emitting the last item + * @param unit the unit of time for the specified timeout + * @return an Observable that performs the throttle operation */ public Observable throttleFirst(long windowDuration, TimeUnit unit) { return create(OperationThrottleFirst.throttleFirst(this, windowDuration, unit)); } /** - * Throttles by skipping value until `skipDuration` passes and then emits the next received value. + * Throttles by skipping items until "skipDuration" passes and then emits + * the next received item. *

    - * This differs from {@link #throttleLast} in that this only tracks passage of time whereas {@link #throttleLast} ticks at scheduled intervals. + * This differs from {@link #throttleLast} in that this only tracks passage + * of time whereas {@link #throttleLast} ticks at scheduled intervals. *

    * * - * @param skipDuration - * Time to wait before sending another value after emitting last value. - * @param unit - * The unit of time for the specified timeout. - * @param scheduler - * The {@link Scheduler} to use internally to manage the timers which handle timeout for each event. - * @return Observable which performs the throttle operation. + * @param skipDuration time to wait before sending another item after + * emitting the last item + * @param unit the unit of time for the specified timeout + * @param scheduler the {@link Scheduler} to use internally to manage the + * timers that handle timeout for each event + * @return an Observable that performs the throttle operation */ public Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler scheduler) { return create(OperationThrottleFirst.throttleFirst(this, skipDuration, unit, scheduler)); } /** - * Throttles by returning the last value of each interval defined by 'intervalDuration'. + * Throttles by emitting the last item in each interval defined by + * intervalDuration. *

    - * This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas {@link #throttleFirst} does not tick, it just tracks passage of time. + * This differs from {@link #throttleFirst} in that this ticks along at a + * scheduled interval whereas {@link #throttleFirst} does not tick, it just + * tracks passage of time. *

    * * - * @param intervalDuration - * Duration of windows within with the last value will be chosen. - * @param unit - * The unit of time for the specified interval. - * @return Observable which performs the throttle operation. + * @param intervalDuration duration of windows within which the last item + * will be emitted + * @param unit the unit of time for the specified interval + * @return an Observable that performs the throttle operation * @see #sample(long, TimeUnit) */ public Observable throttleLast(long intervalDuration, TimeUnit unit) { @@ -2108,17 +2034,19 @@ public Observable throttleLast(long intervalDuration, TimeUnit unit) { } /** - * Throttles by returning the last value of each interval defined by 'intervalDuration'. + * Throttles by emitting the last item in each interval defined by + * intervalDuration. *

    - * This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas {@link #throttleFirst} does not tick, it just tracks passage of time. + * This differs from {@link #throttleFirst} in that this ticks along at a + * scheduled interval whereas {@link #throttleFirst} does not tick, it just + * tracks passage of time. *

    * * - * @param intervalDuration - * Duration of windows within with the last value will be chosen. - * @param unit - * The unit of time for the specified interval. - * @return Observable which performs the throttle operation. + * @param intervalDuration duration of windows within which the last item + * will be emitted + * @param unit the unit of time for the specified interval + * @return an Observable that performs the throttle operation * @see #sample(long, TimeUnit, Scheduler) */ public Observable throttleLast(long intervalDuration, TimeUnit unit, Scheduler scheduler) { @@ -2126,11 +2054,13 @@ public Observable throttleLast(long intervalDuration, TimeUnit unit, Schedule } /** - * Wraps each item emitted by a source Observable in a {@link Timestamped} object. + * Wraps each item emitted by a source Observable in a {@link Timestamped} + * object. *

    * * - * @return an Observable that emits timestamped items from the source Observable + * @return an Observable that emits timestamped items from the source + * Observable */ public Observable> timestamp() { return create(OperationTimestamp.timestamp(this)); @@ -2141,17 +2071,17 @@ public Observable> timestamp() { *

    * *

    - * You can convert any object that supports the {@link Future} interface into an Observable that - * emits the return value of the {@link Future#get} method of that object, by passing the - * object into the {@code from} method. + * You can convert any object that supports the {@link Future} interface + * into an Observable that emits the return value of the {@link Future#get} + * method of that object, by passing the object into the {@code from} + * method. *

    - * Important note: This Observable is blocking; you cannot unsubscribe from it. + * Important note: This Observable is blocking; you cannot + * unsubscribe from it. * - * @param future - * the source {@link Future} - * @param - * the type of object that the {@link Future} returns, and also the type of item to - * be emitted by the resulting Observable + * @param future the source {@link Future} + * @param the type of object that the {@link Future} returns, and also + * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source Future */ public static Observable from(Future future) { @@ -2163,18 +2093,18 @@ public static Observable from(Future future) { *

    * *

    - * You can convert any object that supports the {@link Future} interface into an Observable that - * emits the return value of the {@link Future#get} method of that object, by passing the - * object into the {@code from} method. + * You can convert any object that supports the {@link Future} interface + * into an Observable that emits the return value of the {@link Future#get} + * method of that object, by passing the object into the {@code from} + * method. *

    * - * @param future - * the source {@link Future} - * @param scheduler - * the {@link Scheduler} to wait for the Future on. Use a Scheduler such as {@link Schedulers#threadPoolForIO()} that can block and wait on the future. - * @param - * the type of object that the {@link Future} returns, and also the type of item to - * be emitted by the resulting Observable + * @param future the source {@link Future} + * @param scheduler the {@link Scheduler} to wait for the Future on. Use a + * Scheduler such as {@link Schedulers#threadPoolForIO()} + * that can block and wait on the future. + * @param the type of object that the {@link Future} returns, and also + * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source Future */ public static Observable from(Future future, Scheduler scheduler) { @@ -2186,21 +2116,19 @@ public static Observable from(Future future, Scheduler sched *

    * *

    - * You can convert any object that supports the {@link Future} interface into an Observable that - * emits the return value of the {link Future#get} method of that object, by passing the - * object into the {@code from} method. + * You can convert any object that supports the {@link Future} interface + * into an Observable that emits the return value of the {link Future#get} + * method of that object, by passing the object into the {@code from} + * method. *

    - * Important note: This Observable is blocking; you cannot unsubscribe from it. + * Important note: This Observable is blocking; you cannot + * unsubscribe from it. * - * @param future - * the source {@link Future} - * @param timeout - * the maximum time to wait before calling get() - * @param unit - * the {@link TimeUnit} of the time argument - * @param - * the type of object that the {@link Future} returns, and also the type of item to - * be emitted by the resulting Observable + * @param future the source {@link Future} + * @param timeout the maximum time to wait before calling get() + * @param unit the {@link TimeUnit} of the timeout argument + * @param the type of object that the {@link Future} returns, and also + * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source {@link Future} */ public static Observable from(Future future, long timeout, TimeUnit unit) { @@ -2208,19 +2136,16 @@ public static Observable from(Future future, long timeout, T } /** - * Returns an Observable that emits Boolean values that indicate whether the pairs of items - * emitted by two source Observables are equal. + * Returns an Observable that emits Boolean values that indicate whether the + * pairs of items emitted by two source Observables are equal. *

    * * - * @param first - * one Observable to compare - * @param second - * the second Observable to compare - * @param - * the type of items emitted by each Observable - * @return an Observable that emits Booleans that indicate whether the corresponding items - * emitted by the source Observables are equal + * @param first the first Observable to compare + * @param second the second Observable to compare + * @param the type of items emitted by each Observable + * @return an Observable that emits Booleans that indicate whether the + * corresponding items emitted by the source Observables are equal */ public static Observable sequenceEqual(Observable first, Observable second) { return sequenceEqual(first, second, new Func2() { @@ -2232,46 +2157,48 @@ public Boolean call(T first, T second) { } /** - * Returns an Observable that emits Boolean values that indicate whether the pairs of items - * emitted by two source Observables are equal based on the results of a specified equality - * function. + * Returns an Observable that emits Boolean values that indicate whether the + * pairs of items emitted by two source Observables are equal based on the + * results of a specified equality function. *

    * * - * @param first - * one Observable to compare - * @param second - * the second Observable to compare - * @param equality - * a function used to compare items emitted by both Observables - * @param - * the type of items emitted by each Observable - * @return an Observable that emits Booleans that indicate whether the corresponding items - * emitted by the source Observables are equal + * @param first the first Observable to compare + * @param second the second Observable to compare + * @param equality a function used to compare items emitted by both + * Observables + * @param the type of items emitted by each Observable + * @return an Observable that emits Booleans that indicate whether the + * corresponding items emitted by the source Observables are equal */ public static Observable sequenceEqual(Observable first, Observable second, Func2 equality) { return zip(first, second, equality); } /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of two items emitted, in sequence, by two other Observables. + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of two items emitted, in sequence, by + * two other Observables. *

    * - *

    {@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0} and the first item emitted by {@code w1}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by {@code w0} and the second item emitted by {@code w1}; and so forth. *

    - * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * another source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by {@code o1} and the first item emitted by + * {@code o2}; the second item emitted by the new Observable will be the + * result of the function applied to the second item emitted by {@code o1} + * and the second item emitted by {@code o2}; and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 another source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item that will + * be emitted by the resulting Observable * @return an Observable that emits the zipped results */ public static Observable zip(Observable o1, Observable o2, Func2 zipFunction) { @@ -2279,27 +2206,31 @@ public static Observable zip(Observable o1, Observa } /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of three items emitted, in sequence, by three other Observables. + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of three items emitted, in sequence, by + * three other Observables. *

    * - *

    {@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, and the first item emitted by {@code w2}; the second - * item emitted by the new Observable will be the result of the - * function applied to the second item emitted by {@code w0}, the second item emitted by {@code w1}, and the second item emitted by {@code w2}; and so forth. *

    - * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by {@code o1}, the first item emitted by + * {@code o2}, and the first item emitted by {@code o3}; the second item + * emitted by the new Observable will be the result of the function applied + * to the second item emitted by {@code o1}, the second item emitted by + * {@code o2}, and the second item emitted by {@code o3}; and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results */ public static Observable zip(Observable o1, Observable o2, Observable o3, Func3 zipFunction) { @@ -2307,30 +2238,32 @@ public static Observable zip(Observable o1, Obs } /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of four items emitted, in sequence, by four other Observables. + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of four items emitted, in sequence, by + * four other Observables. *

    * - *

    {@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. *

    - * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by {@code o1}, the first item emitted by + * {@code o2}, the first item emitted by {@code o3}, and the first item + * emitted by {@code 04}; the second item emitted by the new Observable will + * be the result of the function applied to the second item emitted by each + * of those Observables; and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 one source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item that will + * be emitted by the resulting Observable * @return an Observable that emits the zipped results */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Func4 zipFunction) { @@ -2338,32 +2271,33 @@ public static Observable zip(Observable o1, } /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of five items emitted, in sequence, by five other Observables. + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of five items emitted, in sequence, by + * five other Observables. *

    * - *

    {@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. *

    - * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param o5 - * a fifth source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by {@code o1}, the first item emitted by + * {@code o2}, the first item emitted by {@code o3}, the first item emitted + * by {@code o4}, and the first item emitted by {@code o5}; the second item + * emitted by the new Observable will be the result of the function applied + * to the second item emitted by each of those Observables; and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param o5 a fifth source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 zipFunction) { @@ -2371,34 +2305,32 @@ public static Observable zip(Observable } /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of six items emitted, in sequence, by six other Observables. + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of six items emitted, in sequence, by + * six other Observables. *

    * - *

    {@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. *

    - * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param o5 - * a fifth source Observable - * @param o6 - * a sixth source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted each source Observable, the second item emitted + * by the new Observable will be the result of the function applied to the + * second item emitted by each of those Observables, and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param o5 a fifth source Observable + * @param o6 a sixth source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, @@ -2407,36 +2339,33 @@ public static Observable zip(Observable * - *

    {@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. *

    - * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param o5 - * a fifth source Observable - * @param o6 - * a sixth source Observable - * @param o7 - * a seventh source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted each source Observable, the second item emitted + * by the new Observable will be the result of the function applied to the + * second item emitted by each of those Observables, and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param o5 a fifth source Observable + * @param o6 a sixth source Observable + * @param o7 a seventh source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, @@ -2445,38 +2374,34 @@ public static Observable zip(Observable * - *

    {@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. *

    - * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param o5 - * a fifth source Observable - * @param o6 - * a sixth source Observable - * @param o7 - * a seventh source Observable - * @param o8 - * an eighth source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted each source Observable, the second item emitted + * by the new Observable will be the result of the function applied to the + * second item emitted by each of those Observables, and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param o5 a fifth source Observable + * @param o6 a sixth source Observable + * @param o7 a seventh source Observable + * @param o8 an eighth source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, @@ -2485,40 +2410,35 @@ public static Observable zip(Observable * - *

    {@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by {@code w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item - * emitted by {@code w3}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * each of those Observables; and so forth. *

    - * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations - * of the source Observable that emits the fewest items. - * - * @param o1 - * one source Observable - * @param o2 - * a second source Observable - * @param o3 - * a third source Observable - * @param o4 - * a fourth source Observable - * @param o5 - * a fifth source Observable - * @param o6 - * a sixth source Observable - * @param o7 - * a seventh source Observable - * @param o8 - * an eighth source Observable - * @param o9 - * a ninth source Observable - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted each source Observable, the second item emitted + * by the new Observable will be the result of the function applied to the + * second item emitted by each of those Observables, and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param o5 a fifth source Observable + * @param o6 a sixth source Observable + * @param o7 a seventh source Observable + * @param o8 an eighth source Observable + * @param o9 a ninth source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, @@ -2527,33 +2447,60 @@ public static Observable zip(Observab } /** - * Combines the given observables, emitting an event containing an aggregation of the latest values of each of - * the source observables each time an event is received from one of the source observables, where the + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the * aggregation is defined by the given function. *

    * * - * @param o1 - * The first source observable. - * @param o2 - * The second source observable. - * @param combineFunction - * The aggregation function used to combine the source observable values. - * @return An Observable that combines the source Observables with the given combine function + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function */ public static Observable combineLatest(Observable o1, Observable o2, Func2 combineFunction) { return create(OperationCombineLatest.combineLatest(o1, o2, combineFunction)); } /** - * @see #combineLatest(Observable, Observable, Func2) + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Func3 combineFunction) { return create(OperationCombineLatest.combineLatest(o1, o2, o3, combineFunction)); } /** - * @see #combineLatest(Observable, Observable, Func2) + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Func4 combineFunction) { @@ -2561,7 +2508,22 @@ public static Observable combineLatest(Observable + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param o5 the fifth source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 combineFunction) { @@ -2569,7 +2531,23 @@ public static Observable combineLatest(Observable + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param o5 the fifth source Observable + * @param o6 the sixth source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Func6 combineFunction) { @@ -2577,7 +2555,24 @@ public static Observable combineLatest(Observable } /** - * @see #combineLatest(Observable, Observable, Func2) + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param o5 the fifth source Observable + * @param o6 the sixth source Observable + * @param o7 the seventh source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Func7 combineFunction) { @@ -2585,7 +2580,25 @@ public static Observable combineLatest(Observ } /** - * @see #combineLatest(Observable, Observable, Func2) + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param o5 the fifth source Observable + * @param o6 the sixth source Observable + * @param o7 the seventh source Observable + * @param o8 the eighth source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Func8 combineFunction) { @@ -2593,7 +2606,26 @@ public static Observable combineLatest(Ob } /** - * @see #combineLatest(Observable, Observable, Func2) + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param o5 the fifth source Observable + * @param o6 the sixth source Observable + * @param o7 the seventh source Observable + * @param o8 the eighth source Observable + * @param o9 the ninth source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9, Func9 combineFunction) { @@ -2601,21 +2633,27 @@ public static Observable combineLates } /** - * Creates an Observable which produces buffers of collected values. + * Creates an Observable that produces buffers of collected items. *

    * *

    - * This Observable produces connected non-overlapping buffers. The current buffer is - * emitted and replaced with a new buffer when the Observable produced by the specified {@link Func0} produces a {@link rx.util.Closing} object. The * {@link Func0} will then - * be used to create a new Observable to listen for the end of the next buffer. - * - * @param bufferClosingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every buffer created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated buffer - * is emitted and replaced with a new one. - * @return - * An {@link Observable} which produces connected non-overlapping buffers, which are emitted - * when the current {@link Observable} created with the {@link Func0} argument produces a {@link rx.util.Closing} object. + * This Observable produces connected, non-overlapping buffers. The current + * buffer is emitted and replaced with a new buffer when the Observable + * produced by the specified bufferClosingSelector produces a + * {@link rx.util.Closing} object. The bufferClosingSelector + * will then be used to create a new Observable to listen for the end of + * the next buffer. + * + * @param bufferClosingSelector the {@link Func0} which is used to produce + * an {@link Observable} for every buffer + * created. When this {@link Observable} + * produces a {@link rx.util.Closing} object, + * the associated buffer is emitted and + * replaced with a new one. + * @return an {@link Observable} which produces connected, non-overlapping + * buffers, which are emitted when the current {@link Observable} + * created with the {@link Func0} argument produces a + * {@link rx.util.Closing} object */ public Observable> buffer(Func0> bufferClosingSelector) { return create(OperationBuffer.buffer(this, bufferClosingSelector)); @@ -2626,435 +2664,457 @@ public Observable> buffer(Func0> *

    * *

    - * This Observable produces buffers. Buffers are created when the specified "bufferOpenings" - * Observable produces a {@link rx.util.Opening} object. Additionally the {@link Func0} argument - * is used to create an Observable which produces {@link rx.util.Closing} objects. When this - * Observable produces such an object, the associated buffer is emitted. - * - * @param bufferOpenings - * The {@link Observable} which, when it produces a {@link rx.util.Opening} object, will cause - * another buffer to be created. - * @param bufferClosingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every buffer created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated buffer - * is emitted. - * @return - * An {@link Observable} which produces buffers which are created and emitted when the specified {@link Observable}s publish certain objects. + * This Observable produces buffers. Buffers are created when the specified + * bufferOpenings Observable produces a {@link rx.util.Opening} + * object. Additionally the bufferClosingSelector argument is + * used to create an Observable which produces {@link rx.util.Closing} + * objects. When this Observable produces such an object, the associated + * buffer is emitted. + * + * @param bufferOpenings the {@link Observable} that, when it produces a + * {@link rx.util.Opening} object, will cause another + * buffer to be created + * @param bufferClosingSelector the {@link Func1} that is used to produce + * an {@link Observable} for every buffer + * created. When this {@link Observable} + * produces a {@link rx.util.Closing} object, + * the associated buffer is emitted. + * @return an {@link Observable} that produces buffers that are created and + * emitted when the specified {@link Observable}s publish certain + * objects */ public Observable> buffer(Observable bufferOpenings, Func1> bufferClosingSelector) { return create(OperationBuffer.buffer(this, bufferOpenings, bufferClosingSelector)); } /** - * Creates an Observable which produces buffers of collected values. + * Creates an Observable that produces buffers of collected items. *

    * *

    - * This Observable produces connected non-overlapping buffers, each containing "count" - * elements. When the source Observable completes or encounters an error, the current - * buffer is emitted, and the event is propagated. + * This Observable produces connected, non-overlapping buffers, each + * containing count items. When the source Observable completes + * or encounters an error, the current buffer is emitted, and the event is + * propagated. * - * @param count - * The maximum size of each buffer before it should be emitted. - * @return - * An {@link Observable} which produces connected non-overlapping buffers containing at most - * "count" produced values. + * @param count the maximum size of each buffer before it should be emitted + * @return an {@link Observable} that produces connected, non-overlapping + * buffers containing at most "count" items */ public Observable> buffer(int count) { return create(OperationBuffer.buffer(this, count)); } /** - * Creates an Observable which produces buffers of collected values. + * Creates an Observable which produces buffers of collected items. *

    * *

    - *

    This Observable produces buffers every "skip" values, each containing "count" - * elements. When the source Observable completes or encounters an error, the current - * buffer is emitted, and the event is propagated. + * This Observable produces buffers every skip items, each + * containing count items. When the source Observable + * completes or encounters an error, the current buffer is emitted, and the + * event is propagated. * - * @param count - * The maximum size of each buffer before it should be emitted. - * @param skip - * How many produced values need to be skipped before starting a new buffer. Note that when "skip" and - * "count" are equals that this is the same operation as {@link Observable#buffer(int)}. - * @return - * An {@link Observable} which produces buffers every "skipped" values containing at most - * "count" produced values. + * @param count the maximum size of each buffer before it should be emitted + * @param skip how many produced items need to be skipped before starting a + * new buffer. Note that when skip and + * count are equal, this is the same operation as + * {@link Observable#buffer(int)}. + * @return an {@link Observable} that produces buffers every + * skip item containing at most count + * items */ public Observable> buffer(int count, int skip) { return create(OperationBuffer.buffer(this, count, skip)); } /** - * Creates an Observable which produces buffers of collected values. + * Creates an Observable that produces buffers of collected values. *

    * *

    - * This Observable produces connected non-overlapping buffers, each of a fixed duration - * specified by the "timespan" argument. When the source Observable completes or encounters - * an error, the current buffer is emitted and the event is propagated. + * This Observable produces connected, non-overlapping buffers, each of a + * fixed duration specified by the timespan argument. When the + * source Observable completes or encounters an error, the current buffer is + * emitted and the event is propagated. * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @return - * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. + * @param timespan the period of time each buffer collects values before it + * should be emitted and replaced with a new buffer + * @param unit the unit of time which applies to the timespan + * argument + * @return an {@link Observable} that produces connected, non-overlapping + * buffers with a fixed duration */ public Observable> buffer(long timespan, TimeUnit unit) { return create(OperationBuffer.buffer(this, timespan, unit)); } /** - * Creates an Observable which produces buffers of collected values. + * Creates an Observable that produces buffers of collected values. *

    * *

    - * This Observable produces connected non-overlapping buffers, each of a fixed duration - * specified by the "timespan" argument. When the source Observable completes or encounters - * an error, the current buffer is emitted and the event is propagated. + * This Observable produces connected, non-overlapping buffers, each of a + * fixed duration specified by the timespan argument. When the + * source Observable completes or encounters an error, the current buffer is + * emitted and the event is propagated. * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. - * @return - * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. + * @param timespan the period of time each buffer collects values before it + * should be emitted and replaced with a new buffer + * @param unit the unit of time which applies to the timespan + * argument + * @param scheduler the {@link Scheduler} to use when determining the end + * and start of a buffer + * @return an {@link Observable} that produces connected, non-overlapping + * buffers with a fixed duration */ public Observable> buffer(long timespan, TimeUnit unit, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, unit, scheduler)); } /** - * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes - * or encounters an error, the current buffer is emitted and the event is propagated. + * Creates an Observable that produces buffers of collected items. This + * Observable produces connected, non-overlapping buffers, each of a fixed + * duration specified by the timespan argument or a maximum + * size specified by the count argument (whichever is reached + * first). When the source Observable completes or encounters an error, the + * current buffer is emitted and the event is propagated. *

    * * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param count - * The maximum size of each buffer before it should be emitted. - * @return - * An {@link Observable} which produces connected non-overlapping buffers which are emitted after - * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). + * @param timespan the period of time each buffer collects values before it + * should be emitted and replaced with a new buffer + * @param unit the unit of time which applies to the timespan + * argument + * @param count the maximum size of each buffer before it should be emitted + * @return an {@link Observable} that produces connected, non-overlapping + * buffers that are emitted after a fixed duration or when the + * buffer reaches maximum capacity (whichever occurs first) */ public Observable> buffer(long timespan, TimeUnit unit, int count) { return create(OperationBuffer.buffer(this, timespan, unit, count)); } /** - * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes - * or encounters an error, the current buffer is emitted and the event is propagated. + * Creates an Observable that produces buffers of collected items. This + * Observable produces connected, non-overlapping buffers, each of a fixed + * duration specified by the timespan argument or a maximum + * size specified by the count argument (whichever is reached + * first). When the source Observable completes or encounters an error, the + * current buffer is emitted and the event is propagated. *

    * * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param count - * The maximum size of each buffer before it should be emitted. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. - * @return - * An {@link Observable} which produces connected non-overlapping buffers which are emitted after - * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). + * @param timespan the period of time each buffer collects values before it + * should be emitted and replaced with a new buffer + * @param unit the unit of time which applies to the timespan + * argument + * @param count the maximum size of each buffer before it should be emitted + * @param scheduler the {@link Scheduler} to use when determining the end + and start of a buffer + * @return an {@link Observable} that produces connected, non-overlapping + * buffers that are emitted after a fixed duration or when the + * buffer has reached maximum capacity (whichever occurs first) */ public Observable> buffer(long timespan, TimeUnit unit, int count, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, unit, count, scheduler)); } /** - * Creates an Observable which produces buffers of collected values. This Observable starts a new buffer - * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the - * current buffer is emitted and the event is propagated. + * Creates an Observable that produces buffers of collected items. This + * Observable starts a new buffer periodically, as determined by the + * timeshift argument. Each buffer is emitted after a fixed + * timespan, specified by the timespan argument. When the + * source Observable completes or encounters an error, the current buffer is + * emitted and the event is propagated. *

    * * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new buffer will be created. - * @param unit - * The unit of time which applies to the "timespan" and "timeshift" argument. - * @return - * An {@link Observable} which produces new buffers periodically, and these are emitted after - * a fixed timespan has elapsed. + * @param timespan the period of time each buffer collects values before it + * should be emitted + * @param timeshift the period of time after which a new buffer will be + * created + * @param unit the unit of time that applies to the timespan + * and timeshift arguments + * @return an {@link Observable} that produces new buffers periodically and + * emits these after a fixed timespan has elapsed. */ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) { return create(OperationBuffer.buffer(this, timespan, timeshift, unit)); } /** - * Creates an Observable which produces buffers of collected values. This Observable starts a new buffer - * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the - * current buffer is emitted and the event is propagated. + * Creates an Observable that produces buffers of collected items. This + * Observable starts a new buffer periodically, as determined by the + * timeshift argument. Each buffer is emitted after a fixed + * timespan, specified by the timespan argument. When the + * source Observable completes or encounters an error, the current buffer is + * emitted and the event is propagated. *

    * * - * @param timespan - * The period of time each buffer is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new buffer will be created. - * @param unit - * The unit of time which applies to the "timespan" and "timeshift" argument. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. - * @return - * An {@link Observable} which produces new buffers periodically, and these are emitted after - * a fixed timespan has elapsed. + * @param timespan the period of time each buffer collects values before it + * should be emitted + * @param timeshift the period of time after which a new buffer will be + * created + * @param unit the unit of time that applies to the timespan + * and timeshift arguments + * @param scheduler the {@link Scheduler} to use when determining the end + * and start of a buffer + * @return an {@link Observable} that produces new buffers periodically and + * emits these after a fixed timespan has elapsed */ public Observable> buffer(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, timeshift, unit, scheduler)); } /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows. The current window is emitted and replaced with a new window when the - * Observable produced by the specified {@link Func0} produces a {@link rx.util.Closing} object. The {@link Func0} will then be used to create a new Observable to listen for the end of the next + * Creates an Observable that produces windows of collected items. This + * Observable produces connected, non-overlapping windows. The current + * window is emitted and replaced with a new window when the Observable + * produced by the specified closingSelector produces a + * {@link rx.util.Closing} object. The closingSelector will + * then be used to create a new Observable to listen for the end of the next * window. *

    * * - * @param closingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every window created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated window - * is emitted and replaced with a new one. - * @return - * An {@link Observable} which produces connected non-overlapping windows, which are emitted - * when the current {@link Observable} created with the {@link Func0} argument produces a {@link rx.util.Closing} object. + * @param closingSelector the {@link Func0} used to produce an + * {@link Observable} for every window created. When this + * {@link Observable} emits a {@link rx.util.Closing} object, the + * associated window is emitted and replaced with a new one. + * @return an {@link Observable} that produces connected, non-overlapping + * windows, which are emitted when the current {@link Observable} + * created with the closingSelector argument emits a + * {@link rx.util.Closing} object. */ public Observable> window(Func0> closingSelector) { return create(OperationWindow.window(this, closingSelector)); } /** - * Creates an Observable which produces windows of collected values. This Observable produces windows. - * Chunks are created when the specified "windowOpenings" Observable produces a {@link rx.util.Opening} object. - * Additionally the {@link Func0} argument is used to create an Observable which produces {@link rx.util.Closing} objects. When this Observable produces such an object, the associated window is - * emitted. + * Creates an Observable that produces windows of collected items. This + * Observable produces windows. Chunks are created when the + * windowOpenings Observable produces a {@link rx.util.Opening} + * object. Additionally the closingSelector argument creates an + * Observable that produces {@link rx.util.Closing} objects. When this + * Observable produces such an object, the associated window is emitted. *

    * * - * @param windowOpenings - * The {@link Observable} which when it produces a {@link rx.util.Opening} object, will cause - * another window to be created. - * @param closingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every window created. - * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated window - * is emitted. - * @return - * An {@link Observable} which produces windows which are created and emitted when the specified {@link Observable}s publish certain objects. + * @param windowOpenings the {@link Observable} that, when it produces a + * {@link rx.util.Opening} object, causes another + * window to be created + * @param closingSelector the {@link Func1} that produces an + * {@link Observable} for every window created. When + * this {@link Observable} produces a + * {@link rx.util.Closing} object, the associated + * window is emitted. + * @return an {@link Observable} that produces windows that are created and + * emitted when the specified {@link Observable}s publish certain + * objects */ public Observable> window(Observable windowOpenings, Func1> closingSelector) { return create(OperationWindow.window(this, windowOpenings, closingSelector)); } /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each containing "count" elements. When the source Observable completes or - * encounters an error, the current window is emitted, and the event is propagated. + * Creates an Observable that produces windows of collected items. This + * Observable produces connected, non-overlapping windows, each containing + * count elements. When the source Observable completes or + * encounters an error, the current window is emitted, and the event is + * propagated. *

    * * - * @param count - * The maximum size of each window before it should be emitted. - * @return - * An {@link Observable} which produces connected non-overlapping windows containing at most - * "count" produced values. + * @param count the maximum size of each window before it should be emitted + * @return an {@link Observable} that produces connected, non-overlapping + * windows containing at most count items */ public Observable> window(int count) { return create(OperationWindow.window(this, count)); } /** - * Creates an Observable which produces windows of collected values. This Observable produces windows every - * "skip" values, each containing "count" elements. When the source Observable completes or encounters an error, - * the current window is emitted and the event is propagated. + * Creates an Observable that produces windows of collected items. This + * Observable produces windows every skip items, each + * containing count elements. When the source Observable + * completes or encounters an error, the current window is emitted and the + * event is propagated. *

    * * - * @param count - * The maximum size of each window before it should be emitted. - * @param skip - * How many produced values need to be skipped before starting a new window. Note that when "skip" and - * "count" are equals that this is the same operation as {@link #window(int)}. - * @return - * An {@link Observable} which produces windows every "skipped" values containing at most - * "count" produced values. + * @param count the maximum size of each window before it should be emitted + * @param skip how many items need to be skipped before starting a new + * window. Note that if skip and count + * are equal this is the same operation as {@link #window(int)}. + * @return an {@link Observable} that produces windows every "skipped" + * items containing at most count items */ public Observable> window(int count, int skip) { return create(OperationWindow.window(this, count, skip)); } /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument. When the source - * Observable completes or encounters an error, the current window is emitted and the event is propagated. + * Creates an Observable that produces windows of collected items. This + * Observable produces connected, non-overlapping windows, each of a fixed + * duration specified by the timespan argument. When the source + * Observable completes or encounters an error, the current window is + * emitted and the event is propagated. *

    * * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @return - * An {@link Observable} which produces connected non-overlapping windows with a fixed duration. + * @param timespan the period of time each window collects items before it + * should be emitted and replaced with a new window + * @param unit the unit of time that applies to the timespan + * argument + * @return an {@link Observable} that produces connected, non-overlapping + * windows with a fixed duration */ public Observable> window(long timespan, TimeUnit unit) { return create(OperationWindow.window(this, timespan, unit)); } /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument. When the source - * Observable completes or encounters an error, the current window is emitted and the event is propagated. + * Creates an Observable that produces windows of collected items. This + * Observable produces connected, non-overlapping windows, each of a fixed + * duration as specified by the timespan argument. When the + * source Observable completes or encounters an error, the current window is + * emitted and the event is propagated. *

    * * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a window. - * @return - * An {@link Observable} which produces connected non-overlapping windows with a fixed duration. + * @param timespan the period of time each window collects items before it + * should be emitted and replaced with a new window + * @param unit the unit of time which applies to the timespan + * argument + * @param scheduler the {@link Scheduler} to use when determining the end + * and start of a window + * @return an {@link Observable} that produces connected, non-overlapping + * windows with a fixed duration */ public Observable> window(long timespan, TimeUnit unit, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, unit, scheduler)); } /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes - * or encounters an error, the current window is emitted and the event is propagated. + * Creates an Observable that produces windows of collected items. This + * Observable produces connected non-overlapping windows, each of a fixed + * duration as specified by the timespan argument or a maximum + * size as specified by the count argument (whichever is + * reached first). When the source Observable completes or encounters an + * error, the current window is emitted and the event is propagated. *

    * * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param count - * The maximum size of each window before it should be emitted. - * @return - * An {@link Observable} which produces connected non-overlapping windows which are emitted after - * a fixed duration or when the window has reached maximum capacity (which ever occurs first). + * @param timespan the period of time each window collects values before it + * should be emitted and replaced with a new window + * @param unit the unit of time that applies to the timespan + * argument + * @param count the maximum size of each window before it should be emitted + * @return an {@link Observable} that produces connected, non-overlapping + * windows that are emitted after a fixed duration or when the + * window has reached maximum capacity (whichever occurs first) */ public Observable> window(long timespan, TimeUnit unit, int count) { return create(OperationWindow.window(this, timespan, unit, count)); } /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the "timespan" argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes - * or encounters an error, the current window is emitted and the event is propagated. + * Creates an Observable that produces windows of collected items. This + * Observable produces connected, non-overlapping windows, each of a fixed + * duration specified by the timespan argument or a maximum + * size specified by the count argument (whichever is reached + * first). When the source Observable completes or encounters an error, the + * current window is emitted and the event is propagated. *

    * * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param count - * The maximum size of each window before it should be emitted. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a window. - * @return - * An {@link Observable} which produces connected non-overlapping windows which are emitted after - * a fixed duration or when the window has reached maximum capacity (which ever occurs first). + * @param timespan the period of time each window collects values before it + * should be emitted and replaced with a new window + * @param unit the unit of time which applies to the timespan + * argument + * @param count the maximum size of each window before it should be emitted + * @param scheduler the {@link Scheduler} to use when determining the end + * and start of a window. + * @return an {@link Observable} that produces connected non-overlapping + * windows that are emitted after a fixed duration or when the + * window has reached maximum capacity (whichever occurs first). */ public Observable> window(long timespan, TimeUnit unit, int count, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, unit, count, scheduler)); } /** - * Creates an Observable which produces windows of collected values. This Observable starts a new window - * periodically, which is determined by the "timeshift" argument. Each window is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the - * current window is emitted and the event is propagated. + * Creates an Observable that produces windows of collected items. This + * Observable starts a new window periodically, as determined by the + * timeshift argument. Each window is emitted after a fixed + * timespan, specified by the timespan argument. When the + * source Observable completes or encounters an error, the current window is + * emitted and the event is propagated. *

    * * - * @param timespan - * The period of time each window is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new window will be created. - * @param unit - * The unit of time which applies to the "timespan" and "timeshift" argument. - * @return - * An {@link Observable} which produces new windows periodically, and these are emitted after - * a fixed timespan has elapsed. + * @param timespan the period of time each window collects values before it + * should be emitted + * @param timeshift the period of time after which a new window will be + * created + * @param unit the unit of time that applies to the timespan + * and timeshift arguments + * @return an {@link Observable} that produces new windows periodically and + * emits these after a fixed timespan has elapsed */ public Observable> window(long timespan, long timeshift, TimeUnit unit) { return create(OperationWindow.window(this, timespan, timeshift, unit)); } /** - * Creates an Observable which produces windows of collected values. This Observable starts a new window - * periodically, which is determined by the "timeshift" argument. Each window is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the - * current window is emitted and the event is propagated. + * Creates an Observable that produces windows of collected items. This + * Observable starts a new window periodically, as determined by the + * timeshift argument. Each window is emitted after a fixed + * timespan, specified by the timespan argument. When the + * source Observable completes or encounters an error, the current window is + * emitted and the event is propagated. *

    * * - * @param timespan - * The period of time each window is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new window will be created. - * @param unit - * The unit of time which applies to the "timespan" and "timeshift" argument. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a window. - * @return - * An {@link Observable} which produces new windows periodically, and these are emitted after - * a fixed timespan has elapsed. + * @param timespan the period of time each window collects values before it + * should be emitted + * @param timeshift the period of time after which a new window will be + * created + * @param unit the unit of time that applies to the timespan + * and timeshift arguments + * @param scheduler the {@link Scheduler} to use when determining the end + * and start of a window + * @return an {@link Observable} that produces new windows periodically and + * emits these after a fixed timespan has elapsed */ public Observable> window(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, timeshift, unit, scheduler)); } /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of N items emitted, in sequence, by N other Observables as provided by an Iterable. - *

    {@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by - * all of the Observalbes; the second item emitted by the new Observable will be the result of - * the function applied to the second item emitted by each of those Observables; and so forth. + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of n items emitted, in sequence, + * by n other Observables as provided by an Iterable. *

    - * The resulting {@code Observable} returned from {@code zip} will invoke {@code onNext} as many times as the number of {@code onNext} invokations of the - * source Observable that emits the fewest items. + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by all of the source Observables; the second + * item emitted by the new Observable will be the result of the function + * applied to the second item emitted by each of those Observables; and so + * forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@code onNext} as many times as the number of {@code onNext} + * invokations of the source Observable that emits the fewest items. *

    * * - * @param ws - * An Observable of source Observables - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable + * @param ws an Observable of source Observables + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results */ public static Observable zip(Observable> ws, final FuncN zipFunction) { @@ -3067,23 +3127,27 @@ public Observable call(List> wsList) { } /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of four items emitted, in sequence, by four other Observables. - *

    {@code zip} applies this function in strict sequence, so the first item emitted by the - * new Observable will be the result of the function applied to the first item emitted by - * all of the Observalbes; the second item emitted by the new Observable will be the result of - * the function applied to the second item emitted by each of those Observables; and so forth. + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations items emitted, in sequence, by a + * collection of other Observables. + *

    + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by all of the source Observables; the second + * item emitted by the new Observable will be the result of the function + * applied to the second item emitted by each of those Observables; and so + * forth. *

    - * The resulting {@code Observable} returned from {@code zip} will invoke {@code onNext} as many times as the number of {@code onNext} invokations of the - * source Observable that emits the fewest items. + * The resulting {@code Observable} returned from {@code zip} will invoke + * {@code onNext} as many times as the number of {@code onNext} invokations + * of the source Observable that emits the fewest items. *

    * * - * @param ws - * A collection of source Observables - * @param zipFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable + * @param ws a collection of source Observables + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results */ public static Observable zip(Iterable> ws, FuncN zipFunction) { @@ -3095,17 +3159,19 @@ public static Observable zip(Iterable> ws, FuncN< *

    * * - * @param predicate - * a function that evaluates the items emitted by the source Observable, returning {@code true} if they pass the filter - * @return an Observable that emits only those items in the original Observable that the filter - * evaluates as {@code true} + * @param predicate a function that evaluates the items emitted by the + * source Observable, returning {@code true} if they pass + * the filter + * @return an Observable that emits only those items in the original + * Observable that the filter evaluates as {@code true} */ public Observable filter(Func1 predicate) { return create(OperationFilter.filter(this, predicate)); } /** - * Returns an Observable that forwards all sequentially distinct items emitted from the source Observable. + * Returns an Observable that forwards all sequentially distinct items + * emitted from the source Observable. *

    * * @@ -3117,14 +3183,15 @@ public Observable distinctUntilChanged() { } /** - * Returns an Observable that forwards all items emitted from the source Observable that are sequentially - * distinct according to a key selector function. + * Returns an Observable that forwards all items emitted from the source + * Observable that are sequentially distinct according to a key selector + * function. *

    * * - * @param keySelector - * a function that projects an emitted item to a key value which is used for deciding whether an item is sequentially - * distinct from another one or not + * @param keySelector a function that projects an emitted item to a key + * value that is used for deciding whether an item is + * sequentially distinct from another one or not * @return an Observable of sequentially distinct items * @see MSDN: Observable.distinctUntilChanged */ @@ -3133,7 +3200,8 @@ public Observable distinctUntilChanged(Func1 keyS } /** - * Returns an Observable that forwards all distinct items emitted from the source Observable. + * Returns an Observable that emits all distinct items emitted from the + * source Observable. *

    * * @@ -3145,15 +3213,15 @@ public Observable distinct() { } /** - * Returns an Observable that forwards all items emitted from the source Observable that are distinct according - * to a key selector function. + * Returns an Observable that emits all items emitted from the source + * Observable that are distinct according to a key selector function. *

    * * - * @param keySelector - * a function that projects an emitted item to a key value which is used for deciding whether an item is - * distinct from another one or not - * @return an Observable of distinct items + * @param keySelector a function that projects an emitted item to a key + * value that is used to decide whether an item is + * distinct from another one or not + * @return an Observable that emits distinct items * @see MSDN: Observable.distinct */ public Observable distinct(Func1 keySelector) { @@ -3161,66 +3229,67 @@ public Observable distinct(Func1 keySelector) { } /** - * Returns the element at a specified index in a sequence. - * - * @param index - * The zero-based index of the element to retrieve. - * - * @return An observable sequence that produces the element at the specified - * position in the source sequence. + * Returns the item at a specified index in a sequence. + *

    + * * - * @throws IndexOutOfBoundsException - * Index is greater than or equal to the number of elements in the source sequence. - * @throws IndexOutOfBoundsException - * Index is less than 0. + * @param index the zero-based index of the item to retrieve + * @return an Observable that emits the item at the specified position in + * the source sequence + * @throws IndexOutOfBoundsException if index is greater than + * or equal to the number of elements in + * the source sequence + * @throws IndexOutOfBoundsException if index is less than 0 */ public Observable elementAt(int index) { return create(OperationElementAt.elementAt(this, index)); } /** - * Returns the element at a specified index in a sequence or the default - * value if the index is out of range. - * - * @param index - * The zero-based index of the element to retrieve. - * @param defaultValue - * The default value. - * - * @return An observable sequence that produces the element at the specified - * position in the source sequence, or the default value if the - * index is outside the bounds of the source sequence. + * Returns the item at a specified index in a sequence or the default item + * if the index is out of range. + *

    + * * - * @throws IndexOutOfBoundsException - * Index is less than 0. + * @param index the zero-based index of the item to retrieve + * @param defaultValue the default item + * @return an Observable that emits the item at the specified position in + * the source sequence, or the default item if the index is outside + * the bounds of the source sequence + * @throws IndexOutOfBoundsException if index is less than 0 */ public Observable elementAtOrDefault(int index, T defaultValue) { return create(OperationElementAt.elementAtOrDefault(this, index, defaultValue)); } /** - * Returns an {@link Observable} that emits true if any element of the source {@link Observable} satisfies - * the given condition, otherwise false. Note: always emit false if the source {@link Observable} is empty. + * Returns an {@link Observable} that emits true if any element + * of the source {@link Observable} satisfies the given condition, otherwise + * false. Note: always emits false if the source + * {@link Observable} is empty. *

    - * In Rx.Net this is the any operator but renamed in RxJava to better match Java naming idioms. + * In Rx.Net this is the any operator but renamed in RxJava to + * better match Java naming idioms. + *

    + * * - * @param predicate - * The condition to test every element. - * @return A subscription function for creating the target Observable. - * @see MSDN: Observable.Any Note: the description in this page is - * wrong. + * @param predicate the condition to test every element + * @return a subscription function for creating the target Observable + * @see MSDN: Observable.Any Note: the description in this page is wrong. */ public Observable exists(Func1 predicate) { return create(OperationAny.exists(this, predicate)); } /** - * Determines whether an observable sequence contains a specified element. + * Determines whether an Observable sequence contains a specified item. + *

    + * * - * @param element - * The element to search in the sequence. - * @return an Observable that emits if the element is in the source sequence. - * @see MSDN: Observable.Contains + * @param element the item to search in the sequence + * @return an Observable that emits true if the item is in the + * source sequence + * @see MSDN: Observable.Contains */ public Observable contains(final T element) { return exists(new Func1() { @@ -3231,34 +3300,38 @@ public Boolean call(T t1) { } /** - * Registers an {@link Action0} to be called when this Observable invokes {@link Observer#onCompleted onCompleted} or {@link Observer#onError onError}. + * Registers an {@link Action0} to be called when this Observable invokes + * {@link Observer#onCompleted onCompleted} or + * {@link Observer#onError onError}. *

    * * - * @param action - * an {@link Action0} to be invoked when the source Observable finishes - * @return an Observable that emits the same items as the source Observable, then invokes the {@link Action0} - * @see MSDN: Observable.Finally Method + * @param action an {@link Action0} to be invoked when the source + * Observable finishes + * @return an Observable that emits the same items as the source Observable, + * then invokes the {@link Action0} + * @see MSDN: Observable.Finally Method */ public Observable finallyDo(Action0 action) { return create(OperationFinally.finallyDo(this, action)); } /** - * Creates a new Observable by applying a function that you supply to each item emitted by - * the source Observable, where that function returns an Observable, and then merging those - * resulting Observables and emitting the results of this merger. + * Creates a new Observable by applying a function that you supply to each + * item emitted by the source Observable, where that function returns an + * Observable, and then merging those resulting Observables and emitting the + * results of this merger. *

    * *

    * Note: {@code mapMany} and {@code flatMap} are equivalent. * - * @param func - * a function that, when applied to an item emitted by the source Observable, returns - * an Observable - * @return an Observable that emits the result of applying the transformation function to each - * item emitted by the source Observable and merging the results of the Observables - * obtained from this transformation. + * @param func a function that, when applied to an item emitted by the + * source Observable, returns an Observable + * @return an Observable that emits the result of applying the + * transformation function to each item emitted by the source + * Observable and merging the results of the Observables obtained + * from this transformation. * @see #mapMany(Func1) */ public Observable flatMap(Func1> func) { @@ -3270,11 +3343,11 @@ public Observable flatMap(Func1 * * - * @param predicate - * a function that evaluates an item emitted by the source Observable, returning {@code true} if it - * passes the filter - * @return an Observable that emits only those items in the original Observable that the filter - * evaluates as {@code true} + * @param predicate a function that evaluates an item emitted by the source + * Observable, returning {@code true} if it passes the + * filter + * @return an Observable that emits only those items emitted by the original + * Observable that the filter evaluates as {@code true} * @see #filter(Func1) */ public Observable where(Func1 predicate) { @@ -3282,15 +3355,14 @@ public Observable where(Func1 predicate) { } /** - * Returns an Observable that applies the given function to each item emitted by an - * Observable and emits the result. + * Returns an Observable that applies the given function to each item + * emitted by an Observable and emits the result. *

    * * - * @param func - * a function to apply to each item emitted by the Observable - * @return an Observable that emits the items from the source Observable, transformed by the - * given function + * @param func a function to apply to each item emitted by the Observable + * @return an Observable that emits the items from the source Observable, + * transformed by the given function * @see MSDN: Observable.Select */ public Observable map(Func1 func) { @@ -3298,16 +3370,16 @@ public Observable map(Func1 func) { } /** - * Returns an Observable that applies the given function to each item emitted by an - * Observable and emits the result. + * Returns an Observable that applies the given function to each item + * emitted by an Observable and emits the result. *

    * * - * @param func - * a function to apply to each item emitted by the Observable. The function takes the - * index of the emitted item as additional parameter. - * @return an Observable that emits the items from the source Observable, transformed by the - * given function + * @param func a function to apply to each item emitted by the Observable. + * The function takes the index of the emitted item as + * additional parameter. + * @return an Observable that emits the items from the source Observable, + * transformed by the given function * @see MSDN: Observable.Select */ public Observable mapWithIndex(Func2 func) { @@ -3315,20 +3387,21 @@ public Observable mapWithIndex(Func2 fun } /** - * Creates a new Observable by applying a function that you supply to each item emitted by - * the source Observable, where that function returns an Observable, and then merging those - * resulting Observables and emitting the results of this merger. + * Creates a new Observable by applying a function that you supply to each + * item emitted by the source Observable, where that function returns an + * Observable, and then merging those resulting Observables and emitting + * the results of this merger. *

    * *

    * Note: mapMany and flatMap are equivalent. * - * @param func - * a function that, when applied to an item emitted by the source Observable, returns - * an Observable - * @return an Observable that emits the result of applying the transformation function to each - * item emitted by the source Observable and merging the results of the Observables - * obtained from this transformation. + * @param func a function that, when applied to an item emitted by the + * source Observable, returns an Observable + * @return an Observable that emits the result of applying the + * transformation function to each item emitted by the source + * Observable and merging the results of the Observables obtained + * from this transformation. * @see #flatMap(Func1) */ public Observable mapMany(Func1> func) { @@ -3336,12 +3409,14 @@ public Observable mapMany(Func1 * * - * @return an Observable whose items are the result of materializing the items and - * notifications of the source Observable + * @return an Observable whose items are the result of materializing the + * items and notifications of the source Observable * @see MSDN: Observable.materialize */ public Observable> materialize() { @@ -3349,44 +3424,47 @@ public Observable> materialize() { } /** - * Asynchronously subscribes and unsubscribes Observers on the specified {@link Scheduler}. + * Asynchronously subscribes and unsubscribes Observers on the specified + * {@link Scheduler}. *

    * * - * @param scheduler - * the {@link Scheduler} to perform subscription and unsubscription actions on - * @return the source Observable modified so that its subscriptions and unsubscriptions happen - * on the specified {@link Scheduler} + * @param scheduler the {@link Scheduler} to perform subscription and + * unsubscription actions on + * @return the source Observable modified so that its subscriptions and + * unsubscriptions happen on the specified {@link Scheduler} */ public Observable subscribeOn(Scheduler scheduler) { return create(OperationSubscribeOn.subscribeOn(this, scheduler)); } /** - * Asynchronously notify {@link Observer}s on the specified {@link Scheduler}. + * Asynchronously notify {@link Observer}s on the specified + * {@link Scheduler}. *

    * * - * @param scheduler - * the {@link Scheduler} to notify {@link Observer}s on - * @return the source Observable modified so that its {@link Observer}s are notified on the - * specified {@link Scheduler} + * @param scheduler the {@link Scheduler} to notify {@link Observer}s on + * @return the source Observable modified so that its {@link Observer}s are + * notified on the specified {@link Scheduler} */ public Observable observeOn(Scheduler scheduler) { return create(OperationObserveOn.observeOn(this, scheduler)); } /** - * Returns an Observable that reverses the effect of {@link #materialize materialize} by - * transforming the {@link Notification} objects emitted by the source Observable into the items - * or notifications they represent. + * Returns an Observable that reverses the effect of + * {@link #materialize materialize} by transforming the {@link Notification} + * objects emitted by the source Observable into the items or notifications + * they represent. *

    * * - * @return an Observable that emits the items and notifications embedded in the {@link Notification} objects emitted by the source Observable - * @see MSDN: Observable.dematerialize - * @throws Throwable - * if the source Observable is not of type {@code Observable>}. + * @return an Observable that emits the items and notifications embedded in + * the {@link Notification} objects emitted by the source Observable + * @see MSDN: Observable.dematerialize + * @throws Throwable if the source Observable is not of type + * {@code Observable>}. */ @SuppressWarnings("unchecked") public Observable dematerialize() { @@ -3394,27 +3472,31 @@ public Observable dematerialize() { } /** - * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error. + * Instruct an Observable to pass control to another Observable rather than + * invoking {@link Observer#onError onError} if it encounters an error. *

    * *

    - * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorResumeNext method changes this behavior. If you pass a - * function that returns an Observable (resumeFunction) to - * onErrorResumeNext, if the original Observable encounters an error, instead of - * invoking its Observer's onError method, it will instead relinquish control to - * the Observable returned from resumeFunction, which will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no - * Observable necessarily invokes onError, the Observer may never know that an - * error happened. - *

    - * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param resumeFunction - * a function that returns an Observable that will take over if the source Observable - * encounters an error + * By default, when an Observable encounters an error that prevents it from + * emitting the expected item to its {@link Observer}, the Observable + * invokes its Observer's onError method, and then quits + * without invoking any more of its Observer's methods. The + * onErrorResumeNext method changes this behavior. If you pass + * a function that returns an Observable (resumeFunction) to + * onErrorResumeNext, if the original Observable encounters an + * error, instead of invoking its Observer's onError method, it + * will instead relinquish control to the Observable returned from + * resumeFunction, which will invoke the Observer's + * {@link Observer#onNext onNext} method if it is able to do so. In such a + * case, because no Observable necessarily invokes onError, the + * Observer may never know that an error happened. + *

    + * You can use this to prevent errors from propagating or to supply fallback + * data should errors be encountered. + * + * @param resumeFunction a function that returns an Observable that will + * take over if the source Observable encounters an + * error * @return the original Observable, with appropriately modified behavior */ public Observable onErrorResumeNext(final Func1> resumeFunction) { @@ -3422,27 +3504,31 @@ public Observable onErrorResumeNext(final Func1 * *

    - * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorResumeNext method changes this behavior. If you pass + * By default, when an Observable encounters an error that prevents it from + * emitting the expected item to its {@link Observer}, the Observable + * invokes its Observer's onError method, and then quits + * without invoking any more of its Observer's methods. The + * onErrorResumeNext method changes this behavior. If you pass * another Observable (resumeSequence) to an Observable's - * onErrorResumeNext method, if the original Observable encounters an error, - * instead of invoking its Observer's onError method, it will instead relinquish - * control to resumeSequence which will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no - * Observable necessarily invokes onError, the Observer may never know that an - * error happened. - *

    - * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param resumeSequence - * a function that returns an Observable that will take over if the source Observable - * encounters an error + * onErrorResumeNext method, if the original Observable + * encounters an error, instead of invoking its Observer's + * onError method, it will instead relinquish control to + * resumeSequence which will invoke the Observer's + * {@link Observer#onNext onNext} method if it is able to do so. In such a + * case, because no Observable necessarily invokes onError, the + * Observer may never know that an error happened. + *

    + * You can use this to prevent errors from propagating or to supply fallback + * data should errors be encountered. + * + * @param resumeSequence a function that returns an Observable that will + * take over if the source Observable encounters an + * error * @return the original Observable, with appropriately modified behavior */ public Observable onErrorResumeNext(final Observable resumeSequence) { @@ -3450,29 +3536,36 @@ public Observable onErrorResumeNext(final Observable resumeSeque } /** - * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error of type {@link java.lang.Exception}. + * Instruct an Observable to pass control to another Observable rather than + * invoking {@link Observer#onError onError} if it encounters an error of + * type {@link java.lang.Exception}. *

    - * This differs from {@link #onErrorResumeNext} in that this one does not handle {@link java.lang.Throwable} or {@link java.lang.Error} but lets those continue through. + * This differs from {@link #onErrorResumeNext} in that this one does not + * handle {@link java.lang.Throwable} or {@link java.lang.Error} but lets + * those continue through. *

    - * + * *

    - * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorResumeNext method changes this behavior. If you pass + * By default, when an Observable encounters an error that prevents it from + * emitting the expected item to its {@link Observer}, the Observable + * invokes its Observer's onError method, and then quits + * without invoking any more of its Observer's methods. The + * onErrorResumeNext method changes this behavior. If you pass * another Observable (resumeSequence) to an Observable's - * onErrorResumeNext method, if the original Observable encounters an error, - * instead of invoking its Observer's onError method, it will instead relinquish - * control to resumeSequence which will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no - * Observable necessarily invokes onError, the Observer may never know that an - * error happened. - *

    - * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param resumeSequence - * a function that returns an Observable that will take over if the source Observable - * encounters an error + * onErrorResumeNext method, if the original Observable + * encounters an error, instead of invoking its Observer's + * onError method, it will instead relinquish control to + * resumeSequence which will invoke the Observer's + * {@link Observer#onNext onNext} method if it is able to do so. In such a + * case, because no Observable necessarily invokes onError, + * the Observer may never know that an error happened. + *

    + * You can use this to prevent errors from propagating or to supply fallback + * data should errors be encountered. + * + * @param resumeSequence a function that returns an Observable that will + * take over if the source Observable encounters an + * error * @return the original Observable, with appropriately modified behavior */ public Observable onExceptionResumeNext(final Observable resumeSequence) { @@ -3480,26 +3573,29 @@ public Observable onExceptionResumeNext(final Observable resumeS } /** - * Instruct an Observable to emit an item (returned by a specified function) rather than - * invoking {@link Observer#onError onError} if it encounters an error. + * Instruct an Observable to emit an item (returned by a specified function) + * rather than invoking {@link Observer#onError onError} if it encounters an + * error. *

    * *

    - * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorReturn method changes this behavior. If you pass a function - * (resumeFunction) to an Observable's onErrorReturn method, if the - * original Observable encounters an error, instead of invoking its Observer's - * onError method, it will instead pass the return value of - * resumeFunction to the Observer's {@link Observer#onNext onNext} method. - *

    - * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param resumeFunction - * a function that returns an item that the new Observable will emit if the source - * Observable encounters an error + * By default, when an Observable encounters an error that prevents it from + * emitting the expected item to its {@link Observer}, the Observable + * invokes its Observer's onError method, and then quits + * without invoking any more of its Observer's methods. The + * onErrorReturn method changes this behavior. If you pass a + * function (resumeFunction) to an Observable's + * onErrorReturn method, if the original Observable encounters + * an error, instead of invoking its Observer's onError method, + * it will instead pass the return value of resumeFunction to + * the Observer's {@link Observer#onNext onNext} method. + *

    + * You can use this to prevent errors from propagating or to supply fallback + * data should errors be encountered. + * + * @param resumeFunction a function that returns an item that the new + * Observable will emit if the source Observable + * encounters an error * @return the original Observable with appropriately modified behavior */ public Observable onErrorReturn(Func1 resumeFunction) { @@ -3507,25 +3603,26 @@ public Observable onErrorReturn(Func1 resumeFunction) } /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by the source Observable into the same function, and so on until all items have been emitted - * by the source Observable, and emits the final result from the final call to your function as - * its sole item. + * Returns an Observable that applies a function of your choosing to the + * first item emitted by a source Observable, then feeds the result of that + * function along with the second item emitted by the source Observable into + * the same function, and so on until all items have been emitted by the + * source Observable, and emits the final result from the final call to your + * function as its sole item. *

    * *

    - * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," - * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, - * has an inject method that does a similar operation on lists. - * - * @param accumulator - * An accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be used in the next accumulator call - * @return an Observable that emits a single item that is the result of accumulating the - * output from the source Observable - * @throws IllegalArgumentException - * if Observable sequence is empty. + * This technique, which is called "reduce" or "aggregate" here, is + * sometimes called "fold," "accumulate," "compress," or "inject" in other + * programming contexts. Groovy, for instance, has an inject + * method that does a similar operation on lists. + * + * @param accumulator an accumulator function to be invoked on each item + * emitted by the source Observable, whose result will + * be used in the next accumulator call + * @return an Observable that emits a single item that is the result of + * accumulating the output from the source Observable + * @throws IllegalArgumentException if the Observable sequence is empty * @see MSDN: Observable.Aggregate * @see Wikipedia: Fold (higher-order function) */ @@ -3539,12 +3636,13 @@ public Observable reduce(Func2 accumulator) { } /** - * Returns an Observable that counts the total number of elements in the source Observable. + * Returns an Observable that counts the total number of items in the + * source Observable. *

    * * - * @return an Observable emitting the number of counted elements of the source Observable - * as its single item. + * @return an Observable that emits the number of counted elements of the + * source Observable as its single item * @see MSDN: Observable.Count */ public Observable count() { @@ -3557,14 +3655,14 @@ public Integer call(Integer t1, T t2) { } /** - * Returns an Observable that sums up the elements in the source Observable. + * Returns an Observable that sums up the integers emitted by the source + * Observable. *

    * * - * @param source - * Source observable to compute the sum of. - * @return an Observable emitting the sum of all the elements of the source Observable - * as its single item. + * @param source source Observable to compute the sum of + * @return an Observable that emits the sum of all the items of the + * source Observable as its single item * @see MSDN: Observable.Sum */ public static Observable sum(Observable source) { @@ -3572,7 +3670,14 @@ public static Observable sum(Observable source) { } /** - * @see #sum(Observable) + * Returns an Observable that sums up the longs emitted by the source + * Observable. + *

    + * + * + * @param source source Observable to compute the sum of + * @return an Observable that emits the sum of all the items of the + * source Observable as its single item * @see MSDN: Observable.Sum */ public static Observable sumLongs(Observable source) { @@ -3580,7 +3685,14 @@ public static Observable sumLongs(Observable source) { } /** - * @see #sum(Observable) + * Returns an Observable that sums up the floats emitted by the source + * Observable. + *

    + * + * + * @param source source Observable to compute the sum of + * @return an Observable that emits the sum of all the items of the + * source Observable as its single item * @see MSDN: Observable.Sum */ public static Observable sumFloats(Observable source) { @@ -3588,7 +3700,14 @@ public static Observable sumFloats(Observable source) { } /** - * @see #sum(Observable) + * Returns an Observable that sums up the doubles emitted by the source + * Observable. + *

    + * + * + * @param source source Observable to compute the sum of + * @return an Observable that emits the sum of all the items of the + * source Observable as its single item * @see MSDN: Observable.Sum */ public static Observable sumDoubles(Observable source) { @@ -3596,17 +3715,15 @@ public static Observable sumDoubles(Observable source) { } /** - * Returns an Observable that computes the average of all elements in the source Observable. - * For an empty source, it causes an IllegalArgumentException. + * Returns an Observable that computes the average of the integers emitted + * by the source Observable. *

    * * - * @param source - * Source observable to compute the average of. - * @return an Observable emitting the averageof all the elements of the source Observable - * as its single item. - * @throws IllegalArgumentException - * if Observable sequence is empty. + * @param source source observable to compute the average of + * @return an Observable that emits the average of all the items emitted by + * the source Observable as its single item + * @throws IllegalArgumentException if the Observable sequence is empty * @see MSDN: Observable.Average */ public static Observable average(Observable source) { @@ -3614,7 +3731,14 @@ public static Observable average(Observable source) { } /** - * @see #average(Observable) + * Returns an Observable that computes the average of the longs emitted by + * the source Observable. + *

    + * + * + * @param source source observable to compute the average of + * @return an Observable that emits the average of all the items emitted by + * the source Observable as its single item * @see MSDN: Observable.Average */ public static Observable averageLongs(Observable source) { @@ -3622,7 +3746,14 @@ public static Observable averageLongs(Observable source) { } /** - * @see #average(Observable) + * Returns an Observable that computes the average of the floats emitted by + * the source Observable. + *

    + * + * + * @param source source observable to compute the average of + * @return an Observable that emits the average of all the items emitted by + * the source Observable as its single item * @see MSDN: Observable.Average */ public static Observable averageFloats(Observable source) { @@ -3630,7 +3761,14 @@ public static Observable averageFloats(Observable source) { } /** - * @see #average(Observable) + * Returns an Observable that computes the average of the doubles emitted + * by the source Observable. + *

    + * + * + * @param source source observable to compute the average of + * @return an Observable that emits the average of all the items emitted by + * the source Observable as its single item * @see MSDN: Observable.Average */ public static Observable averageDoubles(Observable source) { @@ -3638,133 +3776,143 @@ public static Observable averageDoubles(Observable source) { } /** - * Returns the minimum element in an observable sequence. - * If there are more than one minimum elements, returns the last one. - * For an empty source, it causes an {@link IllegalArgumentException}. + * Returns the minimum item emitted by an Observable. If there are more than + * one minimum items, its returns the last one. + *

    + * * - * @param source - * an observable sequence to determine the minimum element of. - * @return an observable emitting the minimum element. - * @throws IllegalArgumentException - * if the source is empty - * @see MSDN: Observable.Min + * @param source an Observable sequence to determine the minimum item of + * @return an Observable that emits the minimum item + * @throws IllegalArgumentException if the source is empty + * @see MSDN: Observable.Min */ public static > Observable min(Observable source) { return OperationMinMax.min(source); } /** - * Returns the minimum element in an observable sequence according to the specified comparator. - * If there are more than one minimum elements, returns the last one. - * For an empty source, it causes an {@link IllegalArgumentException}. + * Returns the minimum item emitted by an Observable according to a + * specified comparator. If there are more than one minimum items, it + * returns the last one. + *

    + * * - * @param comparator - * the comparer used to compare elements. - * @return an observable emitting the minimum value according to the specified comparator. - * @throws IllegalArgumentException - * if the source is empty - * @see MSDN: Observable.Min + * @param comparator the comparer used to compare elements + * @return an Observable that emits the minimum value according to the + * specified comparator + * @throws IllegalArgumentException if the source is empty + * @see MSDN: Observable.Min */ public Observable min(Comparator comparator) { return OperationMinMax.min(this, comparator); } /** - * Returns the elements in an observable sequence with the minimum key value. - * For an empty source, it returns an observable emitting an empty List. + * Returns the items emitted by an Observable sequence with the minimum key + * value. For an empty source, returns an Observable that emits an empty + * List. + *

    + * * - * @param selector - * the key selector function. - * @return an observable emitting a List of the elements with the minimum key value. - * @see MSDN: Observable.MinBy + * @param selector the key selector function + * @return an Observable that emits a List of the items with the minimum key + * value + * @see MSDN: Observable.MinBy */ public > Observable> minBy(Func1 selector) { return OperationMinMax.minBy(this, selector); } /** - * Returns the elements in an observable sequence with the minimum key value according to the specified comparator. - * For an empty source, it returns an observable emitting an empty List. + * Returns the elements emitted by an Observable with the minimum key value + * according to the specified comparator. For an empty source, it returns an + * Observable that emits an empty List. + *

    + * * - * @param selector - * the key selector function. - * @param comparator - * the comparator used to compare key values. - * @return an observable emitting a List of the elements with the minimum key value according to the specified comparator. - * @see MSDN: Observable.MinBy + * @param selector the key selector function + * @param comparator the comparator used to compare key values + * @return an Observable that emits a List of the elements with the minimum + * key value according to the specified comparator + * @see MSDN: Observable.MinBy */ public Observable> minBy(Func1 selector, Comparator comparator) { return OperationMinMax.minBy(this, selector, comparator); } /** - * Returns the maximum element in an observable sequence. - * If there are more than one maximum elements, returns the last one. - * For an empty source, it causes an {@link IllegalArgumentException}. + * Returns the maximum item emitted by an Observable. If there is more + * than one maximum item, it returns the last one. + *

    + * * - * @param source - * an observable sequence to determine the maximum element of. - * @return an observable emitting the maximum element. - * @throws IllegalArgumentException - * if the source is empty. - * @see MSDN: Observable.Max + * @param source an Observable to determine the maximum item of + * @return an Observable that emits the maximum element + * @throws IllegalArgumentException if the source is empty + * @see MSDN: Observable.Max */ public static > Observable max(Observable source) { return OperationMinMax.max(source); } /** - * Returns the maximum element in an observable sequence according to the specified comparator. - * If there are more than one maximum elements, returns the last one. - * For an empty source, it causes an {@link IllegalArgumentException}. + * Returns the maximum item emitted by an Observable according to the + * specified comparator. If there is more than one maximum item, it returns + * the last one. + *

    + * * - * @param comparator - * the comparer used to compare elements. - * @return an observable emitting the maximum value according to the specified comparator. - * @throws IllegalArgumentException - * if the source is empty. - * @see MSDN: Observable.Max + * @param comparator the comparer used to compare items + * @return an Observable that emits the maximum item according to the + * specified comparator + * @throws IllegalArgumentException if the source is empty + * @see MSDN: Observable.Max */ public Observable max(Comparator comparator) { return OperationMinMax.max(this, comparator); } /** - * Returns the elements in an observable sequence with the maximum key value. - * For an empty source, it returns an observable emitting an empty List. + * Returns the items emitted by an Observable with the maximum key value. + * For an empty source, it returns an Observable that emits an empty List. + *

    + * * - * @param selector - * the key selector function. - * @return an observable emitting a List of the elements with the maximum key value. - * @see MSDN: Observable.MaxBy + * @param selector the key selector function + * @return an Observable that emits a List of the items with the maximum key + * value + * @see MSDN: Observable.MaxBy */ public > Observable> maxBy(Func1 selector) { return OperationMinMax.maxBy(this, selector); } /** - * Returns the elements in an observable sequence with the maximum key value according to the specified comparator. - * For an empty source, it returns an observable emitting an empty List. + * Returns the items emitted by an Observable with the maximum key value + * according to the specified comparator. For an empty source, it returns an + * Observable that emits an empty List. + *

    + * * - * @param selector - * the key selector function. - * @param comparator - * the comparator used to compare key values. - * @return an observable emitting a List of the elements with the maximum key value according to the specified comparator. - * @see MSDN: Observable.MaxBy + * @param selector the key selector function + * @param comparator the comparator used to compare key values + * @return an Observable that emits a List of the elements with the maximum + * key value according to the specified comparator + * @see MSDN: Observable.MaxBy */ public Observable> maxBy(Func1 selector, Comparator comparator) { return OperationMinMax.maxBy(this, selector, comparator); } /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the underlying - * Observable that will replay all of its items and notifications to any future {@link Observer}. + * Returns a {@link ConnectableObservable} that shares a single subscription + * to the underlying Observable that will replay all of its items and + * notifications to any future {@link Observer}. *

    * * - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * emit items to its {@link Observer}s + * @return a {@link ConnectableObservable} that upon connection causes the + * source Observable to emit items to its {@link Observer}s */ public ConnectableObservable replay() { return OperationMulticast.multicast(this, ReplaySubject. create()); @@ -3775,78 +3923,97 @@ public ConnectableObservable replay() { *

    * *

    - * If {@link Observer#onError} is invoked the source Observable will be re-subscribed to as many times as defined by retryCount. + * If {@link Observer#onError} is invoked the source Observable will be + * re-subscribed to as many times as defined by retryCount. *

    - * Any {@link Observer#onNext} calls received on each attempt will be emitted and concatenated together. + * Any {@link Observer#onNext} calls received on each attempt will be + * emitted and concatenated together. *

    - * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and - * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. + * For example, if an Observable fails on first time but emits [1, 2] then + * succeeds the second time and emits [1, 2, 3, 4, 5] then the complete + * output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. * - * @param retryCount - * Number of retry attempts before failing. - * @return Observable with retry logic. + * @param retryCount number of retry attempts before failing + * @return an Observable with retry logic */ public Observable retry(int retryCount) { return create(OperationRetry.retry(this, retryCount)); } /** - * Retry subscription to origin Observable whenever onError is called (infinite retry count). + * Retry subscription to origin Observable whenever onError is + * called (infinite retry count). *

    * *

    - * If {@link Observer#onError} is invoked the source Observable will be re-subscribed to. + * If {@link Observer#onError} is invoked the source Observable will be + * re-subscribed to. *

    - * Any {@link Observer#onNext} calls received on each attempt will be emitted and concatenated together. + * Any {@link Observer#onNext} calls received on each attempt will be + * emitted and concatenated together. *

    - * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and - * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. + * For example, if an Observable fails on first time but emits [1, 2] then + * succeeds the second time and emits [1, 2, 3, 4, 5] then the complete + * output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. * - * @return Observable with retry logic. + * @return an Observable with retry logic */ public Observable retry() { return create(OperationRetry.retry(this)); } /** - * This method has similar behavior to {@link #replay} except that this auto-subscribes to - * the source Observable rather than returning a {@link ConnectableObservable}. + * This method has similar behavior to {@link #replay} except that this + * auto-subscribes to the source Observable rather than returning a + * {@link ConnectableObservable}. *

    * *

    - * This is useful when you want an Observable to cache responses and you can't control the - * subscribe/unsubscribe behavior of all the {@link Observer}s. + * This is useful when you want an Observable to cache responses and you + * can't control the subscribe/unsubscribe behavior of all the + * {@link Observer}s. *

    - * NOTE: You sacrifice the ability to unsubscribe from the origin when you use the - * cache() operator so be careful not to use this operator on Observables that - * emit an infinite or very large number of items that will use up memory. + * Note: You sacrifice the ability to unsubscribe from the origin when you + * use the cache() operator so be careful not to use this + * operator on Observables that emit an infinite or very large number of + * items that will use up memory. * - * @return an Observable that when first subscribed to, caches all of its notifications for - * the benefit of subsequent subscribers. + * @return an Observable that, when first subscribed to, caches all of its + * notifications for the benefit of subsequent subscribers. */ public Observable cache() { return create(OperationCache.cache(this)); } /** - * Perform work in parallel by sharding an {@code Observable} on a {@link Schedulers#threadPoolForComputation()} {@link Scheduler} and return an {@code Observable} with the output. + * Perform work in parallel by sharding an {@code Observable} on a + * {@link Schedulers#threadPoolForComputation()} {@link Scheduler} and + * return an {@code Observable} with the output. + *

    + * * - * @param f - * a {@link Func1} that applies Observable operators to {@code Observable} in parallel and returns an {@code Observable} - * @return an Observable with the output of the {@link Func1} executed on a {@link Scheduler} + * @param f a {@link Func1} that applies Observable operators to + * {@code Observable} in parallel and returns an + * {@code Observable} + * @return an Observable with the output of the {@link Func1} executed on a + * {@link Scheduler} */ public Observable parallel(Func1, Observable> f) { return OperationParallel.parallel(this, f); } /** - * Perform work in parallel by sharding an {@code Observable} on a {@link Scheduler} and return an {@code Observable} with the output. + * Perform work in parallel by sharding an {@code Observable} on a + * {@link Scheduler} and return an {@code Observable} with the output. + *

    + * * - * @param f - * a {@link Func1} that applies Observable operators to {@code Observable} in parallel and returns an {@code Observable} - * @param s - * a {@link Scheduler} to perform the work on. - * @return an Observable with the output of the {@link Func1} executed on a {@link Scheduler} + * @param f a {@link Func1} that applies Observable operators to + * {@code Observable} in parallel and returns an + * {@code Observable} + * @param s a {@link Scheduler} to perform the work on + * @return an Observable with the output of the {@link Func1} executed on a + * {@link Scheduler} */ public Observable parallel(final Func1, Observable> f, final Scheduler s) { @@ -3854,20 +4021,25 @@ public Observable parallel(final Func1, Observable> f, f } /** - * Returns a {@link ConnectableObservable}, which waits until its {@link ConnectableObservable#connect connect} method is called before it begins emitting - * items to those {@link Observer}s that have subscribed to it. + * Returns a {@link ConnectableObservable}, which waits until its + * {@link ConnectableObservable#connect connect} method is called before it + * begins emitting items to those {@link Observer}s that have subscribed to + * it. *

    * * - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * emit items to its {@link Observer}s + * @return a {@link ConnectableObservable} that upon connection causes the + * source Observable to emit items to its {@link Observer}s */ public ConnectableObservable publish() { return OperationMulticast.multicast(this, PublishSubject. create()); } /** - * Returns a {@link ConnectableObservable} that shares a single subscription that contains the last notification only. + * Returns a {@link ConnectableObservable} that shares a single subscription + * that contains the last notification only. + *

    + * * * @return a {@link ConnectableObservable} */ @@ -3887,26 +4059,28 @@ public Observable aggregate(Func2 accumulator) { } /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by an Observable into the same function, and so on until all items have been emitted by the - * source Observable, emitting the final result from the final call to your function as its sole - * item. + * Returns an Observable that applies a function of your choosing to the + * first item emitted by a source Observable, then feeds the result of that + * function along with the second item emitted by an Observable into the + * same function, and so on until all items have been emitted by the source + * Observable, emitting the final result from the final call to your + * function as its sole item. *

    * *

    - * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," - * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, - * has an inject method that does a similar operation on lists. - * - * @param initialValue - * the initial (seed) accumulator value - * @param accumulator - * an accumulator function to be invoked on each item emitted by the source - * Observable, the result of which will be used in the next accumulator call - * @return an Observable that emits a single item that is the result of accumulating the output - * from the items emitted by the source Observable - * @see MSDN: Observable.Aggregate + * This technique, which is called "reduce" or "aggregate" here, is + * sometimes called "fold," "accumulate," "compress," or "inject" in other + * programming contexts. Groovy, for instance, has an inject + * method that does a similar operation on lists. + * + * @param initialValue the initial (seed) accumulator value + * @param accumulator an accumulator function to be invoked on each item + * emitted by the source Observable, the result of which + * will be used in the next accumulator call + * @return an Observable that emits a single item that is the result of + * accumulating the output from the items emitted by the source + * Observable + * @see MSDN: Observable.Aggregate * @see Wikipedia: Fold (higher-order function) */ public Observable reduce(R initialValue, Func2 accumulator) { @@ -3925,22 +4099,26 @@ public Observable aggregate(R initialValue, Func2 accumu } /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by an Observable into the same function, and so on until all items have been emitted by the - * source Observable, emitting the result of each of these iterations. + * Returns an Observable that applies a function of your choosing to the + * first item emitted by a source Observable, then feeds the result of that + * function along with the second item emitted by an Observable into the + * same function, and so on until all items have been emitted by the source + * Observable, emitting the result of each of these iterations. *

    * *

    * This sort of function is sometimes called an accumulator. *

    - * Note that when you pass a seed to scan() the resulting Observable will emit - * that seed as its first emitted item. + * Note that when you pass a seed to scan() the resulting + * Observable will emit that seed as its first emitted item. * - * @param accumulator - * an accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be emitted to {@link Observer}s via {@link Observer#onNext onNext} and used in the next accumulator call. - * @return an Observable that emits the results of each call to the accumulator function + * @param accumulator an accumulator function to be invoked on each item + * emitted by the source Observable, whose result will be + * emitted to {@link Observer}s via + * {@link Observer#onNext onNext} and used in the next + * accumulator call + * @return an Observable that emits the results of each call to the + * accumulator function * @see MSDN: Observable.Scan */ public Observable scan(Func2 accumulator) { @@ -3948,60 +4126,58 @@ public Observable scan(Func2 accumulator) { } /** - * Returns an Observable that emits the results of sampling the items emitted by the source - * Observable at a specified time interval. + * Returns an Observable that emits the results of sampling the items + * emitted by the source Observable at a specified time interval. *

    * * - * @param period - * the sampling rate - * @param unit - * the {@link TimeUnit} in which period is defined - * @return an Observable that emits the results of sampling the items emitted by the source - * Observable at the specified time interval + * @param period the sampling rate + * @param unit the {@link TimeUnit} in which period is defined + * @return an Observable that emits the results of sampling the items + * emitted by the source Observable at the specified time interval */ public Observable sample(long period, TimeUnit unit) { return create(OperationSample.sample(this, period, unit)); } /** - * Returns an Observable that emits the results of sampling the items emitted by the source - * Observable at a specified time interval. + * Returns an Observable that emits the results of sampling the items + * emitted by the source Observable at a specified time interval. *

    * * - * @param period - * the sampling rate - * @param unit - * the {@link TimeUnit} in which period is defined - * @param scheduler - * the {@link Scheduler} to use when sampling - * @return an Observable that emits the results of sampling the items emitted by the source - * Observable at the specified time interval + * @param period the sampling rate + * @param unit the {@link TimeUnit} in which period is defined + * @param scheduler the {@link Scheduler} to use when sampling + * @return an Observable that emits the results of sampling the items + * emitted by the source Observable at the specified time interval */ public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { return create(OperationSample.sample(this, period, unit, scheduler)); } /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by an Observable into the same function, and so on until all items have been emitted by the - * source Observable, emitting the result of each of these iterations. + * Returns an Observable that applies a function of your choosing to the + * first item emitted by a source Observable, then feeds the result of that + * function along with the second item emitted by an Observable into the + * same function, and so on until all items have been emitted by the source + * Observable, emitting the result of each of these iterations. *

    * *

    * This sort of function is sometimes called an accumulator. *

    - * Note that when you pass a seed to scan() the resulting Observable will emit - * that seed as its first emitted item. - * - * @param initialValue - * the initial (seed) accumulator value - * @param accumulator - * an accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be emitted to {@link Observer}s via {@link Observer#onNext onNext} and used in the next accumulator call. - * @return an Observable that emits the results of each call to the accumulator function + * Note that when you pass a seed to scan() the resulting + * Observable will emit that seed as its first emitted item. + * + * @param initialValue the initial (seed) accumulator value + * @param accumulator an accumulator function to be invoked on each item + * emitted by the source Observable, whose result will be + * emitted to {@link Observer}s via + * {@link Observer#onNext onNext} and used in the next + * accumulator call + * @return an Observable that emits the results of each call to the + * accumulator function * @see MSDN: Observable.Scan */ public Observable scan(R initialValue, Func2 accumulator) { @@ -4009,45 +4185,48 @@ public Observable scan(R initialValue, Func2 accumulator } /** - * Returns an Observable that emits a Boolean that indicates whether all of the items emitted by - * the source Observable satisfy a condition. + * Returns an Observable that emits a Boolean that indicates whether all of + * the items emitted by the source Observable satisfy a condition. *

    * * - * @param predicate - * a function that evaluates an item and returns a Boolean - * @return an Observable that emits true if all items emitted by the source - * Observable satisfy the predicate; otherwise, false + * @param predicate a function that evaluates an item and returns a Boolean + * @return an Observable that emits true if all items emitted + * by the source Observable satisfy the predicate; otherwise, + * false */ public Observable all(Func1 predicate) { return create(OperationAll.all(this, predicate)); } /** - * Returns an Observable that skips the first num items emitted by the source - * Observable and emits the remainder. + * Returns an Observable that skips the first num items emitted + * by the source Observable and emits the remainder. *

    * *

    - * You can ignore the first num items emitted by an Observable and attend only to - * those items that come after, by modifying the Observable with the skip method. + * You can ignore the first num items emitted by an Observable + * and attend only to those items that come after, by modifying the + * Observable with the skip method. * - * @param num - * the number of items to skip - * @return an Observable that is identical to the source Observable except that it does not - * emit the first num items that the source emits + * @param num the number of items to skip + * @return an Observable that is identical to the source Observable except + * that it does not emit the first num items that the + * source emits */ public Observable skip(int num) { return create(OperationSkip.skip(this, num)); } /** - * Returns an Observable that emits only the very first item emitted by the source Observable. + * Returns an Observable that emits only the very first item emitted by the + * source Observable. *

    * * - * @return an Observable that emits only the very first item from the source, or none if the - * source Observable completes without emitting a single item. + * @return an Observable that emits only the very first item from the + * source, or none if the source Observable completes without + * emitting a single item. * @see MSDN: Observable.First */ public Observable first() { @@ -4055,15 +4234,15 @@ public Observable first() { } /** - * Returns an Observable that emits only the very first item emitted by the source Observable - * that satisfies a given condition. + * Returns an Observable that emits only the very first item emitted by the + * source Observable that satisfies a given condition. *

    * * - * @param predicate - * The condition any source emitted item has to satisfy. - * @return an Observable that emits only the very first item satisfying the given condition from the source, - * or none if the source Observable completes without emitting a single matching item. + * @param predicate the condition any source emitted item has to satisfy + * @return an Observable that emits only the very first item satisfying the + * given condition from the source, or none if the source Observable + * completes without emitting a single matching item. * @see MSDN: Observable.First */ public Observable first(Func1 predicate) { @@ -4071,15 +4250,16 @@ public Observable first(Func1 predicate) { } /** - * Returns an Observable that emits only the very first item emitted by the source Observable, or - * a default value. + * Returns an Observable that emits only the very first item emitted by the + * source Observable, or a default value. *

    * * - * @param defaultValue - * The default value to emit if the source Observable doesn't emit anything. - * @return an Observable that emits only the very first item from the source, or a default value - * if the source Observable completes without emitting a single item. + * @param defaultValue the default value to emit if the source Observable + * doesn't emit anything + * @return an Observable that emits only the very first item from the + * source, or a default value if the source Observable completes + * without emitting a single item * @see MSDN: Observable.FirstOrDefault */ public Observable firstOrDefault(T defaultValue) { @@ -4087,18 +4267,17 @@ public Observable firstOrDefault(T defaultValue) { } /** - * Returns an Observable that emits only the very first item emitted by the source Observable - * that satisfies a given condition, or a default value otherwise. + * Returns an Observable that emits only the very first item emitted by the + * source Observable that satisfies a given condition, or a default value + * otherwise. *

    * * - * @param predicate - * The condition any source emitted item has to satisfy. - * @param defaultValue - * The default value to emit if the source Observable doesn't emit anything that - * satisfies the given condition. - * @return an Observable that emits only the very first item from the source that satisfies the - * given condition, or a default value otherwise. + * @param predicate the condition any source emitted item has to satisfy + * @param defaultValue the default value to emit if the source Observable + * doesn't emit anything that satisfies the given condition + * @return an Observable that emits only the very first item from the source + * that satisfies the given condition, or a default value otherwise * @see MSDN: Observable.FirstOrDefault */ public Observable firstOrDefault(Func1 predicate, T defaultValue) { @@ -4108,78 +4287,83 @@ public Observable firstOrDefault(Func1 predicate, T defau /** * Returns the elements of the specified sequence or the specified default * value in a singleton sequence if the sequence is empty. + *

    + * * - * @param defaultValue - * The value to return if the sequence is empty. - * @return An observable sequence that contains the specified default value - * if the source is empty; otherwise, the elements of the source - * itself. + * @param defaultValue the value to return if the sequence is empty + * @return an Observable that emits the specified default value if the + * source is empty; otherwise, the items emitted by the source * - * @see MSDN: Observable.DefaultIfEmpty + * @see MSDN: Observable.DefaultIfEmpty */ public Observable defaultIfEmpty(T defaultValue) { return create(OperationDefaultIfEmpty.defaultIfEmpty(this, defaultValue)); } /** - * Returns an Observable that emits only the first num items emitted by the source - * Observable. + * Returns an Observable that emits only the first num items + * emitted by the source Observable. *

    * *

    - * This method returns an Observable that will invoke a subscribing {@link Observer}'s {@link Observer#onNext onNext} function a maximum of num times before invoking + * This method returns an Observable that will invoke a subscribing + * {@link Observer}'s {@link Observer#onNext onNext} function a maximum of + * num times before invoking * {@link Observer#onCompleted onCompleted}. * - * @param num - * the number of items to take - * @return an Observable that emits only the first num items from the source - * Observable, or all of the items from the source Observable if that Observable emits - * fewer than num items + * @param num the number of items to emit + * @return an Observable that emits only the first num items + * from the source Observable, or all of the items from the source + * Observable if that Observable emits fewer than num + * items */ public Observable take(final int num) { return create(OperationTake.take(this, num)); } /** - * Returns an Observable that emits items emitted by the source Observable so long as a - * specified condition is true. + * Returns an Observable that emits items emitted by the source Observable + * so long as a specified condition is true. *

    * * - * @param predicate - * a function that evaluates an item emitted by the source Observable and returns a - * Boolean - * @return an Observable that emits the items from the source Observable so long as each item - * satisfies the condition defined by predicate + * @param predicate a function that evaluates an item emitted by the source + * Observable and returns a Boolean + * @return an Observable that emits the items from the source Observable so + * long as each item satisfies the condition defined by + * predicate */ public Observable takeWhile(final Func1 predicate) { return create(OperationTakeWhile.takeWhile(this, predicate)); } /** - * Returns an Observable that emits the items emitted by a source Observable so long as a given - * predicate remains true, where the predicate can operate on both the item and its index - * relative to the complete sequence. + * Returns an Observable that emits the items emitted by a source Observable + * so long as a given predicate remains true, where the predicate can + * operate on both the item and its index relative to the complete sequence. *

    * * - * @param predicate - * a function to test each item emitted by the source Observable for a condition; - * the second parameter of the function represents the index of the source item - * @return an Observable that emits items from the source Observable so long as the predicate - * continues to return true for each item, then completes + * @param predicate a function to test each item emitted by the source + * Observable for a condition; the second parameter of the + * function represents the index of the source item + * @return an Observable that emits items from the source Observable so long + * as the predicate continues to return true for each + * item, then completes */ public Observable takeWhileWithIndex(final Func2 predicate) { return create(OperationTakeWhile.takeWhileWithIndex(this, predicate)); } /** - * Returns an Observable that emits only the very first item emitted by the source Observable. + * Returns an Observable that emits only the very first item emitted by the + * source Observable. *

    * * - * @return an Observable that emits only the very first item from the source, or none if the - * source Observable completes without emitting a single item. + * @return an Observable that emits only the very first item from the + * source, or none if the source Observable completes without + * emitting a single item. * @see MSDN: Observable.First * @see #first() */ @@ -4188,15 +4372,15 @@ public Observable takeFirst() { } /** - * Returns an Observable that emits only the very first item emitted by the source Observable - * that satisfies a given condition. + * Returns an Observable that emits only the very first item emitted by the + * source Observable that satisfies a given condition. *

    * * - * @param predicate - * The condition any source emitted item has to satisfy. - * @return an Observable that emits only the very first item satisfying the given condition from the source, - * or none if the source Observable completes without emitting a single matching item. + * @param predicate the condition any source emitted item has to satisfy + * @return an Observable that emits only the very first item satisfying the + * given condition from the source, or none if the source Observable + * completes without emitting a single matching item. * @see MSDN: Observable.First * @see #first(Func1) */ @@ -4205,51 +4389,50 @@ public Observable takeFirst(Func1 predicate) { } /** - * Returns an Observable that emits only the last count items emitted by the source - * Observable. + * Returns an Observable that emits only the last count items + * emitted by the source Observable. *

    * * - * @param count - * the number of items to emit from the end of the sequence emitted by the source - * Observable - * @return an Observable that emits only the last count items emitted by the source - * Observable + * @param count the number of items to emit from the end of the sequence + * emitted by the source Observable + * @return an Observable that emits only the last count items + * emitted by the source Observable */ public Observable takeLast(final int count) { return create(OperationTakeLast.takeLast(this, count)); } /** - * Returns an Observable that emits the items from the source Observable only until the - * other Observable emits an item. + * Returns an Observable that emits the items from the source Observable + * only until the other Observable emits an item. *

    * * - * @param other - * the Observable whose first emitted item will cause takeUntil to stop - * emitting items from the source Observable - * @param - * the type of items emitted by other - * @return an Observable that emits the items of the source Observable until such time as - * other emits its first item + * @param other the Observable whose first emitted item will cause + * takeUntil to stop emitting items from the + * source Observable + * @param the type of items emitted by other + * @return an Observable that emits the items of the source Observable until + * such time as other emits its first item */ public Observable takeUntil(Observable other) { return OperationTakeUntil.takeUntil(this, other); } /** - * Returns an Observable that bypasses all items from the source Observable as long as the specified - * condition holds true. Emits all further source items as soon as the condition becomes false. + * Returns an Observable that bypasses all items from the source Observable + * as long as the specified condition holds true, but emits all further + * source items as soon as the condition becomes false. *

    * * - * @param predicate - * A function to test each item emitted from the source Observable for a condition. - * It receives the emitted item as first parameter and the index of the emitted item as - * second parameter. - * @return an Observable that emits all items from the source Observable as soon as the condition - * becomes false. + * @param predicate a function to test each item emitted from the source + * Observable for a condition. It receives the emitted item + * as the first parameter and the index of the emitted item + * as a second parameter. + * @return an Observable that emits all items from the source Observable as + * soon as the condition becomes false * @see MSDN: Observable.SkipWhile */ public Observable skipWhileWithIndex(Func2 predicate) { @@ -4257,15 +4440,16 @@ public Observable skipWhileWithIndex(Func2 predi } /** - * Returns an Observable that bypasses all items from the source Observable as long as the specified - * condition holds true. Emits all further source items as soon as the condition becomes false. + * Returns an Observable that bypasses all items from the source Observable + * as long as the specified condition holds true, but emits all further + * source items as soon as the condition becomes false. *

    * * - * @param predicate - * A function to test each item emitted from the source Observable for a condition. - * @return an Observable that emits all items from the source Observable as soon as the condition - * becomes false. + * @param predicate a function to test each item emitted from the source + * Observable for a condition + * @return an Observable that emits all items from the source Observable as + * soon as the condition becomes false * @see MSDN: Observable.SkipWhile */ public Observable skipWhile(Func1 predicate) { @@ -4273,88 +4457,96 @@ public Observable skipWhile(Func1 predicate) { } /** - * Bypasses a specified number of elements at the end of an observable + * Bypasses a specified number of items at the end of an Observable * sequence. *

    * This operator accumulates a queue with a length enough to store the first - * count elements. As more elements are received, elements are taken from - * the front of the queue and produced on the result sequence. This causes - * elements to be delayed. + * count items. As more items are received, items are taken + * from the front of the queue and produced on the result sequence. This + * causes elements to be delayed. + *

    + * * - * @param count - * number of elements to bypass at the end of the source - * sequence. - * @return An observable sequence containing the source sequence elements - * except for the bypassed ones at the end. + * @param count number of elements to bypass at the end of the source + * sequence + * @return an Observable sequence emitting the source sequence items + * except for the bypassed ones at the end * - * @throws IndexOutOfBoundsException - * count is less than zero. + * @throws IndexOutOfBoundsException if count is less than zero * - * @see MSDN: Observable.SkipLast + * @see MSDN: Observable.SkipLast */ public Observable skipLast(int count) { return create(OperationSkipLast.skipLast(this, count)); } /** - * Returns an Observable that emits a single item, a list composed of all the items emitted by - * the source Observable. + * Returns an Observable that emits a single item, a list composed of all + * the items emitted by the source Observable. *

    * *

    - * Normally, an Observable that returns multiple items will do so by invoking its {@link Observer}'s {@link Observer#onNext onNext} method for each such item. You can change - * this behavior, instructing the Observable to compose a list of all of these items and then to - * invoke the Observer's onNext function once, passing it the entire list, by - * calling the Observable's toList method prior to calling its {@link #subscribe} method. + * Normally, an Observable that returns multiple items will do so by + * invoking its {@link Observer}'s {@link Observer#onNext onNext} method for + * each such item. You can change this behavior, instructing the Observable + * to compose a list of all of these items and then to invoke the Observer's + * onNext function once, passing it the entire list, by calling + * the Observable's toList method prior to calling its + * {@link #subscribe} method. *

    - * Be careful not to use this operator on Observables that emit infinite or very large numbers - * of items, as you do not have the option to unsubscribe. + * Be careful not to use this operator on Observables that emit infinite or + * very large numbers of items, as you do not have the option to + * unsubscribe. * - * @return an Observable that emits a single item: a List containing all of the items emitted by - * the source Observable. + * @return an Observable that emits a single item: a List containing all of + * the items emitted by the source Observable. */ public Observable> toList() { return create(OperationToObservableList.toObservableList(this)); } /** - * Return an Observable that emits the items emitted by the source Observable, in a sorted - * order (each item emitted by the Observable must implement {@link Comparable} with respect to - * all other items in the sequence). + * Return an Observable that emits the items emitted by the source + * Observable, in a sorted order (each item emitted by the Observable must + * implement {@link Comparable} with respect to all other items in the + * sequence). *

    * * - * @throws ClassCastException - * if any item emitted by the Observable does not implement {@link Comparable} with - * respect to all other items emitted by the Observable - * @return an Observable that emits the items from the source Observable in sorted order + * @throws ClassCastException if any item emitted by the Observable does not + * implement {@link Comparable} with respect to + * all other items emitted by the Observable + * @return an Observable that emits the items from the source Observable in + * sorted order */ public Observable> toSortedList() { return create(OperationToObservableSortedList.toSortedList(this)); } /** - * Return an Observable that emits the items emitted by the source Observable, in a sorted - * order based on a specified comparison function + * Return an Observable that emits the items emitted by the source + * Observable, in a sorted order based on a specified comparison function *

    * * - * @param sortFunction - * a function that compares two items emitted by the source Observable and returns - * an Integer that indicates their sort order - * @return an Observable that emits the items from the source Observable in sorted order + * @param sortFunction a function that compares two items emitted by the + * source Observable and returns an Integer that + * indicates their sort order + * @return an Observable that emits the items from the source Observable in + * sorted order */ public Observable> toSortedList(Func2 sortFunction) { return create(OperationToObservableSortedList.toSortedList(this, sortFunction)); } /** - * Emit a specified set of items before beginning to emit items from the source Observable. + * Emit a specified set of items before beginning to emit items from the + * source Observable. *

    * * - * @param values - * Iterable of the items you want the modified Observable to emit first + * @param values Iterable of the items you want the modified Observable to + * emit first * @return an Observable that exhibits the modified behavior */ public Observable startWith(Iterable values) { @@ -4362,12 +4554,12 @@ public Observable startWith(Iterable values) { } /** - * Emit a specified set of items before beginning to emit items from the source Observable. + * Emit a specified item before beginning to emit items from the source + * Observable. *

    * * - * @param t1 - * item to include + * @param t1 item to emit * @return an Observable that exhibits the modified behavior */ public Observable startWith(T t1) { @@ -4375,14 +4567,13 @@ public Observable startWith(T t1) { } /** - * Emit a specified set of items before beginning to emit items from the source Observable. + * Emit a specified set of items before beginning to emit items from the + * source Observable. *

    * * - * @param t1 - * item to include - * @param t2 - * item to include + * @param t1 first item to emit + * @param t2 second item to emit * @return an Observable that exhibits the modified behavior */ public Observable startWith(T t1, T t2) { @@ -4390,16 +4581,14 @@ public Observable startWith(T t1, T t2) { } /** - * Emit a specified set of items before beginning to emit items from the source Observable. + * Emit a specified set of items before beginning to emit items from the + * source Observable. *

    * * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit * @return an Observable that exhibits the modified behavior */ public Observable startWith(T t1, T t2, T t3) { @@ -4407,18 +4596,15 @@ public Observable startWith(T t1, T t2, T t3) { } /** - * Emit a specified set of items before beginning to emit items from the source Observable. + * Emit a specified set of items before beginning to emit items from the + * source Observable. *

    * * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit * @return an Observable that exhibits the modified behavior */ public Observable startWith(T t1, T t2, T t3, T t4) { @@ -4426,20 +4612,16 @@ public Observable startWith(T t1, T t2, T t3, T t4) { } /** - * Emit a specified set of items before beginning to emit items from the source Observable. + * Emit a specified set of items before beginning to emit items from the + * source Observable. *

    * * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include - * @param t5 - * item to include + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit + * @param t5 fifth item to emit * @return an Observable that exhibits the modified behavior */ public Observable startWith(T t1, T t2, T t3, T t4, T t5) { @@ -4447,22 +4629,17 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5) { } /** - * Emit a specified set of items before beginning to emit items from the source Observable. + * Emit a specified set of items before beginning to emit items from the + * source Observable. *

    * * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include - * @param t5 - * item to include - * @param t6 - * item to include + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit + * @param t5 fifth item to emit + * @param t6 sixth item to emit * @return an Observable that exhibits the modified behavior */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { @@ -4470,24 +4647,18 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { } /** - * Emit a specified set of items before beginning to emit items from the source Observable. + * Emit a specified set of items before beginning to emit items from the + * source Observable. *

    * * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include - * @param t5 - * item to include - * @param t6 - * item to include - * @param t7 - * item to include + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit + * @param t5 fifth item to emit + * @param t6 sixth item to emit + * @param t7 seventh item to emit * @return an Observable that exhibits the modified behavior */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { @@ -4495,26 +4666,19 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { } /** - * Emit a specified set of items before beginning to emit items from the source Observable. + * Emit a specified set of items before beginning to emit items from the + * source Observable. *

    * * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include - * @param t5 - * item to include - * @param t6 - * item to include - * @param t7 - * item to include - * @param t8 - * item to include + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit + * @param t5 fifth item to emit + * @param t6 sixth item to emit + * @param t7 seventh item to emit + * @param t8 eighth item to emit * @return an Observable that exhibits the modified behavior */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { @@ -4522,28 +4686,20 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { } /** - * Emit a specified set of items before beginning to emit items from the source Observable. + * Emit a specified set of items before beginning to emit items from the + * source Observable. *

    * * - * @param t1 - * item to include - * @param t2 - * item to include - * @param t3 - * item to include - * @param t4 - * item to include - * @param t5 - * item to include - * @param t6 - * item to include - * @param t7 - * item to include - * @param t8 - * item to include - * @param t9 - * item to include + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit + * @param t5 fifth item to emit + * @param t6 sixth item to emit + * @param t7 seventh item to emit + * @param t8 eighth item to emit + * @param t9 ninth item to emit * @return an Observable that exhibits the modified behavior */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { @@ -4551,70 +4707,79 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T } /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. + * Groups the items emitted by an Observable according to a specified + * criterion, and emits these grouped items as {@link GroupedObservable}s, + * one GroupedObservable per group. *

    * * - * @param keySelector - * a function that extracts the key from an item - * @param elementSelector - * a function to map a source item to an item in a {@link GroupedObservable} - * @param - * the key type - * @param - * the type of items emitted by the resulting {@link GroupedObservable}s - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value + * @param keySelector a function that extracts the key from an item + * @param elementSelector a function to map a source item to an item in a + * {@link GroupedObservable} + * @param the key type + * @param the type of items emitted by the resulting + * {@link GroupedObservable}s + * @return an Observable that emits {@link GroupedObservable}s, each of + * which corresponds to a unique key value and emits items + * representing items from the source Observable that share that key + * value */ public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) { return create(OperationGroupBy.groupBy(this, keySelector, elementSelector)); } /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. + * Groups the items emitted by an Observable according to a specified + * criterion, and emits these grouped items as {@link GroupedObservable}s, + * one GroupedObservable per group. *

    * * - * @param keySelector - * a function that extracts the key for each item - * @param - * the key type - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value + * @param keySelector a function that extracts the key for each item + * @param the key type + * @return an Observable that emits {@link GroupedObservable}s, each of + * which corresponds to a unique key value and emits items + * representing items from the source Observable that share that key + * value */ public Observable> groupBy(final Func1 keySelector) { return create(OperationGroupBy.groupBy(this, keySelector)); } /** - * Returns an {@link Observable} that emits true if the source {@link Observable} is empty, otherwise false. + * Returns an {@link Observable} that emits true if the source + * {@link Observable} is empty, otherwise false. + *

    + * In Rx.Net this is negated as the any operator but renamed in + * RxJava to better match Java naming idioms. *

    - * In Rx.Net this is negated as the any operator but renamed in RxJava to better match Java naming idioms. + * * - * @return An Observable that emits Boolean. - * @see MSDN: Observable.Any + * @return an Observable that emits a Boolean + * @see MSDN: Observable.Any */ public Observable isEmpty() { return create(OperationAny.isEmpty(this)); } /** - * Returns an {@link Observable} that emits the last element of the source or an IllegalArgumentException if the source {@link Observable} is empty. + * Returns an {@link Observable} that emits the last item emitted by the + * source or an IllegalArgumentException if the source + * {@link Observable} is empty. + *

    + * * - * @return Observable + * @return */ public Observable last() { return create(OperationLast.last(this)); } /** - * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking - * operators). + * Converts an Observable into a {@link BlockingObservable} (an Observable + * with blocking operators). * + * @return * @see Blocking Observable Operators */ public BlockingObservable toBlockingObservable() { @@ -4622,32 +4787,29 @@ public BlockingObservable toBlockingObservable() { } /** - * Converts the elements of an observable sequence to the specified type. - * - * @param klass - * The target class type which the elements will be converted to. - * - * @return An observable sequence that contains each element of the source - * sequence converted to the specified type. + * Converts the items emitted by an Observable to the specified type. + *

    + * * - * @see MSDN: Observable.Cast + * @param klass the target class type which the items will be converted to + * @return an Observable that emits each item from the source Observable + * converted to the specified type + * @see MSDN: Observable.Cast */ public Observable cast(final Class klass) { return create(OperationCast.cast(this, klass)); } /** - * Filters the elements of an observable sequence based on the specified - * type. - * - * @param klass - * The class type to filter the elements in the source sequence - * on. - * - * @return An observable sequence that contains elements from the input - * sequence of type klass. + * Filters the items emitted by an Observable based on the specified type. + *

    + * * - * @see MSDN: Observable.OfType + * @param klass the class type to filter the items emitted by the source + * Observable + * @return an Observable that emits items from the source Observable of + * type klass. + * @see MSDN: Observable.OfType */ public Observable ofType(final Class klass) { return filter(new Func1() { @@ -4658,11 +4820,15 @@ public Boolean call(T t) { } /** - * Ignores all values in an observable sequence and only calls onCompleted or onError method. + * Ignores all items emitted by an Observable and only calls + * onCompleted or onError. + *

    + * * - * @return An empty observable sequence that only call onCompleted, or onError method. + * @return an empty Observable that only calls onCompleted or + * onError * - * @see MSDN: Observable.IgnoreElements + * @see MSDN: Observable.IgnoreElements */ public Observable ignoreElements() { return filter(alwaysFalse()); @@ -4673,14 +4839,15 @@ public Observable ignoreElements() { * using the specified scheduler to run timeout timers. If the next element * isn't received within the specified timeout duration starting from its * predecessor, a TimeoutException is propagated to the observer. + *

    + * * - * @param timeout - * Maximum duration between values before a timeout occurs. - * @param timeUnit - * The unit of time which applies to the "timeout" argument. - * - * @return The source sequence with a TimeoutException in case of a timeout. - * @see MSDN: Observable.Timeout + * @param timeout maximum duration between values before a timeout occurs + * @param timeUnit the unit of time which applies to the + * timeout argument. + * @return the source Observable with a TimeoutException in + * case of a timeout + * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit) { return create(OperationTimeout.timeout(this, timeout, timeUnit)); @@ -4692,16 +4859,16 @@ public Observable timeout(long timeout, TimeUnit timeUnit) { * isn't received within the specified timeout duration starting from its * predecessor, the other observable sequence is used to produce future * messages from that point on. + *

    + * * - * @param timeout - * Maximum duration between values before a timeout occurs. - * @param timeUnit - * The unit of time which applies to the "timeout" argument. - * @param other - * Sequence to return in case of a timeout. - * - * @return The source sequence switching to the other sequence in case of a timeout. - * @see MSDN: Observable.Timeout + * @param timeout maximum duration between values before a timeout occurs + * @param timeUnit the unit of time which applies to the + * timeout argument + * @param other sequence to return in case of a timeout + * @return the source sequence switching to the other sequence in case of a + * timeout + * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Observable other) { return create(OperationTimeout.timeout(this, timeout, timeUnit, other)); @@ -4712,16 +4879,16 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Observable + * * - * @param timeout - * Maximum duration between values before a timeout occurs. - * @param timeUnit - * The unit of time which applies to the "timeout" argument. - * @param scheduler - * Scheduler to run the timeout timers on. - * - * @return The source sequence with a TimeoutException in case of a timeout. - * @see MSDN: Observable.Timeout + * @param timeout maximum duration between values before a timeout occurs + * @param timeUnit the unit of time which applies to the + * timeout argument + * @param scheduler Scheduler to run the timeout timers on + * @return the source sequence with a TimeoutException in case + * of a timeout + * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { return create(OperationTimeout.timeout(this, timeout, timeUnit, scheduler)); @@ -4733,90 +4900,91 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler schedule * isn't received within the specified timeout duration starting from its * predecessor, the other observable sequence is used to produce future * messages from that point on. + *

    + * * - * @param timeout - * Maximum duration between values before a timeout occurs. - * @param timeUnit - * The unit of time which applies to the "timeout" argument. - * @param other - * Sequence to return in case of a timeout. - * @param scheduler - * Scheduler to run the timeout timers on. - * - * @return The source sequence switching to the other sequence in case of a - * timeout. - * @see MSDN: Observable.Timeout + * @param timeout maximum duration between values before a timeout occurs + * @param timeUnit the unit of time which applies to the + * timeout argument + * @param other sequence to return in case of a timeout + * @param scheduler Scheduler to run the timeout timers on + * @return the source sequence switching to the other sequence in case of a + * timeout + * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Observable other, Scheduler scheduler) { return create(OperationTimeout.timeout(this, timeout, timeUnit, other, scheduler)); } /** - * Records the time interval between consecutive elements in an observable sequence. + * Records the time interval between consecutive items emitted by an + * Observable. + *

    + * * - * @return An observable sequence with time interval information on elements. - * @see MSDN: Observable.TimeInterval + * @return an Observable that emits time interval information items + * @see MSDN: Observable.TimeInterval */ public Observable> timeInterval() { return create(OperationTimeInterval.timeInterval(this)); } /** - * Records the time interval between consecutive elements in an observable - * sequence, using the specified scheduler to compute time intervals. - * - * @param scheduler - * Scheduler used to compute time intervals. + * Records the time interval between consecutive items emitted by an + * Observable, using the specified Scheduler to compute time intervals. + *

    + * * - * @return An observable sequence with time interval information on elements. - * @see MSDN: Observable.TimeInterval + * @param scheduler Scheduler used to compute time intervals + * @return an Observable that emits time interval information items + * @see MSDN: Observable.TimeInterval */ public Observable> timeInterval(Scheduler scheduler) { return create(OperationTimeInterval.timeInterval(this, scheduler)); } /** - * Constructs an observable sequence that depends on a resource object. + * Constructs an Observable that depends on a resource object. + *

    + * * - * @param resourceFactory - * The factory function to obtain a resource object. - * @param observableFactory - * The factory function to obtain an observable sequence that depends on the obtained resource. - * @return - * The observable sequence whose lifetime controls the lifetime of the dependent resource object. - * @see MSDN: Observable.Using + * @param resourceFactory the factory function to obtain a resource object + * that depends on the Observable + * @param observableFactory the factory function to obtain an Observable + * @return the Observable whose lifetime controls the lifetime of the + * dependent resource object + * @see MSDN: Observable.Using */ public static Observable using(Func0 resourceFactory, Func1> observableFactory) { return create(OperationUsing.using(resourceFactory, observableFactory)); } /** - * Propagates the observable sequence that reacts first. + * Propagates the Observable sequence that reacts first. + *

    + * * - * @param o1 - * an observable competing to react first. - * @param o2 - * an observable competing to react first. - * @return - * an observable sequence that surfaces any of the given sequences, whichever reacted first. - * @see MSDN: Observable.Amb + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2) { return create(OperationAmb.amb(o1, o2)); } /** - * Propagates the observable sequence that reacts first. + * Propagates the Observable sequence that reacts first. + *

    + * * - * @param o1 - * an observable competing to react first. - * @param o2 - * an observable competing to react first. - * @param o3 - * an observable competing to react first. - * @return - * an observable sequence that surfaces any of the given sequences, whichever reacted first. - * @see MSDN: Observable.Amb + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3) { return create(OperationAmb.amb(o1, o2, o3)); @@ -4824,39 +4992,34 @@ public static Observable amb(Observable o1, Observable + * * - * @param o1 - * an observable competing to react first. - * @param o2 - * an observable competing to react first. - * @param o3 - * an observable competing to react first. - * @param o4 - * an observable competing to react first. - * @return - * an observable sequence that surfaces any of the given sequences, whichever reacted first. - * @see MSDN: Observable.Amb + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4) { return create(OperationAmb.amb(o1, o2, o3, o4)); } /** - * Propagates the observable sequence that reacts first. + * Propagates the Observable sequence that reacts first. + *

    + * * - * @param o1 - * an observable competing to react first. - * @param o2 - * an observable competing to react first. - * @param o3 - * an observable competing to react first. - * @param o4 - * an observable competing to react first. - * @param o5 - * an observable competing to react first. - * @return - * an observable sequence that surfaces any of the given sequences, whichever reacted first. - * @see MSDN: Observable.Amb + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @param o5 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5) { return create(OperationAmb.amb(o1, o2, o3, o4, o5)); @@ -4864,22 +5027,18 @@ public static Observable amb(Observable o1, Observable + * * - * @param o1 - * an observable competing to react first. - * @param o2 - * an observable competing to react first. - * @param o3 - * an observable competing to react first. - * @param o4 - * an observable competing to react first. - * @param o5 - * an observable competing to react first. - * @param o6 - * an observable competing to react first. - * @return - * an observable sequence that surfaces any of the given sequences, whichever reacted first. - * @see MSDN: Observable.Amb + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @param o5 an Observable competing to react first + * @param o6 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6) { return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6)); @@ -4887,24 +5046,19 @@ public static Observable amb(Observable o1, Observable + * * - * @param o1 - * an observable competing to react first. - * @param o2 - * an observable competing to react first. - * @param o3 - * an observable competing to react first. - * @param o4 - * an observable competing to react first. - * @param o5 - * an observable competing to react first. - * @param o6 - * an observable competing to react first. - * @param o7 - * an observable competing to react first. - * @return - * an observable sequence that surfaces any of the given sequences, whichever reacted first. - * @see MSDN: Observable.Amb + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @param o5 an Observable competing to react first + * @param o6 an Observable competing to react first + * @param o7 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7) { return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7)); @@ -4912,26 +5066,20 @@ public static Observable amb(Observable o1, Observable + * * - * @param o1 - * an observable competing to react first. - * @param o2 - * an observable competing to react first. - * @param o3 - * an observable competing to react first. - * @param o4 - * an observable competing to react first. - * @param o5 - * an observable competing to react first. - * @param o6 - * an observable competing to react first. - * @param o7 - * an observable competing to react first. - * @param o8 - * an observable competing to react first. - * @return - * an observable sequence that surfaces any of the given sequences, whichever reacted first. - * @see MSDN: Observable.Amb + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @param o5 an Observable competing to react first + * @param o6 an Observable competing to react first + * @param o7 an Observable competing to react first + * @param o8 an observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8) { return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8)); @@ -4939,28 +5087,21 @@ public static Observable amb(Observable o1, Observable + * * - * @param o1 - * an observable competing to react first. - * @param o2 - * an observable competing to react first. - * @param o3 - * an observable competing to react first. - * @param o4 - * an observable competing to react first. - * @param o5 - * an observable competing to react first. - * @param o6 - * an observable competing to react first. - * @param o7 - * an observable competing to react first. - * @param o8 - * an observable competing to react first. - * @param o9 - * an observable competing to react first. - * @return - * an observable sequence that surfaces any of the given sequences, whichever reacted first. - * @see MSDN: Observable.Amb + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @param o5 an Observable competing to react first + * @param o6 an Observable competing to react first + * @param o7 an Observable competing to react first + * @param o8 an Observable competing to react first + * @param o9 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9) { return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8, o9)); @@ -4968,13 +5109,13 @@ public static Observable amb(Observable o1, Observable + * * - * @param sources - * observable sources competing to react first. - * - * @return - * an observable sequence that surfaces any of the given sequences, whichever reacted first. - * @see MSDN: Observable.Amb + * @param sources Observable sources competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see MSDN: Observable.Amb */ public static Observable amb(Iterable> sources) { return create(OperationAmb.amb(sources)); @@ -4982,28 +5123,28 @@ public static Observable amb(Iterable> /** - * Invokes an action for each element in the observable sequence. - * - * @param observer - * The action to invoke for each element in the source sequence. + * Invokes an action for each item emitted by the Observable. + *

    + * * - * @return - * The source sequence with the side-effecting behavior applied. - * @see MSDN: Observable.Do + * @param observer the action to invoke for each item emitted in the source + * sequence + * @return the source sequence with the side-effecting behavior applied + * @see MSDN: Observable.Do */ public Observable doOnEach(Observer observer) { return create(OperationDoOnEach.doOnEach(this, observer)); } /** - * Invokes an action for each element in the observable sequence. - * - * @param onNext - * The action to invoke for each element in the source sequence. + * Invokes an action for each item emitted by an Observable. + *

    + * * - * @return - * The source sequence with the side-effecting behavior applied. - * @see MSDN: Observable.Do + * @param onNext the action to invoke for each item in the source + * sequence + * @return the source sequence with the side-effecting behavior applied + * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext) { Observer observer = new Observer() { @@ -5025,14 +5166,13 @@ public void onNext(T args) { } /** - * Invokes an action if onError is emitted from the observable sequence. - * - * @param onError - * The action to invoke if onError is invoked. + * Invokes an action if onError is called from the Observable. + *

    + * * - * @return - * The source sequence with the side-effecting behavior applied. - * @see MSDN: Observable.Do + * @param onError the action to invoke if onError is invoked + * @return the source sequence with the side-effecting behavior applied + * @see MSDN: Observable.Do */ public Observable doOnError(final Action1 onError) { Observer observer = new Observer() { @@ -5054,14 +5194,15 @@ public void onNext(T args) { } } /** - * Invokes an action when onCompleted is emitted from the observable sequence. - * - * @param onCompleted - * The action to invoke when onCompleted is emitted. + * Invokes an action when onCompleted is called by the + * Observable. + *

    + * * - * @return - * The source sequence with the side-effecting behavior applied. - * @see MSDN: Observable.Do + * @param onCompleted the action to invoke when onCompleted is + * called + * @return the source sequence with the side-effecting behavior applied + * @see MSDN: Observable.Do */ public Observable doOnCompleted(final Action0 onCompleted) { Observer observer = new Observer() { @@ -5083,16 +5224,13 @@ public void onNext(T args) { } } /** - * Invokes an action for each element in the observable sequence. - * - * @param onNext - * The action to invoke for each element in the source sequence. - * @param onError - * The action to invoke when the source sequence calls onError. + * Invokes an action for each item emitted by an Observable. * - * @return - * The source sequence with the side-effecting behavior applied. - * @see MSDN: Observable.Do + * @param onNext the action to invoke for each item in the source sequence + * @param onError the action to invoke when the source Observable calls + * onError + * @return the source sequence with the side-effecting behavior applied + * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext, final Action1 onError) { Observer observer = new Observer() { @@ -5117,18 +5255,15 @@ public void onNext(T args) { /** - * Invokes an action for each element in the observable sequence. + * Invokes an action for each item emitted by an Observable. * - * @param onNext - * The action to invoke for each element in the source sequence. - * @param onError - * The action to invoke when the source sequence calls onError. - * @param onCompleted - * The action to invoke when the source sequence is completed. - * - * @return - * The source sequence with the side-effecting behavior applied. - * @see MSDN: Observable.Do + * @param onNext the action to invoke for each item in the source sequence + * @param onError the action to invoke when the source Observable calls + * onError + * @param onCompleted the action to invoke when the source Observable calls + * onCompleted + * @return the source sequence with the side-effecting behavior applied + * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext, final Action1 onError, final Action0 onCompleted) { Observer observer = new Observer() { @@ -5154,14 +5289,20 @@ public void onNext(T args) { } /** - * Whether a given {@link Function} is an internal implementation inside rx.* packages or not. + * Whether a given {@link Function} is an internal implementation inside + * rx.* packages or not. *

    - * For why this is being used see https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" + * For why this is being used see + * https://github.com/Netflix/RxJava/issues/216 for discussion on + * "Guideline 6.4: Protect calls to user code from within an operator" * - * NOTE: If strong reasons for not depending on package names comes up then the implementation of this method can change to looking for a marker interface. + * Note: If strong reasons for not depending on package names comes up then + * the implementation of this method can change to looking for a marker + * interface. * * @param o - * @return {@code true} if the given function is an internal implementation, and {@code false} otherwise. + * @return {@code true} if the given function is an internal implementation, + * and {@code false} otherwise. */ private boolean isInternalImplementation(Object o) { if (o == null) { From b9d29cee399ed33ce57dbc2aa1be2a7b06000e58 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Fri, 15 Nov 2013 18:55:01 -0800 Subject: [PATCH 263/333] BugFix: AsyncSubject - it was not emitting values to observers that subscribed after onCompleted/onError --- .../main/java/rx/subjects/AsyncSubject.java | 121 ++++++++++---- .../java/rx/subjects/AsyncSubjectTest.java | 150 ++++++++++++++++++ 2 files changed, 239 insertions(+), 32 deletions(-) diff --git a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java index 183efdfda9..3f7f5d0a0f 100644 --- a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java @@ -18,10 +18,13 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; +import rx.Notification; import rx.Observer; import rx.Subscription; import rx.operators.SafeObservableSubscription; +import rx.subscriptions.Subscriptions; /** * Subject that publishes only the last event to each {@link Observer} that has subscribed when the @@ -60,61 +63,115 @@ public class AsyncSubject extends Subject { * @return a new AsyncSubject */ public static AsyncSubject create() { - final ConcurrentHashMap> observers = new ConcurrentHashMap>(); + final AsyncSubjectState state = new AsyncSubjectState(); OnSubscribeFunc onSubscribe = new OnSubscribeFunc() { @Override public Subscription onSubscribe(Observer observer) { - final SafeObservableSubscription subscription = new SafeObservableSubscription(); - - subscription.wrap(new Subscription() { - @Override - public void unsubscribe() { - // on unsubscribe remove it from the map of outbound observers to notify - observers.remove(subscription); + /* + * Subscription needs to be synchronized with terminal states to ensure + * race conditions are handled. When subscribing we must make sure + * onComplete/onError is correctly emitted to all observers, even if it + * comes in while the onComplete/onError is being propagated. + */ + state.SUBSCRIPTION_LOCK.lock(); + try { + if (state.completed.get()) { + emitNotificationToObserver(state, observer); + return Subscriptions.empty(); + } else { + // the subject is not completed so we subscribe + final SafeObservableSubscription subscription = new SafeObservableSubscription(); + + subscription.wrap(new Subscription() { + @Override + public void unsubscribe() { + // on unsubscribe remove it from the map of outbound observers to notify + state.observers.remove(subscription); + } + }); + + // on subscribe add it to the map of outbound observers to notify + state.observers.put(subscription, observer); + + return subscription; } - }); + } finally { + state.SUBSCRIPTION_LOCK.unlock(); + } - // on subscribe add it to the map of outbound observers to notify - observers.put(subscription, observer); - return subscription; } + }; - return new AsyncSubject(onSubscribe, observers); + return new AsyncSubject(onSubscribe, state); } - private final ConcurrentHashMap> observers; - private final AtomicReference currentValue; - private final AtomicBoolean hasValue = new AtomicBoolean(); + private static void emitNotificationToObserver(final AsyncSubjectState state, Observer observer) { + Notification finalValue = state.currentValue.get(); + + // if null that means onNext was never invoked (no Notification set) + if (finalValue != null) { + if (finalValue.isOnNext()) { + observer.onNext(finalValue.getValue()); + } else if (finalValue.isOnError()) { + observer.onError(finalValue.getThrowable()); + } + } + observer.onCompleted(); + } - protected AsyncSubject(OnSubscribeFunc onSubscribe, ConcurrentHashMap> observers) { + /** + * State externally constructed and passed in so the onSubscribe function has access to it. + * + * @param + */ + private static class AsyncSubjectState { + private final ConcurrentHashMap> observers = new ConcurrentHashMap>(); + private final AtomicReference> currentValue = new AtomicReference>(); + private final AtomicBoolean completed = new AtomicBoolean(); + private final ReentrantLock SUBSCRIPTION_LOCK = new ReentrantLock(); + } + + private final AsyncSubjectState state; + + protected AsyncSubject(OnSubscribeFunc onSubscribe, AsyncSubjectState state) { super(onSubscribe); - this.observers = observers; - this.currentValue = new AtomicReference(); + this.state = state; } @Override public void onCompleted() { - T finalValue = currentValue.get(); - for (Observer observer : observers.values()) { - if (hasValue.get()) { - observer.onNext(finalValue); - } - observer.onCompleted(); - } + terminalState(); } @Override public void onError(Throwable e) { - for (Observer observer : observers.values()) { - observer.onError(e); - } + state.currentValue.set(new Notification(e)); + terminalState(); } @Override - public void onNext(T args) { - hasValue.set(true); - currentValue.set(args); + public void onNext(T v) { + state.currentValue.set(new Notification(v)); + } + + private void terminalState() { + /* + * We can not allow new subscribers to be added while we execute the terminal state. + */ + state.SUBSCRIPTION_LOCK.lock(); + try { + if (state.completed.compareAndSet(false, true)) { + for (Subscription s : state.observers.keySet()) { + // emit notifications to this observer + emitNotificationToObserver(state, state.observers.get(s)); + // remove the subscription as it is completed + state.observers.remove(s); + } + } + } finally { + state.SUBSCRIPTION_LOCK.unlock(); + } } } diff --git a/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java index 321b7b422a..b483b9c99f 100644 --- a/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java @@ -15,9 +15,13 @@ */ package rx.subjects; +import static org.junit.Assert.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mockito; @@ -66,6 +70,62 @@ public void testCompleted() { verify(aObserver, times(1)).onCompleted(); } + @Test + public void testNull() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + subject.subscribe(aObserver); + + subject.onNext(null); + subject.onCompleted(); + + verify(aObserver, times(1)).onNext(null); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSubscribeAfterCompleted() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + subject.onCompleted(); + + subject.subscribe(aObserver); + + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSubscribeAfterError() { + AsyncSubject subject = AsyncSubject.create(); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + + subject.onNext("one"); + subject.onNext("two"); + subject.onNext("three"); + + RuntimeException re = new RuntimeException("failed"); + subject.onError(re); + + subject.subscribe(aObserver); + + verify(aObserver, times(1)).onError(re); + verify(aObserver, Mockito.never()).onNext(any(String.class)); + verify(aObserver, Mockito.never()).onCompleted(); + } + @Test public void testError() { AsyncSubject subject = AsyncSubject.create(); @@ -151,4 +211,94 @@ public void testEmptySubjectCompleted() { inOrder.verify(aObserver, times(1)).onCompleted(); inOrder.verifyNoMoreInteractions(); } + + /** + * Can receive timeout if subscribe never receives an onError/onCompleted ... which reveals a race condition. + */ + @Test + public void testSubscribeCompletionRaceCondition() { + /* + * With non-threadsafe code this fails most of the time on my dev laptop and is non-deterministic enough + * to act as a unit test to the race conditions. + * + * With the synchronization code in place I can not get this to fail on my laptop. + */ + for (int i = 0; i < 50; i++) { + final AsyncSubject subject = AsyncSubject.create(); + final AtomicReference value1 = new AtomicReference(); + + subject.subscribe(new Action1() { + + @Override + public void call(String t1) { + try { + // simulate a slow observer + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + value1.set(t1); + } + + }); + + Thread t1 = new Thread(new Runnable() { + + @Override + public void run() { + subject.onNext("value"); + subject.onCompleted(); + } + }); + + SubjectObserverThread t2 = new SubjectObserverThread(subject); + SubjectObserverThread t3 = new SubjectObserverThread(subject); + SubjectObserverThread t4 = new SubjectObserverThread(subject); + SubjectObserverThread t5 = new SubjectObserverThread(subject); + + t2.start(); + t3.start(); + t1.start(); + t4.start(); + t5.start(); + try { + t1.join(); + t2.join(); + t3.join(); + t4.join(); + t5.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + assertEquals("value", value1.get()); + assertEquals("value", t2.value.get()); + assertEquals("value", t3.value.get()); + assertEquals("value", t4.value.get()); + assertEquals("value", t5.value.get()); + } + + } + + private static class SubjectObserverThread extends Thread { + + private final AsyncSubject subject; + private final AtomicReference value = new AtomicReference(); + + public SubjectObserverThread(AsyncSubject subject) { + this.subject = subject; + } + + @Override + public void run() { + try { + // a timeout exception will happen if we don't get a terminal state + String v = subject.timeout(2000, TimeUnit.MILLISECONDS).toBlockingObservable().single(); + value.set(v); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } From eae8916ffe4a7e12c36be75b75f0984bad7222f2 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Sat, 16 Nov 2013 03:03:24 +0000 Subject: [PATCH 264/333] [Gradle Release Plugin] - pre tag commit: '0.14.11'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5f289abc41..e054c50bbc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.11-SNAPSHOT +version=0.14.11 From d1706c3db93218e0054c34526987733ebbc53bb8 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Sat, 16 Nov 2013 03:03:28 +0000 Subject: [PATCH 265/333] [Gradle Release Plugin] - new version commit: '0.14.12-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e054c50bbc..0c0db8d722 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.11 +version=0.14.12-SNAPSHOT From ba2c1f97d6e837ec20802447939a5e71df78a203 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Fri, 15 Nov 2013 19:09:37 -0800 Subject: [PATCH 266/333] Version 0.14.11 --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index c481155149..4eef2f974b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # RxJava Releases # +### Version 0.14.11 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.11%22)) ### + +* [Pull 486](https://github.com/Netflix/RxJava/pull/486) BugFix: AsyncSubject +* [Pull 483](https://github.com/Netflix/RxJava/pull/483) Tweaks to DoOnEach and added DoOnError/DoOnCompleted + +This has a very slight breaking change by removing one `doOnEach` overload. The version was not bumped from 0.14 to 0.15 as it is so minor and the offending method was just released in the previous version. + ### Version 0.14.10 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.10%22)) ### * [Pull 481](https://github.com/Netflix/RxJava/pull/481) Operator: Using From a884a67a7c2b4c4f51d21d6219db7a039fdb9f83 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Tue, 19 Nov 2013 11:45:01 +0800 Subject: [PATCH 267/333] Add contravariant for min and max --- rxjava-core/src/main/java/rx/Observable.java | 16 ++++++------- .../java/rx/operators/OperationMinMax.java | 24 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 368a307191..aeb4896cad 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -3649,7 +3649,7 @@ public static Observable averageDoubles(Observable source) { * if the source is empty * @see MSDN: Observable.Min */ - public static > Observable min(Observable source) { + public static > Observable min(Observable source) { return OperationMinMax.min(source); } @@ -3665,7 +3665,7 @@ public static > Observable min(Observable source) * if the source is empty * @see MSDN: Observable.Min */ - public Observable min(Comparator comparator) { + public Observable min(Comparator comparator) { return OperationMinMax.min(this, comparator); } @@ -3678,7 +3678,7 @@ public Observable min(Comparator comparator) { * @return an observable emitting a List of the elements with the minimum key value. * @see MSDN: Observable.MinBy */ - public > Observable> minBy(Func1 selector) { + public > Observable> minBy(Func1 selector) { return OperationMinMax.minBy(this, selector); } @@ -3693,7 +3693,7 @@ public > Observable> minBy(Func1 selector) * @return an observable emitting a List of the elements with the minimum key value according to the specified comparator. * @see MSDN: Observable.MinBy */ - public Observable> minBy(Func1 selector, Comparator comparator) { + public Observable> minBy(Func1 selector, Comparator comparator) { return OperationMinMax.minBy(this, selector, comparator); } @@ -3709,7 +3709,7 @@ public Observable> minBy(Func1 selector, Comparator compara * if the source is empty. * @see MSDN: Observable.Max */ - public static > Observable max(Observable source) { + public static > Observable max(Observable source) { return OperationMinMax.max(source); } @@ -3725,7 +3725,7 @@ public static > Observable max(Observable source) * if the source is empty. * @see MSDN: Observable.Max */ - public Observable max(Comparator comparator) { + public Observable max(Comparator comparator) { return OperationMinMax.max(this, comparator); } @@ -3738,7 +3738,7 @@ public Observable max(Comparator comparator) { * @return an observable emitting a List of the elements with the maximum key value. * @see MSDN: Observable.MaxBy */ - public > Observable> maxBy(Func1 selector) { + public > Observable> maxBy(Func1 selector) { return OperationMinMax.maxBy(this, selector); } @@ -3753,7 +3753,7 @@ public > Observable> maxBy(Func1 selector) * @return an observable emitting a List of the elements with the maximum key value according to the specified comparator. * @see MSDN: Observable.MaxBy */ - public Observable> maxBy(Func1 selector, Comparator comparator) { + public Observable> maxBy(Func1 selector, Comparator comparator) { return OperationMinMax.maxBy(this, selector, comparator); } diff --git a/rxjava-core/src/main/java/rx/operators/OperationMinMax.java b/rxjava-core/src/main/java/rx/operators/OperationMinMax.java index ba587751e8..0d5a1c9378 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMinMax.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMinMax.java @@ -28,47 +28,47 @@ */ public class OperationMinMax { - public static > Observable min( + public static > Observable min( Observable source) { return minMax(source, -1L); } public static Observable min(Observable source, - final Comparator comparator) { + final Comparator comparator) { return minMax(source, comparator, -1L); } - public static > Observable> minBy( + public static > Observable> minBy( Observable source, final Func1 selector) { return minMaxBy(source, selector, -1L); } public static Observable> minBy(Observable source, - final Func1 selector, final Comparator comparator) { + final Func1 selector, final Comparator comparator) { return minMaxBy(source, selector, comparator, -1L); } - public static > Observable max( + public static > Observable max( Observable source) { return minMax(source, 1L); } public static Observable max(Observable source, - final Comparator comparator) { + final Comparator comparator) { return minMax(source, comparator, 1L); } - public static > Observable> maxBy( + public static > Observable> maxBy( Observable source, final Func1 selector) { return minMaxBy(source, selector, 1L); } public static Observable> maxBy(Observable source, - final Func1 selector, final Comparator comparator) { + final Func1 selector, final Comparator comparator) { return minMaxBy(source, selector, comparator, 1L); } - private static > Observable minMax( + private static > Observable minMax( Observable source, final long flag) { return source.reduce(new Func2() { @Override @@ -82,7 +82,7 @@ public T call(T acc, T value) { } private static Observable minMax(Observable source, - final Comparator comparator, final long flag) { + final Comparator comparator, final long flag) { return source.reduce(new Func2() { @Override public T call(T acc, T value) { @@ -94,7 +94,7 @@ public T call(T acc, T value) { }); } - private static > Observable> minMaxBy( + private static > Observable> minMaxBy( Observable source, final Func1 selector, final long flag) { return source.reduce(new ArrayList(), new Func2, T, List>() { @@ -119,7 +119,7 @@ public List call(List acc, T value) { } private static Observable> minMaxBy(Observable source, - final Func1 selector, final Comparator comparator, + final Func1 selector, final Comparator comparator, final long flag) { return source.reduce(new ArrayList(), new Func2, T, List>() { From 1669bde571194b1f60aee3e1d754ad583d215067 Mon Sep 17 00:00:00 2001 From: zsxwing Date: Tue, 19 Nov 2013 13:09:50 +0800 Subject: [PATCH 268/333] Implement the scheduler overloads for Range, From, StartWith --- rxjava-core/src/main/java/rx/Observable.java | 62 +++++++++++++++++++ .../src/test/java/rx/ObservableTests.java | 62 +++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index f9253b2b9d..b349ffd94b 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -665,6 +665,23 @@ public static Observable from(Iterable iterable) { return create(OperationToObservableIterable.toObservableIterable(iterable)); } + /** + * Converts an {@link Iterable} sequence into an Observable with the specified scheduler. + * + * @param iterable + * the source {@link Iterable} sequence + * @param scheduler + * the scheduler to emit the items of the iterable + * @param + * the type of items in the {@link Iterable} sequence and the type of items to be + * emitted by the resulting Observable + * @return an Observable that emits each item in the source {@link Iterable} sequence with the specified scheduler + * @see MSDN: Observable.ToObservable + */ + public static Observable from(Iterable iterable, Scheduler scheduler) { + return from(iterable).observeOn(scheduler); + } + /** * Converts an Array into an Observable. *

    @@ -959,6 +976,23 @@ public static Observable range(int start, int count) { return from(Range.createWithCount(start, count)); } + /** + * Generates an Observable that emits a sequence of integers within a specified range with the specified scheduler. + * + * @param start + * the value of the first integer in the sequence + * @param count + * the number of sequential integers to generate + * @param scheduler + * the scheduler to run the generator loop on + * @return an Observable that emits a range of sequential integers + * + * @see Observable.Range Method (Int32, Int32, IScheduler) + */ + public static Observable range(int start, int count, Scheduler scheduler) { + return range(start, count).observeOn(scheduler); + } + /** * Returns an Observable that calls an Observable factory to create its * Observable for each new Observer that subscribes. That is, for each @@ -4553,6 +4587,34 @@ public Observable startWith(Iterable values) { return concat(Observable. from(values), this); } + /** + * Emit a specified set of items with the specified scheduler before beginning to emit items from the source Observable. + * + * @param values + * Iterable of the items you want the modified Observable to emit first + * @param scheduler + * The scheduler to emit the prepended values on. + * @return an Observable that exhibits the modified behavior + * @see MSDN: Observable.StartWith + */ + public Observable startWith(Iterable values, Scheduler scheduler) { + return concat(from(values, scheduler), this); + } + + /** + * Emit a specified array of items with the specified scheduler before beginning to emit items from the source Observable. + * + * @param values + * The items you want the modified Observable to emit first + * @param scheduler + * The scheduler to emit the prepended values on. + * @return an Observable that exhibits the modified behavior + * @see MSDN: Observable.StartWith + */ + public Observable startWith(T[] values, Scheduler scheduler) { + return startWith(Arrays.asList(values), scheduler); + } + /** * Emit a specified item before beginning to emit items from the source * Observable. diff --git a/rxjava-core/src/test/java/rx/ObservableTests.java b/rxjava-core/src/test/java/rx/ObservableTests.java index ee817e08b5..3d718210a1 100644 --- a/rxjava-core/src/test/java/rx/ObservableTests.java +++ b/rxjava-core/src/test/java/rx/ObservableTests.java @@ -20,6 +20,7 @@ import static org.mockito.Mockito.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -29,10 +30,12 @@ import org.junit.Before; import org.junit.Test; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import rx.Observable.OnSubscribeFunc; +import rx.concurrency.TestScheduler; import rx.observables.ConnectableObservable; import rx.subscriptions.BooleanSubscription; import rx.subscriptions.Subscriptions; @@ -886,6 +889,7 @@ public void testContainsWithEmptyObservable() { verify(aObserver, times(1)).onCompleted(); } + @Test public void testIgnoreElements() { Observable observable = Observable.from(1, 2, 3).ignoreElements(); @@ -896,4 +900,62 @@ public void testIgnoreElements() { verify(aObserver, never()).onError(any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } + + @Test + public void testFromWithScheduler() { + TestScheduler scheduler = new TestScheduler(); + Observable observable = Observable.from(Arrays.asList(1, 2), scheduler); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + scheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS); + + InOrder inOrder = inOrder(aObserver); + inOrder.verify(aObserver, times(1)).onNext(1); + inOrder.verify(aObserver, times(1)).onNext(2); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testStartWithWithScheduler() { + TestScheduler scheduler = new TestScheduler(); + Observable observable = Observable.from(3, 4).startWith(Arrays.asList(1, 2), scheduler); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + scheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS); + + InOrder inOrder = inOrder(aObserver); + inOrder.verify(aObserver, times(1)).onNext(1); + inOrder.verify(aObserver, times(1)).onNext(2); + inOrder.verify(aObserver, times(1)).onNext(3); + inOrder.verify(aObserver, times(1)).onNext(4); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testRangeWithScheduler() { + TestScheduler scheduler = new TestScheduler(); + Observable observable = Observable.range(3, 4, scheduler); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + + scheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS); + + InOrder inOrder = inOrder(aObserver); + inOrder.verify(aObserver, times(1)).onNext(3); + inOrder.verify(aObserver, times(1)).onNext(4); + inOrder.verify(aObserver, times(1)).onNext(5); + inOrder.verify(aObserver, times(1)).onNext(6); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } } \ No newline at end of file From 21e230c247670f5c321ee39f518632577710567b Mon Sep 17 00:00:00 2001 From: akarnokd Date: Tue, 19 Nov 2013 09:55:03 +0100 Subject: [PATCH 269/333] ZipMany with conformance changes --- .../main/java/rx/operators/OperationZip.java | 426 ++++++++++++------ 1 file changed, 295 insertions(+), 131 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationZip.java b/rxjava-core/src/main/java/rx/operators/OperationZip.java index 1837f6e58b..64d4c28c80 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationZip.java +++ b/rxjava-core/src/main/java/rx/operators/OperationZip.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -15,14 +15,23 @@ */ package rx.operators; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Subscription; +import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.SerialSubscription; import rx.util.functions.Func2; import rx.util.functions.Func3; import rx.util.functions.Func4; @@ -49,135 +58,79 @@ * number of onNext invocations of the source Observable that emits the fewest items. */ public final class OperationZip { - - public static OnSubscribeFunc zip(Observable o1, Observable o2, Func2 zipFunction) { - Aggregator a = new Aggregator(Functions.fromFunc(zipFunction)); - a.addObserver(new ZipObserver(a, o1)); - a.addObserver(new ZipObserver(a, o2)); - return a; + + public static OnSubscribeFunc zip(Observable o1, Observable o2, final Func2 zipFunction) { + return zip(Arrays.asList(o1, o2), Functions.fromFunc(zipFunction)); } - - public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Func3 zipFunction) { - Aggregator a = new Aggregator(Functions.fromFunc(zipFunction)); - a.addObserver(new ZipObserver(a, o1)); - a.addObserver(new ZipObserver(a, o2)); - a.addObserver(new ZipObserver(a, o3)); - return a; + + public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, final Func3 zipFunction) { + return zip(Arrays.asList(o1, o2, o3), Functions.fromFunc(zipFunction)); } - - public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Func4 zipFunction) { - Aggregator a = new Aggregator(Functions.fromFunc(zipFunction)); - a.addObserver(new ZipObserver(a, o1)); - a.addObserver(new ZipObserver(a, o2)); - a.addObserver(new ZipObserver(a, o3)); - a.addObserver(new ZipObserver(a, o4)); - return a; + + public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, final Func4 zipFunction) { + return zip(Arrays.asList(o1, o2, o3, o4), Functions.fromFunc(zipFunction)); } - - public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 zipFunction) { - Aggregator a = new Aggregator(Functions.fromFunc(zipFunction)); - a.addObserver(new ZipObserver(a, o1)); - a.addObserver(new ZipObserver(a, o2)); - a.addObserver(new ZipObserver(a, o3)); - a.addObserver(new ZipObserver(a, o4)); - a.addObserver(new ZipObserver(a, o5)); - return a; + + public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, final Func5 zipFunction) { + return zip(Arrays.asList(o1, o2, o3, o4, o5), Functions.fromFunc(zipFunction)); } - + public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, - Func6 zipFunction) { - Aggregator a = new Aggregator(Functions.fromFunc(zipFunction)); - a.addObserver(new ZipObserver(a, o1)); - a.addObserver(new ZipObserver(a, o2)); - a.addObserver(new ZipObserver(a, o3)); - a.addObserver(new ZipObserver(a, o4)); - a.addObserver(new ZipObserver(a, o5)); - a.addObserver(new ZipObserver(a, o6)); - return a; + final Func6 zipFunction) { + return zip(Arrays.asList(o1, o2, o3, o4, o5, o6), Functions.fromFunc(zipFunction)); } - + public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, - Func7 zipFunction) { - Aggregator a = new Aggregator(Functions.fromFunc(zipFunction)); - a.addObserver(new ZipObserver(a, o1)); - a.addObserver(new ZipObserver(a, o2)); - a.addObserver(new ZipObserver(a, o3)); - a.addObserver(new ZipObserver(a, o4)); - a.addObserver(new ZipObserver(a, o5)); - a.addObserver(new ZipObserver(a, o6)); - a.addObserver(new ZipObserver(a, o7)); - return a; + final Func7 zipFunction) { + return zip(Arrays.asList(o1, o2, o3, o4, o5, o6, o7), Functions.fromFunc(zipFunction)); } - + public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, - Func8 zipFunction) { - Aggregator a = new Aggregator(Functions.fromFunc(zipFunction)); - a.addObserver(new ZipObserver(a, o1)); - a.addObserver(new ZipObserver(a, o2)); - a.addObserver(new ZipObserver(a, o3)); - a.addObserver(new ZipObserver(a, o4)); - a.addObserver(new ZipObserver(a, o5)); - a.addObserver(new ZipObserver(a, o6)); - a.addObserver(new ZipObserver(a, o7)); - a.addObserver(new ZipObserver(a, o8)); - return a; + final Func8 zipFunction) { + return zip(Arrays.asList(o1, o2, o3, o4, o5, o6, o7, o8), Functions.fromFunc(zipFunction)); } - + public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, - Observable o9, Func9 zipFunction) { - Aggregator a = new Aggregator(Functions.fromFunc(zipFunction)); - a.addObserver(new ZipObserver(a, o1)); - a.addObserver(new ZipObserver(a, o2)); - a.addObserver(new ZipObserver(a, o3)); - a.addObserver(new ZipObserver(a, o4)); - a.addObserver(new ZipObserver(a, o5)); - a.addObserver(new ZipObserver(a, o6)); - a.addObserver(new ZipObserver(a, o7)); - a.addObserver(new ZipObserver(a, o8)); - a.addObserver(new ZipObserver(a, o9)); - return a; + Observable o9, final Func9 zipFunction) { + return zip(Arrays.asList(o1, o2, o3, o4, o5, o6, o7, o8, o9), Functions.fromFunc(zipFunction)); } - - public static OnSubscribeFunc zip(Iterable> ws, FuncN zipFunction) { - Aggregator a = new Aggregator(zipFunction); - for (Observable w : ws) { - ZipObserver zipObserver = new ZipObserver(a, w); - a.addObserver(zipObserver); - } + + public static OnSubscribeFunc zip(Iterable> ws, final FuncN zipFunction) { + ManyObservables a = new ManyObservables(ws, zipFunction); return a; } - + /* - * ThreadSafe - */ + * ThreadSafe + */ /* package accessible for unit tests */static class ZipObserver implements Observer { final Observable w; final Aggregator a; private final SafeObservableSubscription subscription = new SafeObservableSubscription(); private final AtomicBoolean subscribed = new AtomicBoolean(false); - + public ZipObserver(Aggregator a, Observable w) { this.a = a; this.w = w; } - + public void startWatching() { if (subscribed.compareAndSet(false, true)) { // only subscribe once even if called more than once subscription.wrap(w.subscribe(this)); } } - + @Override public void onCompleted() { a.complete(this); } - + @Override public void onError(Throwable e) { a.error(this, e); } - + @Override public void onNext(T args) { try { @@ -187,36 +140,36 @@ public void onNext(T args) { } } } - + /** * Receive notifications from each of the Observables we are reducing and execute the zipFunction whenever we have received events from all Observables. - * + * * This class is thread-safe. - * + * * @param */ /* package accessible for unit tests */static class Aggregator implements OnSubscribeFunc { - + private volatile SynchronizedObserver observer; private final FuncN zipFunction; private final AtomicBoolean started = new AtomicBoolean(false); private final AtomicBoolean running = new AtomicBoolean(true); private final ConcurrentHashMap, Boolean> completed = new ConcurrentHashMap, Boolean>(); - + /* we use ConcurrentHashMap despite synchronization of methods because stop() does NOT use synchronization and this map is used by it and can be called by other threads */ private ConcurrentHashMap, ConcurrentLinkedQueue> receivedValuesPerObserver = new ConcurrentHashMap, ConcurrentLinkedQueue>(); /* we use a ConcurrentLinkedQueue to retain ordering (I'd like to just use a ConcurrentLinkedHashMap for 'receivedValuesPerObserver' but that doesn't exist in standard java */ private ConcurrentLinkedQueue> observers = new ConcurrentLinkedQueue>(); - + public Aggregator(FuncN zipFunction) { this.zipFunction = zipFunction; } - + /** * Receive notification of a Observer starting (meaning we should require it for aggregation) - * + * * Thread Safety => Invoke ONLY from the static factory methods at top of this class which are always an atomic execution by a single thread. - * + * * @param w */ void addObserver(ZipObserver w) { @@ -224,10 +177,10 @@ void addObserver(ZipObserver w) { observers.add(w); receivedValuesPerObserver.put(w, new ConcurrentLinkedQueue()); } - + /** * Receive notification of a Observer completing its iterations. - * + * * @param w */ void complete(ZipObserver w) { @@ -242,10 +195,10 @@ void complete(ZipObserver w) { } } } - + /** * Receive error for a Observer. Throw the error up the chain and stop processing. - * + * * @param w */ void error(ZipObserver w, Throwable e) { @@ -256,12 +209,12 @@ void error(ZipObserver w, Throwable e) { stop(); } } - + /** * Receive the next value from a Observer. *

    * If we have received values from all Observers, trigger the zip function, otherwise store the value and keep waiting. - * + * * @param w * @param arg */ @@ -269,18 +222,18 @@ void next(ZipObserver w, Object arg) { if (observer == null) { throw new RuntimeException("This shouldn't be running if a Observer isn't registered"); } - + /* if we've been 'unsubscribed' don't process anything further even if the things we're watching keep sending (likely because they are not responding to the unsubscribe call) */ if (!running.get()) { return; } - + // store the value we received and below we'll decide if we are to send it to the Observer receivedValuesPerObserver.get(w).add(arg); - + // define here so the variable is out of the synchronized scope Object[] argsToZip = new Object[observers.size()]; - + /* we have to synchronize here despite using concurrent data structures because the compound logic here must all be done atomically */ synchronized (this) { // if all ZipObservers in 'receivedValues' map have a value, invoke the zipFunction @@ -301,7 +254,7 @@ void next(ZipObserver w, Object arg) { // this 'next' method while another thread finishes calling this zipFunction observer.onNext(zipFunction.call(argsToZip)); } - + @Override public Subscription onSubscribe(Observer observer) { if (started.compareAndSet(false, true)) { @@ -311,34 +264,34 @@ public Subscription onSubscribe(Observer observer) { for (ZipObserver rw : observers) { rw.startWatching(); } - + return subscription.wrap(new Subscription() { - + @Override public void unsubscribe() { stop(); } - + }); } else { /* a Observer already has subscribed so blow up */ throw new IllegalStateException("Only one Observer can subscribe to this Observable."); } } - + /* - * Do NOT synchronize this because it gets called via unsubscribe which can occur on other threads - * and result in deadlocks. (http://jira/browse/API-4060) - * - * AtomicObservableSubscription uses compareAndSet instead of locking to avoid deadlocks but ensure single-execution. - * - * We do the same in the implementation of this method. - * - * ThreadSafety of this method is provided by: - * - AtomicBoolean[running].compareAndSet - * - ConcurrentLinkedQueue[Observers] - * - ZipObserver.subscription being an AtomicObservableSubscription - */ + * Do NOT synchronize this because it gets called via unsubscribe which can occur on other threads + * and result in deadlocks. (http://jira/browse/API-4060) + * + * AtomicObservableSubscription uses compareAndSet instead of locking to avoid deadlocks but ensure single-execution. + * + * We do the same in the implementation of this method. + * + * ThreadSafety of this method is provided by: + * - AtomicBoolean[running].compareAndSet + * - ConcurrentLinkedQueue[Observers] + * - ZipObserver.subscription being an AtomicObservableSubscription + */ private void stop() { /* tell ourselves to stop processing onNext events by setting running=false */ if (running.compareAndSet(true, false)) { @@ -350,6 +303,217 @@ private void stop() { } } } - + + } + /** + * Merges the values across multiple sources and applies the selector + * function. + *

    The resulting sequence terminates if no more pairs can be + * established, i.e., streams of length 1 and 2 zipped will produce + * only 1 item.

    + *

    Exception semantics: errors from the source observable are + * propagated as-is.

    + * @param the common element type + * @param the result element type + */ + public static class ManyObservables implements OnSubscribeFunc { + /** */ + protected final Iterable> sources; + /** */ + protected final FuncN selector; + /** + * Constructor. + * @param sources the sources + * @param selector the result selector + */ + public ManyObservables( + Iterable> sources, + FuncN selector) { + this.sources = sources; + this.selector = selector; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + + final CompositeSubscription composite = new CompositeSubscription(); + + final ReadWriteLock rwLock = new ReentrantReadWriteLock(true); + + final List> all = new ArrayList>(); + + Observer> o2 = new Observer>() { + @Override + public void onCompleted() { + observer.onCompleted(); + } + @Override + public void onError(Throwable t) { + observer.onError(t); + } + @Override + public void onNext(List value) { + observer.onNext(selector.call(value.toArray(new Object[value.size()]))); + } + }; + + for (Observable o : sources) { + + ItemObserver io = new ItemObserver( + rwLock, all, o, o2, composite); + composite.add(io); + all.add(io); + } + + for (ItemObserver io : all) { + io.connect(); + } + + return composite; + } + /** + * The individual line's observer. + * @author akarnokd, 2013.01.14. + * @param the element type + */ + public static class ItemObserver implements Observer, Subscription { + /** Reader-writer lock. */ + protected final ReadWriteLock rwLock; + /** The queue. */ + public final Queue queue = new LinkedList(); + /** The list of the other observers. */ + public final List> all; + /** The null sentinel value. */ + protected static final Object NULL_SENTINEL = new Object(); + /** The global cancel. */ + protected final Subscription cancel; + /** The subscription to the source. */ + protected final SerialSubscription toSource = new SerialSubscription(); + /** Indicate completion of this stream. */ + protected boolean done; + /** The source. */ + protected final Observable source; + /** The observer. */ + protected final Observer> observer; + /** + * Constructor. + * @param rwLock the reader-writer lock to use + * @param all all observers + * @param source the source sequence + * @param observer the output observer + * @param cancel the cancellation handler + */ + public ItemObserver( + ReadWriteLock rwLock, + List> all, + Observable source, + Observer> observer, + Subscription cancel) { + this.rwLock = rwLock; + this.all = all; + this.source = source; + this.observer = observer; + this.cancel = cancel; + } + @Override + public void onNext(T value) { + rwLock.readLock().lock(); + try { + if (done) { + return; + } + queue.add(value != null ? value : NULL_SENTINEL); + } finally { + rwLock.readLock().unlock(); + } + // run collector + if (rwLock.writeLock().tryLock()) { + try { + while (true) { + List values = new ArrayList(all.size()); + for (ItemObserver io : all) { + if (io.queue.isEmpty()) { + if (io.done) { + observer.onCompleted(); + cancel.unsubscribe(); + } + return; + } + Object v = io.queue.peek(); + if (v == NULL_SENTINEL) { + v = null; + } + values.add((T)v); + } + if (values.size() == all.size()) { + for (ItemObserver io : all) { + io.queue.poll(); + } + observer.onNext(values); + } + } + } finally { + rwLock.writeLock().unlock(); + } + } + } + + @Override + public void onError(Throwable ex) { + boolean c = false; + rwLock.writeLock().lock(); + try { + if (done) { + return; + } + done = true; + c = true; + observer.onError(ex); + cancel.unsubscribe(); + } finally { + rwLock.writeLock().unlock(); + } + if (c) { + unsubscribe(); + } + } + + @Override + public void onCompleted() { + boolean c = false; + rwLock.readLock().lock(); + try { + done = true; + c = true; + } finally { + rwLock.readLock().unlock(); + } + if (rwLock.writeLock().tryLock()) { + try { + for (ItemObserver io : all) { + if (io.queue.isEmpty() && io.done) { + observer.onCompleted(); + cancel.unsubscribe(); + return; + } + } + } finally { + rwLock.writeLock().unlock(); + } + } + if (c) { + unsubscribe(); + } + } + /** Connect to the source observable. */ + public void connect() { + toSource.setSubscription(source.subscribe(this)); + } + @Override + public void unsubscribe() { + toSource.unsubscribe(); + } + + } } } From b8b8334ce2d46ee61964488b3f74e0bab24c400f Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 19 Nov 2013 11:40:55 -0800 Subject: [PATCH 270/333] Unit test to assert correct scheduler thread --- .../rx/operators/OperationObserveOnTest.java | 59 +++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java b/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java index e114f637ff..15f1a44fb3 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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 - * + * + * 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. @@ -16,6 +16,7 @@ package rx.operators; import static org.junit.Assert.*; +import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import static rx.operators.OperationObserveOn.*; @@ -30,6 +31,7 @@ import rx.Observable; import rx.Observer; import rx.concurrency.Schedulers; +import rx.util.functions.Action1; public class OperationObserveOnTest { @@ -81,4 +83,53 @@ public Void answer(InvocationOnMock invocation) throws Throwable { inOrder.verify(observer, times(1)).onCompleted(); inOrder.verifyNoMoreInteractions(); } + + @Test + @SuppressWarnings("unchecked") + public void testThreadName() throws InterruptedException { + Observable obs = Observable.from("one", null, "two", "three", "four"); + + Observer observer = mock(Observer.class); + + InOrder inOrder = inOrder(observer); + + final CountDownLatch completedLatch = new CountDownLatch(1); + doAnswer(new Answer() { + + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + completedLatch.countDown(); + + return null; + } + }).when(observer).onCompleted(); + + doAnswer(new Answer() { + + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + completedLatch.countDown(); + + return null; + } + }).when(observer).onError(any(Exception.class)); + + obs.observeOn(Schedulers.newThread()).doOnEach(new Action1() { + + @Override + public void call(String t1) { + String threadName = Thread.currentThread().getName(); + boolean correctThreadName = threadName.startsWith("RxNewThreadScheduler"); + System.out.println("ThreadName: " + threadName + " Correct => " + correctThreadName); + assertTrue(correctThreadName); + } + + }).subscribe(observer); + + if (!completedLatch.await(1000, TimeUnit.MILLISECONDS)) { + fail("timed out waiting"); + } + + inOrder.verify(observer, times(1)).onCompleted(); + } } From 310d530af8e8e4a245480f7c731e678529f81bb5 Mon Sep 17 00:00:00 2001 From: DavidMGross Date: Tue, 19 Nov 2013 11:41:30 -0800 Subject: [PATCH 271/333] Update Observable.java from(scheduler) -> add diagram, standardize javadoc comments range(scheduler) -> add diagram, standardize javadoc comments startWith(scheduler) -> add diagram, standardize javadoc comments --- rxjava-core/src/main/java/rx/Observable.java | 74 ++++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index b349ffd94b..65ba341035 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -667,16 +667,16 @@ public static Observable from(Iterable iterable) { /** * Converts an {@link Iterable} sequence into an Observable with the specified scheduler. - * - * @param iterable - * the source {@link Iterable} sequence - * @param scheduler - * the scheduler to emit the items of the iterable - * @param - * the type of items in the {@link Iterable} sequence and the type of items to be - * emitted by the resulting Observable - * @return an Observable that emits each item in the source {@link Iterable} sequence with the specified scheduler - * @see MSDN: Observable.ToObservable + *

    + * + * + * @param iterable the source {@link Iterable} sequence + * @param scheduler the scheduler to emit the items of the iterable + * @param the type of items in the {@link Iterable} sequence and the + * type of items to be emitted by the resulting Observable + * @return an Observable that emits each item in the source {@link Iterable} + * sequence with the specified scheduler + * @see MSDN: Observable.ToObservable */ public static Observable from(Iterable iterable, Scheduler scheduler) { return from(iterable).observeOn(scheduler); @@ -977,17 +977,15 @@ public static Observable range(int start, int count) { } /** - * Generates an Observable that emits a sequence of integers within a specified range with the specified scheduler. - * - * @param start - * the value of the first integer in the sequence - * @param count - * the number of sequential integers to generate - * @param scheduler - * the scheduler to run the generator loop on + * Generates an Observable that emits a sequence of integers within a + * specified range with the specified scheduler. + *

    + * + * @param start the value of the first integer in the sequence + * @param count the number of sequential integers to generate + * @param scheduler the scheduler to run the generator loop on * @return an Observable that emits a range of sequential integers - * - * @see Observable.Range Method (Int32, Int32, IScheduler) + * @see Observable.Range Method (Int32, Int32, IScheduler) */ public static Observable range(int start, int count, Scheduler scheduler) { return range(start, count).observeOn(scheduler); @@ -3210,7 +3208,7 @@ public Observable filter(Func1 predicate) { * * * @return an Observable of sequentially distinct items - * @see MSDN: Observable.distinctUntilChanged + * @see MSDN: Observable.distinctUntilChanged */ public Observable distinctUntilChanged() { return create(OperationDistinctUntilChanged.distinctUntilChanged(this)); @@ -3227,7 +3225,7 @@ public Observable distinctUntilChanged() { * value that is used for deciding whether an item is * sequentially distinct from another one or not * @return an Observable of sequentially distinct items - * @see MSDN: Observable.distinctUntilChanged + * @see MSDN: Observable.distinctUntilChanged */ public Observable distinctUntilChanged(Func1 keySelector) { return create(OperationDistinctUntilChanged.distinctUntilChanged(this, keySelector)); @@ -3240,7 +3238,7 @@ public Observable distinctUntilChanged(Func1 keyS * * * @return an Observable of distinct items - * @see MSDN: Observable.distinct + * @see MSDN: Observable.distinct */ public Observable distinct() { return create(OperationDistinct.distinct(this)); @@ -3256,7 +3254,7 @@ public Observable distinct() { * value that is used to decide whether an item is * distinct from another one or not * @return an Observable that emits distinct items - * @see MSDN: Observable.distinct + * @see MSDN: Observable.distinct */ public Observable distinct(Func1 keySelector) { return create(OperationDistinct.distinct(this, keySelector)); @@ -4588,28 +4586,30 @@ public Observable startWith(Iterable values) { } /** - * Emit a specified set of items with the specified scheduler before beginning to emit items from the source Observable. - * - * @param values - * Iterable of the items you want the modified Observable to emit first - * @param scheduler - * The scheduler to emit the prepended values on. + * Emit a specified set of items with the specified scheduler before + * beginning to emit items from the source Observable. + *

    + * + * + * @param values iterable of the items you want the modified Observable to emit first + * @param scheduler the scheduler to emit the prepended values on * @return an Observable that exhibits the modified behavior - * @see MSDN: Observable.StartWith + * @see MSDN: Observable.StartWith */ public Observable startWith(Iterable values, Scheduler scheduler) { return concat(from(values, scheduler), this); } /** - * Emit a specified array of items with the specified scheduler before beginning to emit items from the source Observable. + * Emit a specified array of items with the specified scheduler before + * beginning to emit items from the source Observable. + *

    + * * - * @param values - * The items you want the modified Observable to emit first - * @param scheduler - * The scheduler to emit the prepended values on. + * @param values the items you want the modified Observable to emit first + * @param scheduler the scheduler to emit the prepended values on * @return an Observable that exhibits the modified behavior - * @see MSDN: Observable.StartWith + * @see MSDN: Observable.StartWith */ public Observable startWith(T[] values, Scheduler scheduler) { return startWith(Arrays.asList(values), scheduler); From 9ff362496d5e92eb97c77f8d38836d17663de25f Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 19 Nov 2013 12:00:25 -0800 Subject: [PATCH 272/333] Refactored ObserveOn without ScheduledObserver --- .../src/main/java/rx/Notification.java | 10 ++ .../java/rx/operators/OperationObserveOn.java | 60 +++++++- .../java/rx/operators/ScheduledObserver.java | 138 ------------------ .../concurrency/SchedulerUnsubscribeTest.java | 11 +- 4 files changed, 75 insertions(+), 144 deletions(-) delete mode 100644 rxjava-core/src/main/java/rx/operators/ScheduledObserver.java diff --git a/rxjava-core/src/main/java/rx/Notification.java b/rxjava-core/src/main/java/rx/Notification.java index 866ed06450..996e217d50 100644 --- a/rxjava-core/src/main/java/rx/Notification.java +++ b/rxjava-core/src/main/java/rx/Notification.java @@ -116,6 +116,16 @@ public boolean isOnNext() { return getKind() == Kind.OnNext; } + public void accept(Observer observer) { + if (isOnNext()) { + observer.onNext(getValue()); + } else if (isOnCompleted()) { + observer.onCompleted(); + } else if (isOnError()) { + observer.onError(getThrowable()); + } + } + public static enum Kind { OnNext, OnError, OnCompleted } diff --git a/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java b/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java index e1272b8152..1826ff959c 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java @@ -15,13 +15,20 @@ */ package rx.operators; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; + +import rx.Notification; import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; import rx.Scheduler; import rx.Subscription; +import rx.concurrency.CurrentThreadScheduler; import rx.concurrency.ImmediateScheduler; import rx.subscriptions.CompositeSubscription; +import rx.util.functions.Action0; +import rx.util.functions.Action1; /** * Asynchronously notify Observers on the specified Scheduler. @@ -38,6 +45,9 @@ private static class ObserveOn implements OnSubscribeFunc { private final Observable source; private final Scheduler scheduler; + final ConcurrentLinkedQueue> queue = new ConcurrentLinkedQueue>(); + final AtomicInteger counter = new AtomicInteger(0); + public ObserveOn(Observable source, Scheduler scheduler) { this.source = source; this.scheduler = scheduler; @@ -48,11 +58,55 @@ public Subscription onSubscribe(final Observer observer) { if (scheduler instanceof ImmediateScheduler) { // do nothing if we request ImmediateScheduler so we don't invoke overhead return source.subscribe(observer); + } else if (scheduler instanceof CurrentThreadScheduler) { + // do nothing if we request CurrentThreadScheduler so we don't invoke overhead + return source.subscribe(observer); } else { - CompositeSubscription s = new CompositeSubscription(); - s.add(source.subscribe(new ScheduledObserver(s, observer, scheduler))); - return s; + return observeOn(observer, scheduler); } } + + public Subscription observeOn(final Observer observer, Scheduler scheduler) { + final CompositeSubscription s = new CompositeSubscription(); + + s.add(source.materialize().subscribe(new Action1>() { + + @Override + public void call(Notification e) { + // this must happen before 'counter' is used to provide synchronization between threads + queue.offer(e); + + // we now use counter to atomically determine if we need to start processing or not + // it will be 0 if it's the first notification or the scheduler has finished processing work + // and we need to start doing it again + if (counter.getAndIncrement() == 0) { + processQueue(s, observer); + } + + } + })); + + return s; + } + + private void processQueue(CompositeSubscription s, final Observer observer) { + s.add(scheduler.schedule(new Action1() { + @Override + public void call(Action0 self) { + Notification not = queue.poll(); + if (not != null) { + not.accept(observer); + } + + // decrement count and if we still have work to do + // recursively schedule ourselves to process again + if (counter.decrementAndGet() > 0) { + self.call(); + } + + } + })); + } } + } diff --git a/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java b/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java deleted file mode 100644 index 0bfef9c2c9..0000000000 --- a/rxjava-core/src/main/java/rx/operators/ScheduledObserver.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright 2013 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.operators; - -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import rx.Notification; -import rx.Observer; -import rx.Scheduler; -import rx.Subscription; -import rx.subscriptions.CompositeSubscription; -import rx.subscriptions.MultipleAssignmentSubscription; -import rx.util.functions.Func2; - -/* package */class ScheduledObserver implements Observer { - private final Observer underlying; - private final Scheduler scheduler; - private final CompositeSubscription parentSubscription; - private final EventLoop eventLoop = new EventLoop(); - final AtomicInteger counter = new AtomicInteger(); - private final AtomicBoolean started = new AtomicBoolean(); - - private final ConcurrentLinkedQueue> queue = new ConcurrentLinkedQueue>(); - - public ScheduledObserver(CompositeSubscription s, Observer underlying, Scheduler scheduler) { - this.parentSubscription = s; - this.underlying = underlying; - this.scheduler = scheduler; - } - - @Override - public void onCompleted() { - enqueue(new Notification()); - } - - @Override - public void onError(final Throwable e) { - enqueue(new Notification(e)); - } - - @Override - public void onNext(final T args) { - enqueue(new Notification(args)); - } - - private void enqueue(Notification notification) { - // this must happen before synchronization between threads - queue.offer(notification); - - /** - * If the counter is currently at 0 (before incrementing with this addition) - * we will schedule the work. - */ - if (counter.getAndIncrement() <= 0) { - if (!started.get() && started.compareAndSet(false, true)) { - // first time we use the parent scheduler to start the event loop - MultipleAssignmentSubscription recursiveSubscription = new MultipleAssignmentSubscription(); - parentSubscription.add(scheduler.schedule(recursiveSubscription, eventLoop)); - parentSubscription.add(recursiveSubscription); - } else { - // subsequent times we reschedule existing one - eventLoop.reschedule(); - } - } - } - - private class EventLoop implements Func2 { - - volatile Scheduler _recursiveScheduler; - volatile MultipleAssignmentSubscription _recursiveSubscription; - - public void reschedule() { - _recursiveSubscription.setSubscription(_recursiveScheduler.schedule(_recursiveSubscription, this)); - } - - @Override - public Subscription call(Scheduler s, MultipleAssignmentSubscription recursiveSubscription) { - /* - * -------------------------------------------------------------------------------------- - * Set these the first time through so we can externally trigger recursive execution again - */ - if (_recursiveScheduler == null) { - _recursiveScheduler = s; - } - if (_recursiveSubscription == null) { - _recursiveSubscription = recursiveSubscription; - } - /* - * Back to regular flow - * -------------------------------------------------------------------------------------- - */ - - do { - Notification notification = queue.poll(); - // if we got a notification, send it - if (notification != null) { - - // if unsubscribed stop working - if (parentSubscription.isUnsubscribed()) { - return parentSubscription; - } - // process notification - - switch (notification.getKind()) { - case OnNext: - underlying.onNext(notification.getValue()); - break; - case OnError: - underlying.onError(notification.getThrowable()); - break; - case OnCompleted: - underlying.onCompleted(); - break; - default: - throw new IllegalStateException("Unknown kind of notification " + notification); - } - } - } while (counter.decrementAndGet() > 0); - - return parentSubscription; - } - } -} diff --git a/rxjava-core/src/test/java/rx/concurrency/SchedulerUnsubscribeTest.java b/rxjava-core/src/test/java/rx/concurrency/SchedulerUnsubscribeTest.java index e99a25eaf3..a89da84304 100644 --- a/rxjava-core/src/test/java/rx/concurrency/SchedulerUnsubscribeTest.java +++ b/rxjava-core/src/test/java/rx/concurrency/SchedulerUnsubscribeTest.java @@ -28,12 +28,17 @@ public void testUnsubscribeOfNewThread() throws InterruptedException { public void testUnsubscribeOfThreadPoolForIO() throws InterruptedException { testUnSubscribeForScheduler(Schedulers.threadPoolForIO()); } - + @Test public void testUnsubscribeOfThreadPoolForComputation() throws InterruptedException { testUnSubscribeForScheduler(Schedulers.threadPoolForComputation()); } - + + @Test + public void testUnsubscribeOfImmediateThread() throws InterruptedException { + testUnSubscribeForScheduler(Schedulers.immediate()); + } + @Test public void testUnsubscribeOfCurrentThread() throws InterruptedException { testUnSubscribeForScheduler(Schedulers.currentThread()); @@ -56,7 +61,7 @@ public Long call(Long aLong) { } }) .subscribeOn(scheduler) - .observeOn(Schedulers.currentThread()) + .observeOn(scheduler) .subscribe(new Observer() { @Override public void onCompleted() { From 22885fa9f7f85d3da6b389df3907f401836733ee Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 19 Nov 2013 13:59:57 -0800 Subject: [PATCH 273/333] ParallelMerge Operator --- rxjava-core/src/main/java/rx/Observable.java | 19 +++++++ .../rx/operators/OperationParallelMerge.java | 50 +++++++++++++++++++ .../operators/OperationParallelMergeTest.java | 49 ++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationParallelMerge.java create mode 100644 rxjava-core/src/main/java/rx/operators/OperationParallelMergeTest.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 65ba341035..1975238bcc 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -64,6 +64,7 @@ import rx.operators.OperationOnErrorReturn; import rx.operators.OperationOnExceptionResumeNextViaObservable; import rx.operators.OperationParallel; +import rx.operators.OperationParallelMerge; import rx.operators.OperationRetry; import rx.operators.OperationSample; import rx.operators.OperationScan; @@ -4052,6 +4053,24 @@ public Observable parallel(final Func1, Observable> f, f return OperationParallel.parallel(this, f, s); } + + /** + * Merges an Observable> to Observable> + * with number of inner Observables as defined by parallelObservables. + *

    + * For example, if the original Observable> has 100 Observables to be emitted and parallelObservables + * is defined as 8, the 100 will be grouped onto 8 output Observables. + *

    + * This is a mechanism for efficiently processing N number of Observables on a smaller N number of resources (typically CPU cores). + * + * @param parallelObservables + * the number of Observables to merge into. + * @return an Observable of Observables constrained to number defined by parallelObservables. + */ + public static Observable> parallelMerge(Observable> source, int parallelObservables) { + return OperationParallelMerge.parallelMerge(source, parallelObservables); + } + /** * Returns a {@link ConnectableObservable}, which waits until its * {@link ConnectableObservable#connect connect} method is called before it diff --git a/rxjava-core/src/main/java/rx/operators/OperationParallelMerge.java b/rxjava-core/src/main/java/rx/operators/OperationParallelMerge.java new file mode 100644 index 0000000000..7e5d91d1ea --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationParallelMerge.java @@ -0,0 +1,50 @@ +/** + * Copyright 2013 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.operators; + +import java.util.concurrent.atomic.AtomicLong; + +import rx.Observable; +import rx.observables.GroupedObservable; +import rx.util.functions.Func1; + +public class OperationParallelMerge { + + public static Observable> parallelMerge(final Observable> source, final int num) { + + return source.groupBy(new Func1, Integer>() { + final AtomicLong rollingCount = new AtomicLong(); + + @Override + public Integer call(Observable o) { + return (int) rollingCount.incrementAndGet() % num; + } + }).map(new Func1>, Observable>() { + + /** + * Safe to cast from GroupedObservable to Observable so suppressing warning + */ + @SuppressWarnings("unchecked") + @Override + public Observable call(GroupedObservable> o) { + return (Observable) o; + } + + }); + + } + +} diff --git a/rxjava-core/src/main/java/rx/operators/OperationParallelMergeTest.java b/rxjava-core/src/main/java/rx/operators/OperationParallelMergeTest.java new file mode 100644 index 0000000000..ce9bf8ceeb --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationParallelMergeTest.java @@ -0,0 +1,49 @@ +/** + * Copyright 2013 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.operators; + +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; + +import rx.Observable; +import rx.subjects.PublishSubject; + +public class OperationParallelMergeTest { + + @Test + public void testParallelMerge() { + PublishSubject p1 = PublishSubject. create(); + PublishSubject p2 = PublishSubject. create(); + PublishSubject p3 = PublishSubject. create(); + PublishSubject p4 = PublishSubject. create(); + + Observable> fourStreams = Observable.> from(p1, p2, p3, p4); + + Observable> twoStreams = OperationParallelMerge.parallelMerge(fourStreams, 2); + Observable> threeStreams = OperationParallelMerge.parallelMerge(fourStreams, 3); + + List> fourList = fourStreams.toList().toBlockingObservable().last(); + List> threeList = threeStreams.toList().toBlockingObservable().last(); + List> twoList = twoStreams.toList().toBlockingObservable().last(); + + assertEquals(4, fourList.size()); + assertEquals(3, threeList.size()); + assertEquals(2, twoList.size()); + } +} From bc6965c5b9fa85b010e97a5f3efbcdc5a3a6490c Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 19 Nov 2013 16:30:29 -0800 Subject: [PATCH 274/333] Fix ObserveOn and add ParallelMerge Scheduler overload - ObserveOn was not correctly recursing when the source was async. It would create a new scheduler (i.e. a new thread) each time. - Also added unit tests to ParallelMerge --- rxjava-core/src/main/java/rx/Observable.java | 17 ++++ .../java/rx/operators/OperationObserveOn.java | 31 ++++++- .../rx/operators/OperationParallelMerge.java | 16 ++-- .../operators/OperationParallelMergeTest.java | 86 +++++++++++++++++++ 4 files changed, 139 insertions(+), 11 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 1975238bcc..ce2b883764 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -4071,6 +4071,23 @@ public static Observable> parallelMerge(ObservableObservable> to Observable> + * with number of inner Observables as defined by parallelObservables and runs each Observable on the defined Scheduler. + *

    + * For example, if the original Observable> has 100 Observables to be emitted and parallelObservables + * is defined as 8, the 100 will be grouped onto 8 output Observables. + *

    + * This is a mechanism for efficiently processing N number of Observables on a smaller N number of resources (typically CPU cores). + * + * @param parallelObservables + * the number of Observables to merge into. + * @return an Observable of Observables constrained to number defined by parallelObservables. + */ + public static Observable> parallelMerge(Observable> source, int parallelObservables, Scheduler scheduler) { + return OperationParallelMerge.parallelMerge(source, parallelObservables, scheduler); + } + /** * Returns a {@link ConnectableObservable}, which waits until its * {@link ConnectableObservable#connect connect} method is called before it diff --git a/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java b/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java index 1826ff959c..fbfda6ef1b 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationObserveOn.java @@ -27,8 +27,10 @@ import rx.concurrency.CurrentThreadScheduler; import rx.concurrency.ImmediateScheduler; import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; import rx.util.functions.Action1; +import rx.util.functions.Func2; /** * Asynchronously notify Observers on the specified Scheduler. @@ -44,6 +46,7 @@ public static OnSubscribeFunc observeOn(Observable source, S private static class ObserveOn implements OnSubscribeFunc { private final Observable source; private final Scheduler scheduler; + private volatile Scheduler recursiveScheduler; final ConcurrentLinkedQueue> queue = new ConcurrentLinkedQueue>(); final AtomicInteger counter = new AtomicInteger(0); @@ -66,7 +69,7 @@ public Subscription onSubscribe(final Observer observer) { } } - public Subscription observeOn(final Observer observer, Scheduler scheduler) { + public Subscription observeOn(final Observer observer, final Scheduler scheduler) { final CompositeSubscription s = new CompositeSubscription(); s.add(source.materialize().subscribe(new Action1>() { @@ -80,7 +83,22 @@ public void call(Notification e) { // it will be 0 if it's the first notification or the scheduler has finished processing work // and we need to start doing it again if (counter.getAndIncrement() == 0) { - processQueue(s, observer); + if (recursiveScheduler == null) { + s.add(scheduler.schedule(null, new Func2() { + + @Override + public Subscription call(Scheduler innerScheduler, T state) { + // record innerScheduler so 'processQueue' can use it for all subsequent executions + recursiveScheduler = innerScheduler; + + processQueue(s, observer); + + return Subscriptions.empty(); + } + })); + } else { + processQueue(s, observer); + } } } @@ -89,8 +107,13 @@ public void call(Notification e) { return s; } - private void processQueue(CompositeSubscription s, final Observer observer) { - s.add(scheduler.schedule(new Action1() { + /** + * This uses 'recursiveScheduler' NOT 'scheduler' as it should reuse the same scheduler each time it processes. + * This means it must first get the recursiveScheduler when it first executes. + */ + private void processQueue(final CompositeSubscription s, final Observer observer) { + + s.add(recursiveScheduler.schedule(new Action1() { @Override public void call(Action0 self) { Notification not = queue.poll(); diff --git a/rxjava-core/src/main/java/rx/operators/OperationParallelMerge.java b/rxjava-core/src/main/java/rx/operators/OperationParallelMerge.java index 7e5d91d1ea..cb2dd0bc34 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationParallelMerge.java +++ b/rxjava-core/src/main/java/rx/operators/OperationParallelMerge.java @@ -18,29 +18,31 @@ import java.util.concurrent.atomic.AtomicLong; import rx.Observable; +import rx.Scheduler; +import rx.concurrency.Schedulers; import rx.observables.GroupedObservable; import rx.util.functions.Func1; public class OperationParallelMerge { - public static Observable> parallelMerge(final Observable> source, final int num) { + public static Observable> parallelMerge(final Observable> source, final int parallelObservables) { + return parallelMerge(source, parallelObservables, Schedulers.currentThread()); + } + + public static Observable> parallelMerge(final Observable> source, final int parallelObservables, final Scheduler scheduler) { return source.groupBy(new Func1, Integer>() { final AtomicLong rollingCount = new AtomicLong(); @Override public Integer call(Observable o) { - return (int) rollingCount.incrementAndGet() % num; + return (int) rollingCount.incrementAndGet() % parallelObservables; } }).map(new Func1>, Observable>() { - /** - * Safe to cast from GroupedObservable to Observable so suppressing warning - */ - @SuppressWarnings("unchecked") @Override public Observable call(GroupedObservable> o) { - return (Observable) o; + return Observable.merge(o).observeOn(scheduler); } }); diff --git a/rxjava-core/src/main/java/rx/operators/OperationParallelMergeTest.java b/rxjava-core/src/main/java/rx/operators/OperationParallelMergeTest.java index ce9bf8ceeb..a001877a71 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationParallelMergeTest.java +++ b/rxjava-core/src/main/java/rx/operators/OperationParallelMergeTest.java @@ -18,11 +18,16 @@ import static org.junit.Assert.*; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import org.junit.Test; import rx.Observable; +import rx.concurrency.Schedulers; import rx.subjects.PublishSubject; +import rx.util.functions.Action1; +import rx.util.functions.Func1; public class OperationParallelMergeTest { @@ -42,8 +47,89 @@ public void testParallelMerge() { List> threeList = threeStreams.toList().toBlockingObservable().last(); List> twoList = twoStreams.toList().toBlockingObservable().last(); + System.out.println("two list: " + twoList); + System.out.println("three list: " + threeList); + System.out.println("four list: " + fourList); + assertEquals(4, fourList.size()); assertEquals(3, threeList.size()); assertEquals(2, twoList.size()); } + + @Test + public void testNumberOfThreads() { + final ConcurrentHashMap threads = new ConcurrentHashMap(); + Observable.merge(getStreams()) + .toBlockingObservable().forEach(new Action1() { + + @Override + public void call(String o) { + System.out.println("o: " + o + " Thread: " + Thread.currentThread()); + threads.put(Thread.currentThread().getName(), Thread.currentThread().getName()); + } + }); + + // without injecting anything, the getStream() method uses Interval which runs on a default scheduler + assertEquals(Runtime.getRuntime().availableProcessors(), threads.keySet().size()); + + // clear + threads.clear(); + + // now we parallelMerge into 3 streams and observeOn for each + // we expect 3 threads in the output + OperationParallelMerge.parallelMerge(getStreams(), 3) + .flatMap(new Func1, Observable>() { + + @Override + public Observable call(Observable o) { + // for each of the parallel + return o.observeOn(Schedulers.newThread()); + } + }) + .toBlockingObservable().forEach(new Action1() { + + @Override + public void call(String o) { + System.out.println("o: " + o + " Thread: " + Thread.currentThread()); + threads.put(Thread.currentThread().getName(), Thread.currentThread().getName()); + } + }); + + assertEquals(3, threads.keySet().size()); + } + + @Test + public void testNumberOfThreadsOnScheduledMerge() { + final ConcurrentHashMap threads = new ConcurrentHashMap(); + + // now we parallelMerge into 3 streams and observeOn for each + // we expect 3 threads in the output + Observable.merge(OperationParallelMerge.parallelMerge(getStreams(), 3, Schedulers.newThread())) + .toBlockingObservable().forEach(new Action1() { + + @Override + public void call(String o) { + System.out.println("o: " + o + " Thread: " + Thread.currentThread()); + threads.put(Thread.currentThread().getName(), Thread.currentThread().getName()); + } + }); + + assertEquals(3, threads.keySet().size()); + } + + private static Observable> getStreams() { + return Observable.range(0, 10).map(new Func1>() { + + @Override + public Observable call(final Integer i) { + return Observable.interval(10, TimeUnit.MILLISECONDS).map(new Func1() { + + @Override + public String call(Long l) { + return "Stream " + i + " Value: " + l; + } + }).take(5); + } + }); + } } From 62564d6641746dbd3bfaa5f82a69dbf76c2418d3 Mon Sep 17 00:00:00 2001 From: headinthebox Date: Tue, 19 Nov 2013 16:58:49 -0800 Subject: [PATCH 275/333] Scala Bindings Refactor --- .../rx/lang/scala/examples/MovieLibUsage.java | 34 +- .../lang/scala/examples => }/MovieLib.scala | 12 +- .../lang/scala/examples => }/Olympics.scala | 16 +- .../ImplicitFunctionConversions.scala | 32 +- .../{rx/lang/scala => }/Notification.scala | 19 +- .../{rx/lang/scala => }/Observable.scala | 1092 ++++++++--------- .../src/main/scala/Observer.scala | 47 + .../src/main/scala/Scheduler.scala | 212 ++++ .../src/main/scala/WithFilter.scala | 23 + .../scala => }/concurrency/Schedulers.scala | 15 - .../scala/concurrency/TestScheduler.scala | 105 ++ .../observables/BlockingObservable.scala | 10 +- .../main/scala/rx/lang/scala/Scheduler.scala | 176 --- .../scala/concurrency/TestScheduler.scala | 120 -- .../rx/lang/scala/concurrency/package.scala | 31 - .../rx/lang/scala/observables/package.scala | 27 - .../main/scala/rx/lang/scala/package.scala | 158 --- .../rx/lang/scala/subjects/package.scala | 46 - .../main/scala/subjects/AsyncSubject.scala | 11 + .../main/scala/subjects/BehaviorSubject.scala | 15 + .../main/scala/subjects/PublishSubject.scala | 12 + .../main/scala/subjects/ReplaySubject.scala | 15 + .../src/main/scala/subjects/Subject.scala | 11 + .../subscriptions/BooleanSubscription.scala | 38 + .../subscriptions/CompositeSubscription.scala | 61 + .../MultiAssignmentSubscription.scala | 54 + .../subscriptions/SerialSubscription.scala | 48 + .../scala/subscriptions/Subscription.scala | 83 ++ .../src/main/scala/subscriptions/scala.scala | 24 + .../util/package.scala => util/util.scala} | 10 +- .../scala/RxJavaDemos.scala} | 208 ++-- .../src/test/scala/SubscriptionTests.scala | 114 ++ .../src/test/scala/UnitTestSuite.scala | 87 ++ .../rx/lang/scala/CompletenessTest.scala | 369 ------ 34 files changed, 1626 insertions(+), 1709 deletions(-) rename language-adaptors/rxjava-scala/src/examples/scala/{rx/lang/scala/examples => }/MovieLib.scala (97%) rename language-adaptors/rxjava-scala/src/examples/scala/{rx/lang/scala/examples => }/Olympics.scala (98%) rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala => }/ImplicitFunctionConversions.scala (80%) rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala => }/Notification.scala (70%) rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala => }/Observable.scala (73%) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/Observer.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala => }/concurrency/Schedulers.scala (80%) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala => }/observables/BlockingObservable.scala (95%) delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala/util/package.scala => util/util.scala} (67%) rename language-adaptors/rxjava-scala/src/{examples/scala/rx/lang/scala/examples/RxScalaDemo.scala => test/scala/RxJavaDemos.scala} (91%) create mode 100644 language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala create mode 100644 language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala delete mode 100644 language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala diff --git a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java index 84920e0d12..19a618d158 100644 --- a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java +++ b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java @@ -15,25 +15,27 @@ */ package rx.lang.scala.examples; -import org.junit.Test; - import rx.Observable; -import rx.util.functions.Action1; +import rx.lang.scala.examples.Movie; +import rx.lang.scala.examples.MovieLib; +import static rx.lang.scala.ImplicitFunctionConversions.toScalaObservable; public class MovieLibUsage { - - Action1 moviePrinter = new Action1() { - public void call(Movie m) { - System.out.println("A movie of length " + m.lengthInSeconds() + "s"); - } - }; - - @Test - public void test() { - MovieLib lib = new MovieLib(Observable.from(new Movie(3000), new Movie(1000), new Movie(2000))); - - lib.longMovies().subscribe(moviePrinter); - } + + public static void main(String[] args) { + + Observable movies = Observable.from( + new Movie(3000), + new Movie(1000), + new Movie(2000) + ); + + MovieLib lib = new MovieLib(toScalaObservable(movies)); + + lib.longMovies().asJavaObservable().subscribe(m -> + System.out.println("A movie of length " + m.lengthInSeconds() + "s") + ); + } } diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala b/language-adaptors/rxjava-scala/src/examples/scala/MovieLib.scala similarity index 97% rename from language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala rename to language-adaptors/rxjava-scala/src/examples/scala/MovieLib.scala index 1eefac79c9..8e2ce5b29b 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/MovieLib.scala @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -20,11 +20,11 @@ import rx.lang.scala.Observable class Movie(val lengthInSeconds: Int) { } class MovieLib(val moviesStream: Observable[Movie]) { - + val threshold = 1200 - + def shortMovies: Observable[Movie] = moviesStream.filter(_.lengthInSeconds <= threshold) - + def longMovies: Observable[Movie] = moviesStream.filter(_.lengthInSeconds > threshold) } diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala b/language-adaptors/rxjava-scala/src/examples/scala/Olympics.scala similarity index 98% rename from language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala rename to language-adaptors/rxjava-scala/src/examples/scala/Olympics.scala index 699523ea55..7a11bdf539 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/Olympics.scala @@ -20,7 +20,7 @@ import scala.concurrent.duration._ object Olympics { case class Medal(val year: Int, val games: String, val discipline: String, val medal: String, val athlete: String, val country: String) - + def mountainBikeMedals: Observable[Medal] = Observable( Observable( Medal(1996, "Atlanta 1996", "cross-country men", "Gold", "Bart BRENTJENS", "Netherlands"), @@ -31,7 +31,7 @@ object Olympics { Medal(1996, "Atlanta 1996", "cross-country women", "Bronze", "Susan DEMATTEI", "United States of America") ), fourYearsEmpty, - Observable( + Observable( Medal(2000, "Sydney 2000", "cross-country women", "Gold", "Paola PEZZO", "Italy"), Medal(2000, "Sydney 2000", "cross-country women", "Silver", "Barbara BLATTER", "Switzerland"), Medal(2000, "Sydney 2000", "cross-country women", "Bronze", "Marga FULLANA", "Spain"), @@ -40,7 +40,7 @@ object Olympics { Medal(2000, "Sydney 2000", "cross-country men", "Bronze", "Christoph SAUSER", "Switzerland") ), fourYearsEmpty, - Observable( + Observable( Medal(2004, "Athens 2004", "cross-country men", "Gold", "Julien ABSALON", "France"), Medal(2004, "Athens 2004", "cross-country men", "Silver", "Jose Antonio HERMIDA RAMOS", "Spain"), Medal(2004, "Athens 2004", "cross-country men", "Bronze", "Bart BRENTJENS", "Netherlands"), @@ -49,7 +49,7 @@ object Olympics { Medal(2004, "Athens 2004", "cross-country women", "Bronze", "Sabine SPITZ", "Germany") ), fourYearsEmpty, - Observable( + Observable( Medal(2008, "Beijing 2008", "cross-country women", "Gold", "Sabine SPITZ", "Germany"), Medal(2008, "Beijing 2008", "cross-country women", "Silver", "Maja WLOSZCZOWSKA", "Poland"), Medal(2008, "Beijing 2008", "cross-country women", "Bronze", "Irina KALENTYEVA", "Russian Federation"), @@ -67,12 +67,12 @@ object Olympics { Medal(2012, "London 2012", "cross-country women", "Bronze", "Georgia GOULD", "United States of America") ) ).concat - + // speed it up :D val fourYears = 4000.millis - + val neverUsedDummyMedal = Medal(3333, "?", "?", "?", "?", "?") - + def fourYearsEmpty: Observable[Medal] = { // TODO this should return an observable which emits nothing during fourYears and then completes // Because of https://github.com/Netflix/RxJava/issues/388, we get non-terminating tests @@ -82,5 +82,5 @@ object Olympics { // But we just return empty, which completes immediately Observable() } - + } \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala similarity index 80% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala rename to language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala index 5db1c673f6..67f24af638 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala @@ -15,8 +15,12 @@ */ package rx.lang.scala +import java.lang.Exception import java.{ lang => jlang } +import rx.lang.scala._ import rx.util.functions._ +import scala.collection.Seq +import rx.lang.scala.subscriptions.Subscription /** * These function conversions convert between Scala functions and Rx `Func`s and `Action`s. @@ -28,20 +32,28 @@ object ImplicitFunctionConversions { import language.implicitConversions implicit def schedulerActionToFunc2[T](action: (Scheduler, T) => Subscription) = - new Func2[rx.Scheduler, T, Subscription] { - def call(s: rx.Scheduler, t: T): Subscription = { - action(s, t) + new Func2[rx.Scheduler, T, rx.Subscription] { + def call(s: rx.Scheduler, t: T): rx.Subscription = { + action(s, t).asJavaSubscription } - } - - implicit def scalaSchedulerToJavaScheduler(s: Scheduler): rx.Scheduler = s.asJava - + } + + implicit def toJavaSubscription(s: Subscription): rx.Subscription = s.asJavaSubscription + implicit def toScalaSubscription(s: rx.Subscription): Subscription = Subscription(s) + + implicit def scalaSchedulerToJavaScheduler(s: Scheduler): rx.Scheduler = s.asJavaScheduler implicit def javaSchedulerToScalaScheduler(s: rx.Scheduler): Scheduler = Scheduler(s) + + implicit def toJavaObserver[T](s: Observer[T]): rx.Observer[_ >: T] = s.asJavaObserver + implicit def toScalaObserver[T](s: rx.Observer[T]): Observer[T] = Observer(s) + + implicit def toJavaObservable[T](s: Observable[T]): rx.Observable[_ <: T] = s.asJavaObservable + implicit def toScalaObservable[T](s: rx.Observable[T]): Observable[T] = Observable(s) implicit def scalaFunction1ToOnSubscribeFunc[T](f: rx.lang.scala.Observer[T] => Subscription) = new rx.Observable.OnSubscribeFunc[T] { def onSubscribe(obs: rx.Observer[_ >: T]): rx.Subscription = { - f(obs) + f(Observer(obs)) } } @@ -55,6 +67,10 @@ object ImplicitFunctionConversions { def call(): Unit = f() } + implicit def Action1toScalaFunction1ProducingUnit[A](f: Action1[A]): (A=>Unit) = { + a => f(a) + } + implicit def scalaFunction1ProducingUnitToAction1[A](f: (A => Unit)): Action1[A] = new Action1[A] { def call(a: A): Unit = f(a) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/Notification.scala similarity index 70% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala rename to language-adaptors/rxjava-scala/src/main/scala/Notification.scala index 133157d5ca..430cfd8e80 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/Notification.scala @@ -1,22 +1,7 @@ -/** - * Copyright 2013 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.lang.scala /** - * Emitted by Observables returned by [[Observable.materialize]]. + * Emitted by Observables returned by [[rx.lang.scala.Observable.materialize]]. */ sealed trait Notification[+T] { def asJava: rx.Notification[_ <: T] @@ -58,7 +43,7 @@ object Notification { } class OnError[+T](val asJava: rx.Notification[_ <: T]) extends Notification[T] { - def error: Throwable = asJava.getThrowable() + def error: Throwable = asJava.getThrowable } object OnError { diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/Observable.scala similarity index 73% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala rename to language-adaptors/rxjava-scala/src/main/scala/Observable.scala index 29111f4cf7..a0dfaf3601 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/Observable.scala @@ -16,22 +16,27 @@ package rx.lang.scala + +import rx.util.functions.FuncN +import rx.Observable.OnSubscribeFunc + + /** * The Observable interface that implements the Reactive Pattern. - * - * @param asJava the underlying Java observable - * + * + * @param asJavaObservable the underlying Java observable + * * @define subscribeObserverMain - * Call this method to subscribe an [[Observer]] for receiving + * Call this method to subscribe an [[rx.lang.scala.Observer]] for receiving * items and notifications from the Observable. - * + * * A typical implementation of `subscribe` does the following: * * It stores a reference to the Observer in a collection object, such as a `List[T]` object. * - * It returns a reference to the [[Subscription]] interface. This enables Observers to + * It returns a reference to the [[rx.lang.scala.Subscription]] interface. This enables Observers to * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's [[Observer.onCompleted onCompleted]] method. + * sending them, which also invokes the Observer's [[rx.lang.scala.Observer.onCompleted onCompleted]] method. * * An `Observable[T]` instance is responsible for accepting all subscriptions * and notifying all Observers. Unless the documentation for a particular @@ -41,17 +46,17 @@ package rx.lang.scala * @define subscribeObserverParamObserver * the observer * @define subscribeObserverParamScheduler - * the [[Scheduler]] on which Observers subscribe to the Observable + * the [[rx.lang.scala.Scheduler]] on which Observers subscribe to the Observable * @define subscribeAllReturn - * a [[Subscription]] reference whose `unsubscribe` method can be called to stop receiving items + * a [[rx.lang.scala.Subscription]] reference whose `unsubscribe` method can be called to stop receiving items * before the Observable has finished sending them - * + * * @define subscribeCallbacksMainWithNotifications * Call this method to receive items and notifications from this observable. - * + * * @define subscribeCallbacksMainNoNotifications * Call this method to receive items from this observable. - * + * * @define subscribeCallbacksParamOnNext * this function will be called whenever the Observable emits an item * @define subscribeCallbacksParamOnError @@ -67,64 +72,62 @@ package rx.lang.scala * - [[http://unscriptable.com/2009/03/20/debouncing-javascript-methods/]] * - [[http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/]] * - * + * */ -// constructor is private because users should use apply in companion -class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) - // Uncommenting this line combined with `new Observable(...)` instead of `new Observable[T](...)` - // makes the compiler crash - extends AnyVal +trait Observable[+T] { - import scala.collection.JavaConverters._ import scala.collection.Seq import scala.concurrent.duration.{Duration, TimeUnit} - import rx.{Observable => JObservable} import rx.util.functions._ import rx.lang.scala.util._ - import rx.lang.scala.subjects.Subject import rx.lang.scala.observables.BlockingObservable import rx.lang.scala.ImplicitFunctionConversions._ - + + def asJavaObservable: rx.Observable[_ <: T] + /** * $subscribeObserverMain - * + * * @param observer $subscribeObserverParamObserver * @param scheduler $subscribeObserverParamScheduler * @return $subscribeAllReturn */ def subscribe(observer: Observer[T], scheduler: Scheduler): Subscription = { - asJava.subscribe(observer, scheduler) + asJavaObservable.subscribe(observer.asJavaObserver, scheduler) } /** * $subscribeObserverMain - * + * * @param observer $subscribeObserverParamObserver * @return $subscribeAllReturn */ def subscribe(observer: Observer[T]): Subscription = { - asJava.subscribe(observer) + asJavaObservable.subscribe(observer.asJavaObserver) } /** * $subscribeCallbacksMainNoNotifications - * + * `` * @param onNext $subscribeCallbacksParamOnNext * @return $subscribeAllReturn */ def subscribe(onNext: T => Unit): Subscription = { - asJava.subscribe(onNext) + asJavaObservable.subscribe(scalaFunction1ProducingUnitToAction1(onNext)) } - + /** * $subscribeCallbacksMainWithNotifications * * @param onNext $subscribeCallbacksParamOnNext * @param onError $subscribeCallbacksParamOnError * @return $subscribeAllReturn - */ + */ def subscribe(onNext: T => Unit, onError: Throwable => Unit): Subscription = { - asJava.subscribe(onNext, onError) + asJavaObservable.subscribe( + scalaFunction1ProducingUnitToAction1(onNext), + scalaFunction1ProducingUnitToAction1(onError) + ) } /** @@ -132,11 +135,15 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * @param onNext $subscribeCallbacksParamOnNext * @param onError $subscribeCallbacksParamOnError - * @param onComplete $subscribeCallbacksParamOnComplete + * @param onCompleted $subscribeCallbacksParamOnComplete * @return $subscribeAllReturn */ - def subscribe(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit): Subscription = { - asJava.subscribe(onNext, onError, onComplete) + def subscribe(onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit): Subscription = { + asJavaObservable.subscribe( + scalaFunction1ProducingUnitToAction1(onNext), + scalaFunction1ProducingUnitToAction1(onError), + scalaFunction0ProducingUnitToAction0(onCompleted) + ) } /** @@ -144,14 +151,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * @param onNext $subscribeCallbacksParamOnNext * @param onError $subscribeCallbacksParamOnError - * @param onComplete $subscribeCallbacksParamOnComplete + * @param onCompleted $subscribeCallbacksParamOnComplete * @param scheduler $subscribeCallbacksParamScheduler * @return $subscribeAllReturn */ - def subscribe(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit, scheduler: Scheduler): Subscription = { - asJava.subscribe(onNext, onError, onComplete, scheduler) + def subscribe(onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit, scheduler: Scheduler): Subscription = { + asJavaObservable.subscribe(scalaFunction1ProducingUnitToAction1(onNext), + scalaFunction1ProducingUnitToAction1(onError), + scalaFunction0ProducingUnitToAction0(onCompleted), + scheduler) } - + /** * $subscribeCallbacksMainWithNotifications * @@ -161,7 +171,10 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return $subscribeAllReturn */ def subscribe(onNext: T => Unit, onError: Throwable => Unit, scheduler: Scheduler): Subscription = { - asJava.subscribe(onNext, onError, scheduler) + asJavaObservable.subscribe( + scalaFunction1ProducingUnitToAction1(onNext), + scalaFunction1ProducingUnitToAction1(onError), + scheduler) } /** @@ -172,56 +185,56 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return $subscribeAllReturn */ def subscribe(onNext: T => Unit, scheduler: Scheduler): Subscription = { - asJava.subscribe(onNext, scheduler) + asJavaObservable.subscribe(scalaFunction1ProducingUnitToAction1(onNext), scheduler) } /** - * Returns a pair of a start function and an [[Observable]] that upon calling the start function causes the source Observable to + * Returns a pair of a start function and an [[rx.lang.scala.Observable]] that upon calling the start function causes the source Observable to * push results into the specified subject. - * + * * @param subject * the `rx.lang.scala.subjects.Subject` to push source items into * @tparam R * result type - * @return a pair of a start function and an [[Observable]] such that when the start function + * @return a pair of a start function and an [[rx.lang.scala.Observable]] such that when the start function * is called, the Observable starts to push results into the specified Subject */ - def multicast[R](subject: Subject[T, R]): (() => Subscription, Observable[R]) = { - val javaCO = asJava.multicast[R](subject) + def multicast[R](subject: rx.lang.scala.Subject[T, R]): (() => Subscription, Observable[R]) = { + val javaCO = asJavaObservable.multicast[R](subject.asJavaSubject) (() => javaCO.connect(), Observable[R](javaCO)) } - + /** * Returns an Observable that first emits the items emitted by `this`, and then the items emitted * by `that`. * * - * + * * @param that * an Observable to be appended * @return an Observable that emits items that are the result of combining the items emitted by * this and that, one after the other */ def ++[U >: T](that: Observable[U]): Observable[U] = { - val o1: JObservable[_ <: U] = this.asJava - val o2: JObservable[_ <: U] = that.asJava - Observable(JObservable.concat(o1, o2)) + val o1: rx.Observable[_ <: U] = this.asJavaObservable + val o2: rx.Observable[_ <: U] = that.asJavaObservable + Observable(rx.Observable.concat(o1, o2)) } /** * Returns an Observable that emits the items emitted by several Observables, one after the * other. - * + * * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, * otherwise you'll get a compilation error. - * + * * @usecase def concat[U]: Observable[U] * @inheritdoc */ def concat[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava + val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) + val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable val o5 = rx.Observable.concat[U](o4) Observable[U](o5) } @@ -232,31 +245,29 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * A well-behaved Observable does not interleave its invocations of the [[Observer.onNext onNext]], [[Observer.onCompleted onCompleted]], and [[Observer.onError onError]] methods of - * its [[Observer]]s; it invokes `onCompleted` or `onError` only once; and it never invokes `onNext` after invoking either `onCompleted` or `onError`. + * A well-behaved Observable does not interleave its invocations of the [[rx.lang.scala.Observer.onNext onNext]], [[rx.lang.scala.Observer.onCompleted onCompleted]], and [[rx.lang.scala.Observer.onError onError]] methods of + * its [[rx.lang.scala.Observer]]s; it invokes `onCompleted` or `onError` only once; and it never invokes `onNext` after invoking either `onCompleted` or `onError`. * `synchronize` enforces this, and the Observable it returns invokes `onNext` and `onCompleted` or `onError` synchronously. - * - * @param observable - * the source Observable + * * @return an Observable that is a chronologically well-behaved version of the source - * Observable, and that synchronously notifies its [[Observer]]s + * Observable, and that synchronously notifies its [[rx.lang.scala.Observer]]s */ def synchronize: Observable[T] = { - Observable[T](asJava.synchronize) + Observable[T](asJavaObservable.synchronize) } - + /** * Wraps each item emitted by a source Observable in a timestamped tuple. * * - * + * * @return an Observable that emits timestamped items from the source Observable */ def timestamp: Observable[(Long, T)] = { - Observable[rx.util.Timestamped[_ <: T]](asJava.timestamp()) - .map((t: rx.util.Timestamped[_ <: T]) => (t.getTimestampMillis, t.getValue())) + Observable[rx.util.Timestamped[_ <: T]](asJavaObservable.timestamp()) + .map((t: rx.util.Timestamped[_ <: T]) => (t.getTimestampMillis, t.getValue)) } - + /** * Returns an Observable formed from this Observable and another Observable by combining * corresponding elements in pairs. @@ -264,161 +275,161 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * is the minumum of the number of `onNext` invocations of `this` and `that`. */ def zip[U](that: Observable[U]): Observable[(T, U)] = { - Observable[(T, U)](JObservable.zip[T, U, (T, U)](this.asJava, that.asJava, (t: T, u: U) => (t, u))) + Observable[(T, U)](rx.Observable.zip[T, U, (T, U)](this.asJavaObservable, that.asJavaObservable, (t: T, u: U) => (t, u))) } /** * Zips this Observable with its indices. - * + * * @return An Observable emitting pairs consisting of all elements of this Observable paired with * their index. Indices start at 0. */ def zipWithIndex: Observable[(T, Int)] = { val fScala: (T, Integer) => (T, Int) = (elem: T, index: Integer) => (elem, index) val fJava : Func2[_ >: T, Integer, _ <: (T, Int)] = fScala - Observable[(T, Int)](asJava.mapWithIndex[(T, Int)](fJava)) + Observable[(T, Int)](asJavaObservable.mapWithIndex[(T, Int)](fJava)) } - + /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces connected non-overlapping buffers. The current buffer is * emitted and replaced with a new buffer when the Observable produced by the specified function produces a [[rx.lang.scala.util.Closing]] object. The function will then * be used to create a new Observable to listen for the end of the next buffer. - * + * * @param closings - * The function which is used to produce an [[Observable]] for every buffer created. - * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer + * The function which is used to produce an [[rx.lang.scala.Observable]] for every buffer created. + * When this [[rx.lang.scala.Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer * is emitted and replaced with a new one. * @return - * An [[Observable]] which produces connected non-overlapping buffers, which are emitted - * when the current [[Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers, which are emitted + * when the current [[rx.lang.scala.Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. */ def buffer(closings: () => Observable[Closing]) : Observable[Seq[T]] = { - val f: Func0[_ <: rx.Observable[_ <: Closing]] = closings().asJava - val jObs: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(f) + val f: Func0[_ <: rx.Observable[_ <: Closing]] = closings().asJavaObservable + val jObs: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(f) Observable.jObsOfListToScObsOfSeq(jObs.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces buffers. Buffers are created when the specified `openings` * Observable produces a [[rx.lang.scala.util.Opening]] object. Additionally the function argument * is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. When this * Observable produces such an object, the associated buffer is emitted. - * + * * @param openings - * The [[Observable]] which, when it produces a [[rx.lang.scala.util.Opening]] object, will cause + * The [[rx.lang.scala.Observable]] which, when it produces a [[rx.lang.scala.util.Opening]] object, will cause * another buffer to be created. * @param closings - * The function which is used to produce an [[Observable]] for every buffer created. - * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer + * The function which is used to produce an [[rx.lang.scala.Observable]] for every buffer created. + * When this [[rx.lang.scala.Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer * is emitted. * @return - * An [[Observable]] which produces buffers which are created and emitted when the specified [[Observable]]s publish certain objects. + * An [[rx.lang.scala.Observable]] which produces buffers which are created and emitted when the specified [[rx.lang.scala.Observable]]s publish certain objects. */ def buffer(openings: Observable[Opening], closings: Opening => Observable[Closing]): Observable[Seq[T]] = { - val opening: rx.Observable[_ <: Opening] = openings.asJava - val closing: Func1[Opening, _ <: rx.Observable[_ <: Closing]] = (o: Opening) => closings(o).asJava - val jObs: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(opening, closing) + val opening: rx.Observable[_ <: Opening] = openings.asJavaObservable + val closing: Func1[Opening, _ <: rx.Observable[_ <: Closing]] = (o: Opening) => closings(o).asJavaObservable + val jObs: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(opening, closing) Observable.jObsOfListToScObsOfSeq(jObs.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces connected non-overlapping buffers, each containing `count` * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. - * + * * @param count * The maximum size of each buffer before it should be emitted. * @return - * An [[Observable]] which produces connected non-overlapping buffers containing at most + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers containing at most * `count` produced values. */ def buffer(count: Int): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(count) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(count) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces buffers every `skip` values, each containing `count` * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. - * + * * @param count * The maximum size of each buffer before it should be emitted. * @param skip * How many produced values need to be skipped before starting a new buffer. Note that when `skip` and * `count` are equals that this is the same operation as `buffer(int)`. * @return - * An [[Observable]] which produces buffers every `skip` values containing at most + * An [[rx.lang.scala.Observable]] which produces buffers every `skip` values containing at most * `count` produced values. */ def buffer(count: Int, skip: Int): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(count, skip) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(count, skip) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } - + /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces connected non-overlapping buffers, each of a fixed duration * specified by the `timespan` argument. When the source Observable completes or encounters * an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. * @return - * An [[Observable]] which produces connected non-overlapping buffers with a fixed duration. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers with a fixed duration. */ def buffer(timespan: Duration): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(timespan.length, timespan.unit) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces connected non-overlapping buffers, each of a fixed duration * specified by the `timespan` argument. When the source Observable completes or encounters * an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a buffer. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a buffer. * @return - * An [[Observable]] which produces connected non-overlapping buffers with a fixed duration. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers with a fixed duration. */ def buffer(timespan: Duration, scheduler: Scheduler): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(timespan.length, timespan.unit, scheduler) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit, scheduler) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } + } /** * Creates an Observable which produces buffers of collected values. This Observable produces connected * non-overlapping buffers, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. * @param count * The maximum size of each buffer before it should be emitted. * @return - * An [[Observable]] which produces connected non-overlapping buffers which are emitted after + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers which are emitted after * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). */ def buffer(timespan: Duration, count: Int): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(timespan.length, timespan.unit, count) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit, count) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } @@ -427,20 +438,20 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * non-overlapping buffers, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. * @param count * The maximum size of each buffer before it should be emitted. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a buffer. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a buffer. * @return - * An [[Observable]] which produces connected non-overlapping buffers which are emitted after + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers which are emitted after * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). */ def buffer(timespan: Duration, count: Int, scheduler: Scheduler): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(timespan.length, timespan.unit, count, scheduler) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit, count, scheduler) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } @@ -449,46 +460,46 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * periodically, which is determined by the `timeshift` argument. Each buffer is emitted after a fixed timespan * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted. * @param timeshift * The period of time after which a new buffer will be created. * @return - * An [[Observable]] which produces new buffers periodically, and these are emitted after + * An [[rx.lang.scala.Observable]] which produces new buffers periodically, and these are emitted after * a fixed timespan has elapsed. */ def buffer(timespan: Duration, timeshift: Duration): Observable[Seq[T]] = { val span: Long = timespan.length val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) val unit: TimeUnit = timespan.unit - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(span, shift, unit) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(span, shift, unit) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } - + } + /** * Creates an Observable which produces buffers of collected values. This Observable starts a new buffer * periodically, which is determined by the `timeshift` argument. Each buffer is emitted after a fixed timespan * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted. * @param timeshift * The period of time after which a new buffer will be created. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a buffer. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a buffer. * @return - * An [[Observable]] which produces new buffers periodically, and these are emitted after + * An [[rx.lang.scala.Observable]] which produces new buffers periodically, and these are emitted after * a fixed timespan has elapsed. */ def buffer(timespan: Duration, timeshift: Duration, scheduler: Scheduler): Observable[Seq[T]] = { val span: Long = timespan.length val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) val unit: TimeUnit = timespan.unit - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(span, shift, unit, scheduler) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(span, shift, unit, scheduler) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } + } /** * Creates an Observable which produces windows of collected values. This Observable produces connected @@ -496,19 +507,19 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Observable produced by the specified function produces a [[rx.lang.scala.util.Closing]] object. * The function will then be used to create a new Observable to listen for the end of the next * window. - * + * * @param closings - * The function which is used to produce an [[Observable]] for every window created. - * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window + * The function which is used to produce an [[rx.lang.scala.Observable]] for every window created. + * When this [[rx.lang.scala.Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window * is emitted and replaced with a new one. * @return - * An [[Observable]] which produces connected non-overlapping windows, which are emitted - * when the current [[Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows, which are emitted + * when the current [[rx.lang.scala.Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. */ def window(closings: () => Observable[Closing]): Observable[Observable[T]] = { - val func : Func0[_ <: rx.Observable[_ <: Closing]] = closings().asJava - val o1: rx.Observable[_ <: rx.Observable[_]] = asJava.window(func) - val o2 = new Observable[rx.Observable[_]](o1).map((x: rx.Observable[_]) => { + val func : Func0[_ <: rx.Observable[_ <: Closing]] = closings().asJavaObservable + val o1: rx.Observable[_ <: rx.Observable[_]] = asJavaObservable.window(func) + val o2 = Observable[rx.Observable[_]](o1).map((x: rx.Observable[_]) => { val x2 = x.asInstanceOf[rx.Observable[_ <: T]] Observable[T](x2) }) @@ -520,207 +531,207 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Chunks are created when the specified `openings` Observable produces a [[rx.lang.scala.util.Opening]] object. * Additionally the `closings` argument is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. * When this Observable produces such an object, the associated window is emitted. - * + * * @param openings - * The [[Observable]] which when it produces a [[rx.lang.scala.util.Opening]] object, will cause + * The [[rx.lang.scala.Observable]] which when it produces a [[rx.lang.scala.util.Opening]] object, will cause * another window to be created. * @param closings - * The function which is used to produce an [[Observable]] for every window created. - * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window + * The function which is used to produce an [[rx.lang.scala.Observable]] for every window created. + * When this [[rx.lang.scala.Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window * is emitted. * @return - * An [[Observable]] which produces windows which are created and emitted when the specified [[Observable]]s publish certain objects. + * An [[rx.lang.scala.Observable]] which produces windows which are created and emitted when the specified [[rx.lang.scala.Observable]]s publish certain objects. */ def window(openings: Observable[Opening], closings: Opening => Observable[Closing]) = { Observable.jObsOfJObsToScObsOfScObs( - asJava.window(openings.asJava, (op: Opening) => closings(op).asJava)) - : Observable[Observable[T]] // SI-7818 - } + asJavaObservable.window(openings.asJavaObservable, (op: Opening) => closings(op).asJavaObservable)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each containing `count` elements. When the source Observable completes or * encounters an error, the current window is emitted, and the event is propagated. - * + * * @param count * The maximum size of each window before it should be emitted. * @return - * An [[Observable]] which produces connected non-overlapping windows containing at most + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows containing at most * `count` produced values. */ def window(count: Int): Observable[Observable[T]] = { // this unnecessary ascription is needed because of this bug (without, compiler crashes): // https://issues.scala-lang.org/browse/SI-7818 - Observable.jObsOfJObsToScObsOfScObs(asJava.window(count)) : Observable[Observable[T]] + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(count)) : Observable[Observable[T]] } /** * Creates an Observable which produces windows of collected values. This Observable produces windows every * `skip` values, each containing `count` elements. When the source Observable completes or encounters an error, * the current window is emitted and the event is propagated. - * + * * @param count * The maximum size of each window before it should be emitted. * @param skip * How many produced values need to be skipped before starting a new window. Note that when `skip` and * `count` are equal that this is the same operation as `window(int)`. * @return - * An [[Observable]] which produces windows every `skip` values containing at most + * An [[rx.lang.scala.Observable]] which produces windows every `skip` values containing at most * `count` produced values. */ def window(count: Int, skip: Int): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJava.window(count, skip)) - : Observable[Observable[T]] // SI-7818 + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(count, skip)) + : Observable[Observable[T]] // SI-7818 } /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the `timespan` argument. When the source * Observable completes or encounters an error, the current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted, and * replaced with a new window. * @return - * An [[Observable]] which produces connected non-overlapping windows with a fixed duration. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows with a fixed duration. */ def window(timespan: Duration): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJava.window(timespan.length, timespan.unit)) - : Observable[Observable[T]] // SI-7818 - } + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the `timespan` argument. When the source * Observable completes or encounters an error, the current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted, and * replaced with a new window. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a window. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a window. * @return - * An [[Observable]] which produces connected non-overlapping windows with a fixed duration. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows with a fixed duration. */ def window(timespan: Duration, scheduler: Scheduler): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJava.window(timespan.length, timespan.unit, scheduler)) - : Observable[Observable[T]] // SI-7818 - } + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit, scheduler)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted, and * replaced with a new window. * @param count * The maximum size of each window before it should be emitted. * @return - * An [[Observable]] which produces connected non-overlapping windows which are emitted after + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows which are emitted after * a fixed duration or when the window has reached maximum capacity (which ever occurs first). */ def window(timespan: Duration, count: Int): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJava.window(timespan.length, timespan.unit, count)) - : Observable[Observable[T]] // SI-7818 - } + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit, count)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted, and * replaced with a new window. * @param count * The maximum size of each window before it should be emitted. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a window. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a window. * @return - * An [[Observable]] which produces connected non-overlapping windows which are emitted after + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows which are emitted after * a fixed duration or when the window has reached maximum capacity (which ever occurs first). */ def window(timespan: Duration, count: Int, scheduler: Scheduler): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJava.window(timespan.length, timespan.unit, count, scheduler)) - : Observable[Observable[T]] // SI-7818 - } + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit, count, scheduler)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable starts a new window * periodically, which is determined by the `timeshift` argument. Each window is emitted after a fixed timespan * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted. * @param timeshift * The period of time after which a new window will be created. * @return - * An [[Observable]] which produces new windows periodically, and these are emitted after + * An [[rx.lang.scala.Observable]] which produces new windows periodically, and these are emitted after * a fixed timespan has elapsed. */ def window(timespan: Duration, timeshift: Duration): Observable[Observable[T]] = { val span: Long = timespan.length val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) val unit: TimeUnit = timespan.unit - Observable.jObsOfJObsToScObsOfScObs(asJava.window(span, shift, unit)) - : Observable[Observable[T]] // SI-7818 - } + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(span, shift, unit)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable starts a new window * periodically, which is determined by the `timeshift` argument. Each window is emitted after a fixed timespan * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted. * @param timeshift * The period of time after which a new window will be created. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a window. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a window. * @return - * An [[Observable]] which produces new windows periodically, and these are emitted after + * An [[rx.lang.scala.Observable]] which produces new windows periodically, and these are emitted after * a fixed timespan has elapsed. */ def window(timespan: Duration, timeshift: Duration, scheduler: Scheduler): Observable[Observable[T]] = { val span: Long = timespan.length val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) val unit: TimeUnit = timespan.unit - Observable.jObsOfJObsToScObsOfScObs(asJava.window(span, shift, unit, scheduler)) - : Observable[Observable[T]] // SI-7818 - } - + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(span, shift, unit, scheduler)) + : Observable[Observable[T]] // SI-7818 + } + /** * Returns an Observable which only emits those items for which a given predicate holds. - * + * * - * + * * @param predicate * a function that evaluates the items emitted by the source Observable, returning `true` if they pass the filter * @return an Observable that emits only those items in the original Observable that the filter * evaluates as `true` */ def filter(predicate: T => Boolean): Observable[T] = { - Observable[T](asJava.filter(predicate)) + Observable[T](asJavaObservable.filter(predicate)) } /** - * Registers an function to be called when this Observable invokes [[Observer.onCompleted onCompleted]] or [[Observer.onError onError]]. + * Registers an function to be called when this Observable invokes [[rx.lang.scala.Observer.onCompleted onCompleted]] or [[rx.lang.scala.Observer.onError onError]]. * * - * + * * @param action * an function to be invoked when the source Observable finishes * @return an Observable that emits the same items as the source Observable, then invokes the function */ def finallyDo(action: () => Unit): Observable[T] = { - Observable[T](asJava.finallyDo(action)) - } + Observable[T](asJavaObservable.finallyDo(action)) + } /** * Creates a new Observable by applying a function that you supply to each item emitted by @@ -728,8 +739,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * resulting Observables and emitting the results of this merger. * * - * - * @param func + * + * @param f * a function that, when applied to an item emitted by the source Observable, returns * an Observable * @return an Observable that emits the result of applying the transformation function to each @@ -737,201 +748,206 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * obtained from this transformation. */ def flatMap[R](f: T => Observable[R]): Observable[R] = { - Observable[R](asJava.flatMap[R]((t: T) => f(t).asJava)) + Observable[R](asJavaObservable.flatMap[R](new Func1[T, rx.Observable[_ <: R]]{ + def call(t1: T): rx.Observable[_ <: R] = { f(t1).asJavaObservable } + })) } - + /** * Returns an Observable that applies the given function to each item emitted by an * Observable and emits the result. * * - * + * * @param func * a function to apply to each item emitted by the Observable * @return an Observable that emits the items from the source Observable, transformed by the * given function */ def map[R](func: T => R): Observable[R] = { - Observable[R](asJava.map[R](func)) + Observable[R](asJavaObservable.map[R](new Func1[T,R] { + def call(t1: T): R = func(t1) + })) } - + /** - * Turns all of the notifications from a source Observable into [[Observer.onNext onNext]] emissions, and marks them with their original notification types within [[Notification]] objects. + * Turns all of the notifications from a source Observable into [[rx.lang.scala.Observer.onNext onNext]] emissions, + * and marks them with their original notification types within [[rx.lang.scala.Notification]] objects. * * - * + * * @return an Observable whose items are the result of materializing the items and * notifications of the source Observable */ def materialize: Observable[Notification[T]] = { - Observable[rx.Notification[_ <: T]](asJava.materialize()).map(Notification(_)) + Observable[rx.Notification[_ <: T]](asJavaObservable.materialize()).map(Notification(_)) } /** - * Asynchronously subscribes and unsubscribes Observers on the specified [[Scheduler]]. + * Asynchronously subscribes and unsubscribes Observers on the specified [[rx.lang.scala.Scheduler]]. * * - * + * * @param scheduler - * the [[Scheduler]] to perform subscription and unsubscription actions on + * the [[rx.lang.scala.Scheduler]] to perform subscription and unsubscription actions on * @return the source Observable modified so that its subscriptions and unsubscriptions happen - * on the specified [[Scheduler]] + * on the specified [[rx.lang.scala.Scheduler]] */ def subscribeOn(scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.subscribeOn(scheduler)) - } + Observable[T](asJavaObservable.subscribeOn(scheduler)) + } /** - * Asynchronously notify [[Observer]]s on the specified [[Scheduler]]. + * Asynchronously notify [[rx.lang.scala.Observer]]s on the specified [[rx.lang.scala.Scheduler]]. * * - * + * * @param scheduler - * the [[Scheduler]] to notify [[Observer]]s on - * @return the source Observable modified so that its [[Observer]]s are notified on the - * specified [[Scheduler]] + * the [[rx.lang.scala.Scheduler]] to notify [[rx.lang.scala.Observer]]s on + * @return the source Observable modified so that its [[rx.lang.scala.Observer]]s are notified on the + * specified [[rx.lang.scala.Scheduler]] */ def observeOn(scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.observeOn(scheduler)) + Observable[T](asJavaObservable.observeOn(scheduler)) } - + /** - * Returns an Observable that reverses the effect of [[Observable.materialize]] by - * transforming the [[Notification]] objects emitted by the source Observable into the items + * Returns an Observable that reverses the effect of [[rx.lang.scala.Observable.materialize]] by + * transforming the [[rx.lang.scala.Notification]] objects emitted by the source Observable into the items * or notifications they represent. - * + * * This operation is only available if `this` is of type `Observable[Notification[U]]` for some `U`, * otherwise you will get a compilation error. * * - * - * @return an Observable that emits the items and notifications embedded in the [[Notification]] objects emitted by the source Observable - * + * + * @return an Observable that emits the items and notifications embedded in the [[rx.lang.scala.Notification]] objects emitted by the source Observable + * * @usecase def dematerialize[U]: Observable[U] * @inheritdoc - * + * */ // with =:= it does not work, why? def dematerialize[U](implicit evidence: Observable[T] <:< Observable[Notification[U]]): Observable[U] = { val o1: Observable[Notification[U]] = this val o2: Observable[rx.Notification[_ <: U]] = o1.map(_.asJava) - val o3 = o2.asJava.dematerialize[U]() + val o3 = o2.asJavaObservable.dematerialize[U]() Observable[U](o3) } - + /** - * Instruct an Observable to pass control to another Observable rather than invoking [[Observer.onError onError]] if it encounters an error. + * Instruct an Observable to pass control to another Observable rather than invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error. * * * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[Observer]], the Observable invokes its Observer's + * expected item to its [[rx.lang.scala.Observer]], the Observable invokes its Observer's * `onError` method, and then quits without invoking any more of its Observer's * methods. The `onErrorResumeNext` method changes this behavior. If you pass a * function that returns an Observable (`resumeFunction`) to * `onErrorResumeNext`, if the original Observable encounters an error, instead of * invoking its Observer's `onError` method, it will instead relinquish control to * the Observable returned from `resumeFunction`, which will invoke the Observer's - * [[Observer.onNext onNext]] method if it is able to do so. In such a case, because no + * [[rx.lang.scala.Observer.onNext onNext]] method if it is able to do so. In such a case, because no * Observable necessarily invokes `onError`, the Observer may never know that an * error happened. * * You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. - * + * * @param resumeFunction * a function that returns an Observable that will take over if the source Observable * encounters an error * @return the original Observable, with appropriately modified behavior */ def onErrorResumeNext[U >: T](resumeFunction: Throwable => Observable[U]): Observable[U] = { - val f: Func1[Throwable, rx.Observable[_ <: U]] = (t: Throwable) => resumeFunction(t).asJava + val f: Func1[Throwable, rx.Observable[_ <: U]] = (t: Throwable) => resumeFunction(t).asJavaObservable val f2 = f.asInstanceOf[Func1[Throwable, rx.Observable[Nothing]]] - Observable[U](asJava.onErrorResumeNext(f2)) + Observable[U](asJavaObservable.onErrorResumeNext(f2)) } - + /** - * Instruct an Observable to pass control to another Observable rather than invoking [[Observer.onError onError]] if it encounters an error. + * Instruct an Observable to pass control to another Observable rather than invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error. * * * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[Observer]], the Observable invokes its Observer's + * expected item to its [[rx.lang.scala.Observer]], the Observable invokes its Observer's * `onError` method, and then quits without invoking any more of its Observer's * methods. The `onErrorResumeNext` method changes this behavior. If you pass * another Observable (`resumeSequence`) to an Observable's * `onErrorResumeNext` method, if the original Observable encounters an error, * instead of invoking its Observer's `onError` method, it will instead relinquish - * control to `resumeSequence` which will invoke the Observer's [[Observer.onNext onNext]] + * control to `resumeSequence` which will invoke the Observer's [[rx.lang.scala.Observer.onNext onNext]] * method if it is able to do so. In such a case, because no * Observable necessarily invokes `onError`, the Observer may never know that an * error happened. * * You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. - * + * * @param resumeSequence * a function that returns an Observable that will take over if the source Observable * encounters an error * @return the original Observable, with appropriately modified behavior */ def onErrorResumeNext[U >: T](resumeSequence: Observable[U]): Observable[U] = { - val rSeq1: rx.Observable[_ <: U] = resumeSequence.asJava + val rSeq1: rx.Observable[_ <: U] = resumeSequence.asJavaObservable val rSeq2: rx.Observable[Nothing] = rSeq1.asInstanceOf[rx.Observable[Nothing]] - Observable[U](asJava.onErrorResumeNext(rSeq2)) + Observable[U](asJavaObservable.onErrorResumeNext(rSeq2)) } /** - * Instruct an Observable to pass control to another Observable rather than invoking [[Observer.onError onError]] if it encounters an error of type `java.lang.Exception`. + * Instruct an Observable to pass control to another Observable rather than invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error of type `java.lang.Exception`. * * This differs from `Observable.onErrorResumeNext` in that this one does not handle `java.lang.Throwable` or `java.lang.Error` but lets those continue through. * * * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[Observer]], the Observable invokes its Observer's + * expected item to its [[rx.lang.scala.Observer]], the Observable invokes its Observer's * `onError` method, and then quits without invoking any more of its Observer's * methods. The `onErrorResumeNext` method changes this behavior. If you pass * another Observable (`resumeSequence`) to an Observable's * `onErrorResumeNext` method, if the original Observable encounters an error, * instead of invoking its Observer's `onError` method, it will instead relinquish - * control to `resumeSequence` which will invoke the Observer's [[Observer.onNext onNext]] + * control to `resumeSequence` which will invoke the Observer's [[rx.lang.scala.Observer.onNext onNext]] * method if it is able to do so. In such a case, because no * Observable necessarily invokes `onError`, the Observer may never know that an * error happened. * * You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. - * + * * @param resumeSequence * a function that returns an Observable that will take over if the source Observable * encounters an error * @return the original Observable, with appropriately modified behavior - */ + */ def onExceptionResumeNext[U >: T](resumeSequence: Observable[U]): Observable[U] = { - val rSeq1: rx.Observable[_ <: U] = resumeSequence.asJava + val rSeq1: rx.Observable[_ <: U] = resumeSequence.asJavaObservable val rSeq2: rx.Observable[Nothing] = rSeq1.asInstanceOf[rx.Observable[Nothing]] - Observable[U](asJava.onExceptionResumeNext(rSeq2)) + Observable[U](asJavaObservable.onExceptionResumeNext(rSeq2)) } /** * Instruct an Observable to emit an item (returned by a specified function) rather than - * invoking [[Observer.onError onError]] if it encounters an error. + * invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error. * * * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[Observer]], the Observable invokes its Observer's + * expected item to its [[rx.lang.scala.Observer]], the Observable invokes its Observer's * `onError` method, and then quits without invoking any more of its Observer's * methods. The `onErrorReturn` method changes this behavior. If you pass a function * (`resumeFunction`) to an Observable's `onErrorReturn` method, if the * original Observable encounters an error, instead of invoking its Observer's * `onError` method, it will instead pass the return value of - * `resumeFunction` to the Observer's [[Observer.onNext onNext]] method. + * `resumeFunction` to the Observer's [[rx.lang.scala.Observer.onNext onNext]] method. * * You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. - * + * * @param resumeFunction * a function that returns an item that the new Observable will emit if the source * Observable encounters an error @@ -940,8 +956,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) def onErrorReturn[U >: T](resumeFunction: Throwable => U): Observable[U] = { val f1: Func1[Throwable, _ <: U] = resumeFunction val f2 = f1.asInstanceOf[Func1[Throwable, Nothing]] - Observable[U](asJava.onErrorReturn(f2)) - } + Observable[U](asJavaObservable.onErrorReturn(f2)) + } /** * Returns an Observable that applies a function of your choosing to the first item emitted by a @@ -955,69 +971,69 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, * has an `inject` method that does a similar operation on lists. - * + * * @param accumulator * An accumulator function to be invoked on each item emitted by the source * Observable, whose result will be used in the next accumulator call * @return an Observable that emits a single item that is the result of accumulating the * output from the source Observable */ - def reduce[U >: T](f: (U, U) => U): Observable[U] = { - val func: Func2[_ >: U, _ >: U, _ <: U] = f + def reduce[U >: T](accumulator: (U, U) => U): Observable[U] = { + val func: Func2[_ >: U, _ >: U, _ <: U] = accumulator val func2 = func.asInstanceOf[Func2[T, T, T]] - Observable[U](asJava.asInstanceOf[rx.Observable[T]].reduce(func2)) - } + Observable[U](asJavaObservable.asInstanceOf[rx.Observable[T]].reduce(func2)) + } /** - * Returns a pair of a start function and an [[Observable]] that shares a single subscription to the underlying - * Observable that will replay all of its items and notifications to any future [[Observer]]. + * Returns a pair of a start function and an [[rx.lang.scala.Observable]] that shares a single subscription to the underlying + * Observable that will replay all of its items and notifications to any future [[rx.lang.scala.Observer]]. * * - * - * @return a pair of a start function and an [[Observable]] such that when the start function - * is called, the Observable starts to emit items to its [[Observer]]s + * + * @return a pair of a start function and an [[rx.lang.scala.Observable]] such that when the start function + * is called, the Observable starts to emit items to its [[rx.lang.scala.Observer]]s */ def replay: (() => Subscription, Observable[T]) = { - val javaCO = asJava.replay() + val javaCO = asJavaObservable.replay() (() => javaCO.connect(), Observable[T](javaCO)) } /** - * This method has similar behavior to [[Observable.replay]] except that this auto-subscribes to + * This method has similar behavior to [[rx.lang.scala.Observable.replay]] except that this auto-subscribes to * the source Observable rather than returning a start function and an Observable. * * * * This is useful when you want an Observable to cache responses and you can't control the - * subscribe/unsubscribe behavior of all the [[Observer]]s. + * subscribe/unsubscribe behavior of all the [[rx.lang.scala.Observer]]s. * * NOTE: You sacrifice the ability to unsubscribe from the origin when you use the * `cache()` operator so be careful not to use this operator on Observables that * emit an infinite or very large number of items that will use up memory. - * + * * @return an Observable that when first subscribed to, caches all of its notifications for * the benefit of subsequent subscribers. */ def cache: Observable[T] = { - Observable[T](asJava.cache()) + Observable[T](asJavaObservable.cache()) } /** - * Returns a a pair of a start function and an [[Observable]], which waits until the start function is called before it begins emitting - * items to those [[Observer]]s that have subscribed to it. + * Returns a a pair of a start function and an [[rx.lang.scala.Observable]], which waits until the start function is called before it begins emitting + * items to those [[rx.lang.scala.Observer]]s that have subscribed to it. * * - * - * @return a pair of a start function and an [[Observable]] such that when the start function - * is called, the Observable starts to emit items to its [[Observer]]s + * + * @return a pair of a start function and an [[rx.lang.scala.Observable]] such that when the start function + * is called, the Observable starts to emit items to its [[rx.lang.scala.Observer]]s */ def publish: (() => Subscription, Observable[T]) = { - val javaCO = asJava.publish() + val javaCO = asJavaObservable.publish() (() => javaCO.connect(), Observable[T](javaCO)) } // TODO add Scala-like aggregate function - + /** * Returns an Observable that applies a function of your choosing to the first item emitted by a * source Observable, then feeds the result of that function along with the second item emitted @@ -1030,7 +1046,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, * has an `inject` method that does a similar operation on lists. - * + * * @param initialValue * the initial (seed) accumulator value * @param accumulator @@ -1040,24 +1056,23 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * from the items emitted by the source Observable */ def foldLeft[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { - Observable[R](asJava.reduce(initialValue, accumulator)) + Observable[R](asJavaObservable.reduce(initialValue, new Func2[R,T,R]{ + def call(t1: R, t2: T): R = accumulator(t1,t2) + })) } - + /** * Returns an Observable that emits the results of sampling the items emitted by the source * Observable at a specified time interval. * * - * - * @param period - * the sampling rate - * @param unit - * the [[TimeUnit]] in which `period` is defined + * + * @param duration the sampling rate * @return an Observable that emits the results of sampling the items emitted by the source * Observable at the specified time interval */ def sample(duration: Duration): Observable[T] = { - Observable[T](asJava.sample(duration.length, duration.unit)) + Observable[T](asJavaObservable.sample(duration.length, duration.unit)) } /** @@ -1065,20 +1080,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Observable at a specified time interval. * * - * - * @param period - * the sampling rate - * @param unit - * the [[TimeUnit]] in which `period` is defined + * + * @param duration the sampling rate * @param scheduler - * the [[Scheduler]] to use when sampling + * the [[rx.lang.scala.Scheduler]] to use when sampling * @return an Observable that emits the results of sampling the items emitted by the source * Observable at the specified time interval */ def sample(duration: Duration, scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.sample(duration.length, duration.unit, scheduler)) + Observable[T](asJavaObservable.sample(duration.length, duration.unit, scheduler)) } - + /** * Returns an Observable that applies a function of your choosing to the first item emitted by a * source Observable, then feeds the result of that function along with the second item emitted @@ -1091,16 +1103,18 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * Note that when you pass a seed to `scan()` the resulting Observable will emit * that seed as its first emitted item. - * + * * @param initialValue * the initial (seed) accumulator value * @param accumulator * an accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be emitted to [[Observer]]s via [[Observer.onNext onNext]] and used in the next accumulator call. + * Observable, whose result will be emitted to [[rx.lang.scala.Observer]]s via [[rx.lang.scala.Observer.onNext onNext]] and used in the next accumulator call. * @return an Observable that emits the results of each call to the accumulator function */ def scan[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { - Observable[R](asJava.scan(initialValue, accumulator)) + Observable[R](asJavaObservable.scan(initialValue, new Func2[R,T,R]{ + def call(t1: R, t2: T): R = accumulator(t1,t2) + })) } /** @@ -1108,7 +1122,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * the source Observable satisfy a condition. * * - * + * * @param predicate * a function that evaluates an item and returns a Boolean * @return an Observable that emits `true` if all items emitted by the source @@ -1120,20 +1134,20 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) // it's more fun in Scala: this.map(predicate).foldLeft(true)(_ && _) } - + /** * Returns an Observable that skips the first `num` items emitted by the source * Observable and emits the remainder. * * * - * @param num + * @param n * the number of items to skip * @return an Observable that is identical to the source Observable except that it does not * emit the first `num` items that the source emits */ def drop(n: Int): Observable[T] = { - Observable[T](asJava.skip(n)) + Observable[T](asJavaObservable.skip(n)) } /** @@ -1148,7 +1162,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * becomes false. */ def dropWhile(predicate: T => Boolean): Observable[T] = { - Observable[T](asJava.skipWhile(predicate)) + Observable(asJavaObservable.skipWhile(predicate)) } /** @@ -1157,18 +1171,18 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * This method returns an Observable that will invoke a subscribing [[Observer]]'s - * [[Observer.onNext onNext]] function a maximum of `num` times before invoking - * [[Observer.onCompleted onCompleted]]. - * - * @param num + * This method returns an Observable that will invoke a subscribing [[rx.lang.scala.Observer]]'s + * [[rx.lang.scala.Observer.onNext onNext]] function a maximum of `num` times before invoking + * [[rx.lang.scala.Observer.onCompleted onCompleted]]. + * + * @param n * the number of items to take * @return an Observable that emits only the first `num` items from the source * Observable, or all of the items from the source Observable if that Observable emits * fewer than `num` items */ def take(n: Int): Observable[T] = { - Observable[T](asJava.take(n)) + Observable[T](asJavaObservable.take(n)) } /** @@ -1176,7 +1190,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * specified condition is true. * * - * + * * @param predicate * a function that evaluates an item emitted by the source Observable and returns a * Boolean @@ -1184,7 +1198,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * satisfies the condition defined by `predicate` */ def takeWhile(predicate: T => Boolean): Observable[T] = { - Observable[T](asJava.takeWhile(predicate)) + Observable[T](asJavaObservable.takeWhile(predicate)) } /** @@ -1192,23 +1206,23 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Observable. * * - * + * * @param count * the number of items to emit from the end of the sequence emitted by the source * Observable * @return an Observable that emits only the last `count` items emitted by the source * Observable */ - def takeRight(n: Int): Observable[T] = { - Observable[T](asJava.takeLast(n)) + def takeRight(count: Int): Observable[T] = { + Observable[T](asJavaObservable.takeLast(count)) } - + /** * Returns an Observable that emits the items from the source Observable only until the * `other` Observable emits an item. * * - * + * * @param that * the Observable whose first emitted item will cause `takeUntil` to stop * emitting items from the source Observable @@ -1218,35 +1232,35 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * `other` emits its first item */ def takeUntil[E](that: Observable[E]): Observable[T] = { - Observable[T](asJava.takeUntil(that.asJava)) - } - + Observable[T](asJavaObservable.takeUntil(that.asJavaObservable)) + } + /** * Returns an Observable that emits a single item, a list composed of all the items emitted by * the source Observable. * * * - * Normally, an Observable that returns multiple items will do so by invoking its [[Observer]]'s - * [[Observer.onNext onNext]] method for each such item. You can change + * Normally, an Observable that returns multiple items will do so by invoking its [[rx.lang.scala.Observer]]'s + * [[rx.lang.scala.Observer.onNext onNext]] method for each such item. You can change * this behavior, instructing the Observable to compose a list of all of these items and then to * invoke the Observer's `onNext` function once, passing it the entire list, by * calling the Observable's `toList` method prior to calling its `Observable.subscribe` method. * * Be careful not to use this operator on Observables that emit infinite or very large numbers * of items, as you do not have the option to unsubscribe. - * + * * @return an Observable that emits a single item: a List containing all of the items emitted by * the source Observable. */ def toSeq: Observable[Seq[T]] = { - Observable.jObsOfListToScObsOfSeq(asJava.toList()) - : Observable[Seq[T]] // SI-7818 + Observable.jObsOfListToScObsOfSeq(asJavaObservable.toList) + : Observable[Seq[T]] // SI-7818 } - + /** * Groups the items emitted by this Observable according to a specified discriminator function. - * + * * @param f * a function that extracts the key from an item * @tparam K @@ -1255,59 +1269,57 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * contains all items for which `f` returned `key`. */ def groupBy[K](f: T => K): Observable[(K, Observable[T])] = { - val o1 = asJava.groupBy[K](f) : rx.Observable[_ <: rx.observables.GroupedObservable[K, _ <: T]] - val func = (o: rx.observables.GroupedObservable[K, _ <: T]) => (o.getKey(), Observable[T](o)) + val o1 = asJavaObservable.groupBy[K](f) : rx.Observable[_ <: rx.observables.GroupedObservable[K, _ <: T]] + val func = (o: rx.observables.GroupedObservable[K, _ <: T]) => (o.getKey, Observable[T](o)) Observable[(K, Observable[T])](o1.map[(K, Observable[T])](func)) } - + /** * Given an Observable that emits Observables, creates a single Observable that * emits the items emitted by the most recently published of those Observables. * * - * + * * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, * otherwise you'll get a compilation error. - * - * @param sequenceOfSequences - * the source Observable that emits Observables + * * @return an Observable that emits only the items emitted by the most recently published * Observable - * + * * @usecase def switch[U]: Observable[U] - * @inheritdoc + * @inheritdoc */ def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava + val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) + val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable val o5 = rx.Observable.switchOnNext[U](o4) Observable[U](o5) } // Naming: We follow C# (switch), not Java (switchOnNext), because Java just had to avoid clash with keyword - - /** + + /** * Flattens two Observables into one Observable, without any transformation. * * * * You can combine items emitted by two Observables so that they act like a single * Observable by using the `merge` method. - * + * * @param that * an Observable to be merged * @return an Observable that emits items from `this` and `that` until * `this` or `that` emits `onError` or `onComplete`. */ def merge[U >: T](that: Observable[U]): Observable[U] = { - val thisJava: rx.Observable[_ <: U] = this.asJava - val thatJava: rx.Observable[_ <: U] = that.asJava + val thisJava: rx.Observable[_ <: U] = this.asJavaObservable + val thatJava: rx.Observable[_ <: U] = that.asJavaObservable Observable[U](rx.Observable.merge(thisJava, thatJava)) } /** - * This behaves like [[Observable.merge]] except that if any of the merged Observables - * notify of an error via [[Observer.onError onError]], `mergeDelayError` will + * This behaves like [[rx.lang.scala.Observable.merge]] except that if any of the merged Observables + * notify of an error via [[rx.lang.scala.Observer.onError onError]], `mergeDelayError` will * refrain from propagating that error notification until all of the merged Observables have * finished emitting items. * @@ -1325,7 +1337,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * `this` and `that` */ def mergeDelayError[U >: T](that: Observable[U]): Observable[U] = { - Observable[U](rx.Observable.mergeDelayError[U](this.asJava, that.asJava)) + Observable[U](rx.Observable.mergeDelayError[U](this.asJavaObservable, that.asJavaObservable)) } /** @@ -1339,24 +1351,24 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, * otherwise you'll get a compilation error. - * + * * @return an Observable that emits items that are the result of flattening the items emitted * by the Observables emitted by `this` - * + * * @usecase def flatten[U]: Observable[U] - * @inheritdoc + * @inheritdoc */ def flatten[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava + val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) + val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable val o5 = rx.Observable.merge[U](o4) Observable[U](o5) } /** * This behaves like `flatten` except that if any of the merged Observables - * notify of an error via [[Observer.onError onError]], this method will + * notify of an error via [[rx.lang.scala.Observer.onError onError]], this method will * refrain from propagating that error notification until all of the merged Observables have * finished emitting items. * @@ -1367,20 +1379,20 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. - * + * * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, * otherwise you'll get a compilation error. * * @return an Observable that emits items that are the result of flattening the items emitted by * the Observables emitted by the this Observable - * + * * @usecase def flattenDelayError[U]: Observable[U] * @inheritdoc */ def flattenDelayError[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava + val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) + val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable val o5 = rx.Observable.mergeDelayError[U](o4) Observable[U](o5) } @@ -1396,7 +1408,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) */ def combineLatest[U](that: Observable[U]): Observable[(T, U)] = { val f: Func2[_ >: T, _ >: U, _ <: (T, U)] = (t: T, u: U) => (t, u) - Observable[(T, U)](rx.Observable.combineLatest[T, U, (T, U)](this.asJava, that.asJava, f)) + Observable[(T, U)](rx.Observable.combineLatest[T, U, (T, U)](this.asJavaObservable, that.asJavaObservable, f)) } /** @@ -1409,13 +1421,13 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * $debounceVsThrottle * * @param timeout - * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. * - * @return An [[Observable]] which filters out values which are too quickly followed up with newer values. + * @return An [[rx.lang.scala.Observable]] which filters out values which are too quickly followed up with newer values. * @see `Observable.debounce` */ def throttleWithTimeout(timeout: Duration): Observable[T] = { - Observable[T](asJava.throttleWithTimeout(timeout.length, timeout.unit)) + Observable[T](asJavaObservable.throttleWithTimeout(timeout.length, timeout.unit)) } /** @@ -1428,13 +1440,13 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * $debounceVsThrottle * * @param timeout - * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. * - * @return An [[Observable]] which filters out values which are too quickly followed up with newer values. + * @return An [[rx.lang.scala.Observable]] which filters out values which are too quickly followed up with newer values. * @see `Observable.throttleWithTimeout` */ def debounce(timeout: Duration): Observable[T] = { - Observable[T](asJava.debounce(timeout.length, timeout.unit)) + Observable[T](asJavaObservable.debounce(timeout.length, timeout.unit)) } /** @@ -1447,14 +1459,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * $debounceVsThrottle * * @param timeout - * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. * @param scheduler - * The [[Scheduler]] to use internally to manage the timers which handle timeout for each event. + * The [[rx.lang.scala.Scheduler]] to use internally to manage the timers which handle timeout for each event. * @return Observable which performs the throttle operation. * @see `Observable.throttleWithTimeout` */ def debounce(timeout: Duration, scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.debounce(timeout.length, timeout.unit, scheduler)) + Observable[T](asJavaObservable.debounce(timeout.length, timeout.unit, scheduler)) } /** @@ -1465,14 +1477,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * @param timeout - * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. * @param scheduler - * The [[Scheduler]] to use internally to manage the timers which handle timeout for each event. + * The [[rx.lang.scala.Scheduler]] to use internally to manage the timers which handle timeout for each event. * @return Observable which performs the throttle operation. * @see `Observable.debounce` */ def throttleWithTimeout(timeout: Duration, scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.throttleWithTimeout(timeout.length, timeout.unit, scheduler)) + Observable[T](asJavaObservable.throttleWithTimeout(timeout.length, timeout.unit, scheduler)) } /** @@ -1485,11 +1497,11 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param skipDuration * Time to wait before sending another value after emitting last value. * @param scheduler - * The [[Scheduler]] to use internally to manage the timers which handle timeout for each event. + * The [[rx.lang.scala.Scheduler]] to use internally to manage the timers which handle timeout for each event. * @return Observable which performs the throttle operation. */ def throttleFirst(skipDuration: Duration, scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.throttleFirst(skipDuration.length, skipDuration.unit, scheduler)) + Observable[T](asJavaObservable.throttleFirst(skipDuration.length, skipDuration.unit, scheduler)) } /** @@ -1504,7 +1516,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return Observable which performs the throttle operation. */ def throttleFirst(skipDuration: Duration): Observable[T] = { - Observable[T](asJava.throttleFirst(skipDuration.length, skipDuration.unit)) + Observable[T](asJavaObservable.throttleFirst(skipDuration.length, skipDuration.unit)) } /** @@ -1519,7 +1531,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return Observable which performs the throttle operation. */ def throttleLast(intervalDuration: Duration): Observable[T] = { - Observable[T](asJava.throttleLast(intervalDuration.length, intervalDuration.unit)) + Observable[T](asJavaObservable.throttleLast(intervalDuration.length, intervalDuration.unit)) } /** @@ -1534,34 +1546,34 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return Observable which performs the throttle operation. */ def throttleLast(intervalDuration: Duration, scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.throttleLast(intervalDuration.length, intervalDuration.unit, scheduler)) + Observable[T](asJavaObservable.throttleLast(intervalDuration.length, intervalDuration.unit, scheduler)) } /** * Returns an Observable that sums up the elements of this Observable. - * + * * This operation is only available if the elements of this Observable are numbers, otherwise * you will get a compilation error. - * + * * @return an Observable emitting the sum of all the elements of the source Observable * as its single item. - * + * * @usecase def sum: Observable[T] * @inheritdoc */ def sum[U >: T](implicit num: Numeric[U]): Observable[U] = { foldLeft(num.zero)(num.plus) } - + /** * Returns an Observable that multiplies up the elements of this Observable. - * + * * This operation is only available if the elements of this Observable are numbers, otherwise * you will get a compilation error. - * + * * @return an Observable emitting the product of all the elements of the source Observable * as its single item. - * + * * @usecase def product: Observable[T] * @inheritdoc */ @@ -1612,7 +1624,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * source Observable completes without emitting a single item. */ def first: Observable[T] = take(1) - + /* TODO once https://github.com/Netflix/RxJava/issues/417 is fixed, we can add head and tail methods @@ -1633,7 +1645,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) def tail: Observable[T] = ??? */ - + /** * Returns an Observable that forwards all sequentially distinct items emitted from the source Observable. * @@ -1642,7 +1654,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable of sequentially distinct items */ def distinctUntilChanged: Observable[T] = { - Observable[T](asJava.distinctUntilChanged) + Observable[T](asJavaObservable.distinctUntilChanged) } /** @@ -1657,7 +1669,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable of sequentially distinct items */ def distinctUntilChanged[U](keySelector: T => U): Observable[T] = { - Observable[T](asJava.distinctUntilChanged[U](keySelector)) + Observable[T](asJavaObservable.distinctUntilChanged[U](keySelector)) } /** @@ -1668,7 +1680,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable of distinct items */ def distinct: Observable[T] = { - Observable[T](asJava.distinct()) + Observable[T](asJavaObservable.distinct()) } /** @@ -1683,7 +1695,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable of distinct items */ def distinct[U](keySelector: T => U): Observable[T] = { - Observable[T](asJava.distinct[U](keySelector)) + Observable[T](asJavaObservable.distinct[U](keySelector)) } /** @@ -1695,9 +1707,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * as its single item. */ def length: Observable[Int] = { - Observable[Integer](asJava.count()).map(_.intValue()) + Observable[Integer](asJavaObservable.count()).map(_.intValue()) } - + /** * Returns an Observable that counts the total number of elements in the source Observable. * @@ -1713,9 +1725,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * If [[Observer.onError]] is invoked the source Observable will be re-subscribed to as many times as defined by retryCount. + * If [[rx.lang.scala.Observer.onError]] is invoked the source Observable will be re-subscribed to as many times as defined by retryCount. * - * Any [[Observer.onNext]] calls received on each attempt will be emitted and concatenated together. + * Any [[rx.lang.scala.Observer.onNext]] calls received on each attempt will be emitted and concatenated together. * * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. @@ -1725,7 +1737,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return Observable with retry logic. */ def retry(retryCount: Int): Observable[T] = { - Observable[T](asJava.retry(retryCount)) + Observable[T](asJavaObservable.retry(retryCount)) } /** @@ -1733,18 +1745,18 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * If [[Observer.onError]] is invoked the source Observable will be re-subscribed to. + * If [[rx.lang.scala.Observer.onError]] is invoked the source Observable will be re-subscribed to. * - * Any [[Observer.onNext]] calls received on each attempt will be emitted and concatenated together. + * Any [[rx.lang.scala.Observer.onNext]] calls received on each attempt will be emitted and concatenated together. * * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. * @return Observable with retry logic. */ def retry: Observable[T] = { - Observable[T](asJava.retry()) + Observable[T](asJavaObservable.retry()) } - + /** * Converts an Observable into a [[rx.lang.scala.observables.BlockingObservable]] (an Observable with blocking * operators). @@ -1752,80 +1764,62 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @see Blocking Observable Operators */ def toBlockingObservable: BlockingObservable[T] = { - new BlockingObservable[T](asJava.toBlockingObservable()) + new BlockingObservable[T](asJavaObservable.toBlockingObservable) } /** * Perform work in parallel by sharding an `Observable[T]` on a - * [[rx.lang.scala.concurrency.Schedulers.threadPoolForComputation computation]] - * [[Scheduler]] and return an `Observable[R]` with the output. + * [[rx.lang.scala.concurrency.Schedulers.threadPoolForComputation computation]] + * [[rx.lang.scala.Scheduler]] and return an `Observable[R]` with the output. * * @param f * a function that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` - * @return an Observable with the output of the function executed on a [[Scheduler]] + * @return an Observable with the output of the function executed on a [[rx.lang.scala.Scheduler]] */ def parallel[R](f: Observable[T] => Observable[R]): Observable[R] = { val fJava: Func1[rx.Observable[T], rx.Observable[R]] = - (jo: rx.Observable[T]) => f(Observable[T](jo)).asJava.asInstanceOf[rx.Observable[R]] - Observable[R](asJava.asInstanceOf[rx.Observable[T]].parallel[R](fJava)) + (jo: rx.Observable[T]) => f(Observable[T](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] + Observable[R](asJavaObservable.asInstanceOf[rx.Observable[T]].parallel[R](fJava)) } /** - * Perform work in parallel by sharding an `Observable[T]` on a [[Scheduler]] and return an `Observable[R]` with the output. + * Perform work in parallel by sharding an `Observable[T]` on a [[rx.lang.scala.Scheduler]] and return an `Observable[R]` with the output. * * @param f * a function that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` - * @param s - * a [[Scheduler]] to perform the work on. - * @return an Observable with the output of the function executed on a [[Scheduler]] + * @param scheduler + * a [[rx.lang.scala.Scheduler]] to perform the work on. + * @return an Observable with the output of the function executed on a [[rx.lang.scala.Scheduler]] */ def parallel[R](f: Observable[T] => Observable[R], scheduler: Scheduler): Observable[R] = { val fJava: Func1[rx.Observable[T], rx.Observable[R]] = - (jo: rx.Observable[T]) => f(Observable[T](jo)).asJava.asInstanceOf[rx.Observable[R]] - Observable[R](asJava.asInstanceOf[rx.Observable[T]].parallel[R](fJava, scheduler)) + (jo: rx.Observable[T]) => f(Observable[T](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] + Observable[R](asJavaObservable.asInstanceOf[rx.Observable[T]].parallel[R](fJava, scheduler)) } /** Tests whether a predicate holds for some of the elements of this `Observable`. - * - * @param p the predicate used to test elements. - * @return an Observable emitting one single Boolean, which is `true` if the given predicate `p` - * holds for some of the elements of this Observable, and `false` otherwise. - */ + * + * @param p the predicate used to test elements. + * @return an Observable emitting one single Boolean, which is `true` if the given predicate `p` + * holds for some of the elements of this Observable, and `false` otherwise. + */ def exists(p: T => Boolean): Observable[Boolean] = { - Observable[java.lang.Boolean](asJava.exists(p)).map(_.booleanValue()) + Observable[java.lang.Boolean](asJavaObservable.exists(p)).map(_.booleanValue()) } /** Tests whether this `Observable` emits no elements. - * - * @return an Observable emitting one single Boolean, which is `true` if this `Observable` - * emits no elements, and `false` otherwise. - */ + * + * @return an Observable emitting one single Boolean, which is `true` if this `Observable` + * emits no elements, and `false` otherwise. + */ def isEmpty: Observable[Boolean] = { - Observable[java.lang.Boolean](asJava.isEmpty).map(_.booleanValue()) - } - - def withFilter(p: T => Boolean): WithFilter[T] = { - new WithFilter[T](p, asJava) - } - - - def doOnEach(observer: Observer[T]): Observable[T] = { - Observable[T](asJava.doOnEach(observer)) - } - - def doOnEach(onNext: T => Unit): Observable[T] = { - Observable[T](asJava.doOnEach(onNext)) - } - - def doOnEach(onNext: T => Unit, onError: Throwable => Unit): Observable[T] = { - Observable[T](asJava.doOnEach(onNext, onError)) + Observable[java.lang.Boolean](asJavaObservable.isEmpty).map(_.booleanValue()) } - def doOnEach(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit): Observable[T] = { - Observable[T](asJava.doOnEach(onNext, onError, onComplete)) - } + //def withFilter(p: T => Boolean): WithFilter[T] = { + // new WithFilter[T](p, asJava) + //} - } /** @@ -1835,37 +1829,36 @@ object Observable { import scala.collection.JavaConverters._ import scala.collection.immutable.Range import scala.concurrent.duration.Duration - import rx.{Observable => JObservable} - import rx.lang.scala.util._ - import rx.util.functions._ - import rx.lang.scala.ImplicitFunctionConversions._ - - private[scala] + import ImplicitFunctionConversions._ + + private[scala] def jObsOfListToScObsOfSeq[T](jObs: rx.Observable[_ <: java.util.List[T]]): Observable[Seq[T]] = { - val oScala1: Observable[java.util.List[T]] = new Observable[java.util.List[T]](jObs) + val oScala1: Observable[java.util.List[T]] = new Observable[java.util.List[T]]{ def asJavaObservable = jObs } oScala1.map((lJava: java.util.List[T]) => lJava.asScala) } - - private[scala] + + private[scala] def jObsOfJObsToScObsOfScObs[T](jObs: rx.Observable[_ <: rx.Observable[_ <: T]]): Observable[Observable[T]] = { - val oScala1: Observable[rx.Observable[_ <: T]] = new Observable[rx.Observable[_ <: T]](jObs) - oScala1.map((oJava: rx.Observable[_ <: T]) => new Observable[T](oJava)) + val oScala1: Observable[rx.Observable[_ <: T]] = new Observable[rx.Observable[_ <: T]]{ def asJavaObservable = jObs } + oScala1.map((oJava: rx.Observable[_ <: T]) => new Observable[T]{ def asJavaObservable = oJava}) } - + /** * Creates a new Scala Observable from a given Java Observable. */ - def apply[T](asJava: rx.Observable[_ <: T]): Observable[T] = { - new Observable[T](asJava) + def apply[T](observable: rx.Observable[_ <: T]): Observable[T] = { + new Observable[T]{ + def asJavaObservable = observable + } } - + /** - * Creates an Observable that will execute the given function when an [[Observer]] subscribes to it. + * Creates an Observable that will execute the given function when an [[rx.lang.scala.Observer]] subscribes to it. * * * * Write the function you pass to `create` so that it behaves as an Observable: It - * should invoke the Observer's [[Observer.onNext onNext]], [[Observer.onError onError]], and [[Observer.onCompleted onCompleted]] methods + * should invoke the Observer's [[rx.lang.scala.Observer.onNext onNext]], [[rx.lang.scala.Observer.onError onError]], and [[rx.lang.scala.Observer.onCompleted onCompleted]] methods * appropriately. * * A well-formed Observable must invoke either the Observer's `onCompleted` method @@ -1873,45 +1866,49 @@ object Observable { * * See Rx Design Guidelines (PDF) * for detailed information. - * - * + * + * * @tparam T * the type of the items that this Observable emits * @param func * a function that accepts an `Observer[T]`, invokes its `onNext`, `onError`, and `onCompleted` methods - * as appropriate, and returns a [[Subscription]] to allow the Observer to + * as appropriate, and returns a [[rx.lang.scala.Subscription]] to allow the Observer to * canceling the subscription - * @return an Observable that, when an [[Observer]] subscribes to it, will execute the given + * @return an Observable that, when an [[rx.lang.scala.Observer]] subscribes to it, will execute the given * function */ def apply[T](func: Observer[T] => Subscription): Observable[T] = { - Observable[T](JObservable.create(func)) + Observable[T](rx.Observable.create(new OnSubscribeFunc[T] { + def onSubscribe(t1: rx.Observer[_ >: T]): rx.Subscription = { + func(Observer(t1)) + } + })) } - + /** - * Returns an Observable that invokes an [[Observer]]'s [[Observer.onError onError]] method when the Observer subscribes to it + * Returns an Observable that invokes an [[rx.lang.scala.Observer]]'s [[rx.lang.scala.Observer.onError onError]] method when the Observer subscribes to it * * - * + * * @param exception * the particular error to report * @tparam T * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the [[Observer]]'s [[Observer.onError onError]] method when the Observer subscribes to it + * @return an Observable that invokes the [[rx.lang.scala.Observer]]'s [[rx.lang.scala.Observer.onError onError]] method when the Observer subscribes to it */ def apply[T](exception: Throwable): Observable[T] = { - Observable[T](JObservable.error(exception)) + Observable[T](rx.Observable.error(exception)) } /** * Converts a sequence of values into an Observable. * * - * - * Implementation note: the entire array will be immediately emitted each time an [[Observer]] subscribes. - * Since this occurs before the [[Subscription]] is returned, + * + * Implementation note: the entire array will be immediately emitted each time an [[rx.lang.scala.Observer]] subscribes. + * Since this occurs before the [[rx.lang.scala.Subscription]] is returned, * it in not possible to unsubscribe from the sequence before it completes. - * + * * @param items * the source Array * @tparam T @@ -1919,93 +1916,93 @@ object Observable { * resulting Observable * @return an Observable that emits each item in the source Array */ - def apply[T](args: T*): Observable[T] = { - Observable[T](JObservable.from(args.toIterable.asJava)) + def apply[T](items: T*): Observable[T] = { + Observable[T](rx.Observable.from(items.toIterable.asJava)) } /** * Generates an Observable that emits a sequence of integers within a specified range. - * + * * * - * Implementation note: the entire range will be immediately emitted each time an [[Observer]] subscribes. - * Since this occurs before the [[Subscription]] is returned, + * Implementation note: the entire range will be immediately emitted each time an [[rx.lang.scala.Observer]] subscribes. + * Since this occurs before the [[rx.lang.scala.Subscription]] is returned, * it in not possible to unsubscribe from the sequence before it completes. * * @param range the range * @return an Observable that emits a range of sequential integers */ def apply(range: Range): Observable[Int] = { - Observable[Int](JObservable.from(range.toIterable.asJava)) + Observable[Int](rx.Observable.from(range.toIterable.asJava)) } - + /** * Returns an Observable that calls an Observable factory to create its Observable for each * new Observer that subscribes. That is, for each subscriber, the actual Observable is determined * by the factory function. - * + * * * * The defer operator allows you to defer or delay emitting items from an Observable until such - * time as an Observer subscribes to the Observable. This allows an [[Observer]] to easily + * time as an Observer subscribes to the Observable. This allows an [[rx.lang.scala.Observer]] to easily * obtain updates or a refreshed version of the sequence. - * - * @param observableFactory - * the Observable factory function to invoke for each [[Observer]] that + * + * @param observable + * the Observable factory function to invoke for each [[rx.lang.scala.Observer]] that * subscribes to the resulting Observable * @tparam T * the type of the items emitted by the Observable - * @return an Observable whose [[Observer]]s trigger an invocation of the given Observable + * @return an Observable whose [[rx.lang.scala.Observer]]s trigger an invocation of the given Observable * factory function */ def defer[T](observable: => Observable[T]): Observable[T] = { - Observable[T](JObservable.defer[T](() => observable.asJava)) + Observable[T](rx.Observable.defer[T](() => observable.asJavaObservable)) } - + /** - * Returns an Observable that never sends any items or notifications to an [[Observer]]. + * Returns an Observable that never sends any items or notifications to an [[rx.lang.scala.Observer]]. * * * * This Observable is useful primarily for testing purposes. - * - * @return an Observable that never sends any items or notifications to an [[Observer]] + * + * @return an Observable that never sends any items or notifications to an [[rx.lang.scala.Observer]] */ def never: Observable[Nothing] = { - Observable[Nothing](JObservable.never()) + Observable[Nothing](rx.Observable.never()) } /** * Given 3 observables, returns an observable that emits Tuples of 3 elements each. * The first emitted Tuple will contain the first element of each source observable, * the second Tuple the second element of each source observable, and so on. - * + * * @return an Observable that emits the zipped Observables */ def zip[A, B, C](obA: Observable[A], obB: Observable[B], obC: Observable[C]): Observable[(A, B, C)] = { - Observable[(A, B, C)](rx.Observable.zip[A, B, C, (A, B, C)](obA.asJava, obB.asJava, obC.asJava, (a: A, b: B, c: C) => (a, b, c))) + Observable[(A, B, C)](rx.Observable.zip[A, B, C, (A, B, C)](obA.asJavaObservable, obB.asJavaObservable, obC.asJavaObservable, (a: A, b: B, c: C) => (a, b, c))) } - + /** * Given 4 observables, returns an observable that emits Tuples of 4 elements each. * The first emitted Tuple will contain the first element of each source observable, * the second Tuple the second element of each source observable, and so on. - * + * * @return an Observable that emits the zipped Observables */ def zip[A, B, C, D](obA: Observable[A], obB: Observable[B], obC: Observable[C], obD: Observable[D]): Observable[(A, B, C, D)] = { - Observable[(A, B, C, D)](rx.Observable.zip[A, B, C, D, (A, B, C, D)](obA.asJava, obB.asJava, obC.asJava, obD.asJava, (a: A, b: B, c: C, d: D) => (a, b, c, d))) + Observable[(A, B, C, D)](rx.Observable.zip[A, B, C, D, (A, B, C, D)](obA.asJavaObservable, obB.asJavaObservable, obC.asJavaObservable, obD.asJavaObservable, (a: A, b: B, c: C, d: D) => (a, b, c, d))) } /** - * Given an Observable emitting `N` source observables, returns an observable that + * Given an Observable emitting `N` source observables, returns an observable that * emits Seqs of `N` elements each. * The first emitted Seq will contain the first element of each source observable, * the second Seq the second element of each source observable, and so on. - * - * Note that the returned Observable will only start emitting items once the given + * + * Note that the returned Observable will only start emitting items once the given * `Observable[Observable[T]]` has completed, because otherwise it cannot know `N`. - * + * * @param observables * An Observable emitting N source Observables * @return an Observable that emits the zipped Seqs @@ -2015,14 +2012,14 @@ object Observable { val asSeq: Seq[Object] = args.toSeq asSeq.asInstanceOf[Seq[T]] } - val list = observables.map(_.asJava).asJava + val list = observables.map(_.asJavaObservable).asJavaObservable val o = rx.Observable.zip(list, f) Observable[Seq[T]](o) } /** * Emits `0`, `1`, `2`, `...` with a delay of `duration` between consecutive numbers. - * + * * * * @param duration @@ -2030,12 +2027,13 @@ object Observable { * @return An Observable that emits a number each time interval. */ def interval(duration: Duration): Observable[Long] = { - (new Observable[java.lang.Long](JObservable.interval(duration.length, duration.unit))).map(_.longValue()) + Observable[java.lang.Long](rx.Observable.interval(duration.length, duration.unit)).map(_.longValue()) + /*XXX*/ } /** * Emits `0`, `1`, `2`, `...` with a delay of `duration` between consecutive numbers. - * + * * * * @param duration @@ -2045,116 +2043,14 @@ object Observable { * @return An Observable that emits a number each time interval. */ def interval(duration: Duration, scheduler: Scheduler): Observable[Long] = { - (new Observable[java.lang.Long](JObservable.interval(duration.length, duration.unit, scheduler))).map(_.longValue()) + Observable[java.lang.Long](rx.Observable.interval(duration.length, duration.unit, scheduler)).map(_.longValue()) + /*XXX*/ } - -} -// Cannot yet have inner class because of this error message: -// "implementation restriction: nested class is not allowed in value class. -// This restriction is planned to be removed in subsequent releases." -private[scala] class WithFilter[+T] (p: T => Boolean, asJava: rx.Observable[_ <: T]) { - import rx.lang.scala.ImplicitFunctionConversions._ - - def map[B](f: T => B): Observable[B] = { - Observable[B](asJava.filter(p).map[B](f)) - } - - def flatMap[B](f: T => Observable[B]): Observable[B] = { - Observable[B](asJava.filter(p).flatMap[B]((x: T) => f(x).asJava)) - } - - def withFilter(q: T => Boolean): Observable[T] = { - Observable[T](asJava.filter((x: T) => p(x) && q(x))) - } - - // there is no foreach here, that's only available on BlockingObservable } -private[scala] class UnitTestSuite extends org.scalatest.junit.JUnitSuite { - import scala.concurrent.duration._ - import org.junit.{Before, Test, Ignore} - import org.junit.Assert._ - import org.mockito.Matchers._ - import org.mockito.Mockito._ - import org.mockito.{ MockitoAnnotations, Mock } - - // Tests which needn't be run: - - @Ignore def testCovariance = { - println("hey, you shouldn't run this test") - - val o1: Observable[Nothing] = Observable() - val o2: Observable[Int] = o1 - val o3: Observable[App] = o1 - val o4: Observable[Any] = o2 - val o5: Observable[Any] = o3 - } - - // Tests which have to be run: - - @Test def testDematerialize() { - val o = Observable(1, 2, 3) - val mat = o.materialize - val demat = mat.dematerialize - - // correctly rejected: - // val wrongDemat = Observable("hello").dematerialize - - assertEquals(demat.toBlockingObservable.toIterable.toList, List(1, 2, 3)) - } - - // Test that Java's firstOrDefault propagates errors. - // If this changes (i.e. it suppresses errors and returns default) then Scala's firstOrElse - // should be changed accordingly. - @Test def testJavaFirstOrDefault() { - assertEquals(1, rx.Observable.from(1, 2).firstOrDefault(10).toBlockingObservable().single) - assertEquals(10, rx.Observable.empty().firstOrDefault(10).toBlockingObservable().single) - val msg = "msg6251" - var receivedMsg = "none" - try { - rx.Observable.error(new Exception(msg)).firstOrDefault(10).toBlockingObservable().single - } catch { - case e: Exception => receivedMsg = e.getCause().getMessage() - } - assertEquals(receivedMsg, msg) - } - - @Test def testFirstOrElse() { - def mustNotBeCalled: String = sys.error("this method should not be called") - def mustBeCalled: String = "this is the default value" - assertEquals("hello", Observable("hello").firstOrElse(mustNotBeCalled).toBlockingObservable.single) - assertEquals("this is the default value", Observable().firstOrElse(mustBeCalled).toBlockingObservable.single) - } - - @Test def testFirstOrElseWithError() { - val msg = "msg6251" - var receivedMsg = "none" - try { - Observable[Int](new Exception(msg)).firstOrElse(10).toBlockingObservable.single - } catch { - case e: Exception => receivedMsg = e.getCause().getMessage() - } - assertEquals(receivedMsg, msg) - } - - /* - @Test def testHead() { - val observer = mock(classOf[Observer[Int]]) - val o = Observable().head - val sub = o.subscribe(observer) - verify(observer, never).onNext(any(classOf[Int])) - verify(observer, never).onCompleted() - verify(observer, times(1)).onError(any(classOf[NoSuchElementException])) - } - */ - - @Test def testTest() = { - val a: Observable[Int] = Observable() - assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) - println("This UnitTestSuite.testTest() for rx.lang.scala.Observable") - } - -} + + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/Observer.scala b/language-adaptors/rxjava-scala/src/main/scala/Observer.scala new file mode 100644 index 0000000000..f66ea52c57 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/Observer.scala @@ -0,0 +1,47 @@ +package rx.lang.scala + +/** + Provides a mechanism for receiving push-based notifications. +* +* After an Observer calls an [[rx.lang.scala.Observable]]'s `subscribe` method, the Observable +* calls the Observer's `onNext` method to provide notifications. A well-behaved Observable will +* call an Observer's `onCompleted` method exactly once or the Observer's `onError` method exactly once. +*/ +trait Observer[-T] { + + def asJavaObserver: rx.Observer[_ >: T] + + /** + * Provides the Observer with new data. + * + * The [[rx.lang.scala.Observable]] calls this closure 0 or more times. + * + * The [[rx.lang.scala.Observable]] will not call this method again after it calls either `onCompleted` or `onError`. + */ + def onNext(value: T): Unit = asJavaObserver.onNext(value) + + /** + * Notifies the Observer that the [[rx.lang.scala.Observable]] has experienced an error condition. + * + * If the [[rx.lang.scala.Observable]] calls this method, it will not thereafter call `onNext` or `onCompleted`. + */ + def onError(error: Throwable): Unit = asJavaObserver.onError(error) + + /** + * Notifies the Observer that the [[rx.lang.scala.Observable]] has finished sending push-based notifications. + * + * The [[rx.lang.scala.Observable]] will not call this method if it calls `onError`. + */ + def onCompleted(): Unit = asJavaObserver.onCompleted() + +} + +object Observer { + def apply[T](observer: rx.Observer[T]) : Observer[T] = { + new Observer[T]() { + def asJavaObserver: rx.Observer[_ >: T] = observer + } + } +} + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala new file mode 100644 index 0000000000..10e5bc0d15 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala @@ -0,0 +1,212 @@ +package rx.lang.scala + +import java.util.Date +import scala.concurrent.duration.Duration +import scala.language.postfixOps +import ImplicitFunctionConversions.scalaFunction0ProducingUnitToAction0 +import ImplicitFunctionConversions.schedulerActionToFunc2 +import rx.util.functions.{Action0, Action1, Func2} +import rx.lang.scala.subscriptions.Subscription + +/** + * Represents an object that schedules units of work. + */ +trait Scheduler { + def asJavaScheduler: rx.Scheduler + + /** + * Schedules a cancelable action to be executed. + * + * @param action Action to schedule. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(action: rx.lang.scala.Scheduler => Subscription): Subscription = { + this.schedule[Integer](0, (s: Scheduler, x: Integer) => action(s): Subscription): Subscription + } + + /** + * Schedules a cancelable action to be executed. + * + * @param state State to pass into the action. + * @param action Action to schedule. + * @return a subscription to be able to unsubscribe from action. + */ + private def schedule[T](state: T, action: (Scheduler, T) => Subscription): Subscription = { + Subscription(asJavaScheduler.schedule(state, new Func2[rx.Scheduler, T, rx.Subscription] { + def call(t1: rx.Scheduler, t2: T): rx.Subscription = { + action(Scheduler(t1), t2).asJavaSubscription + } + })) + } + + /** + * Schedules a cancelable action to be executed in delayTime. + * + * @param action Action to schedule. + * @param delayTime Time the action is to be delayed before executing. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(delayTime: Duration)(action: Scheduler => Subscription): Subscription = { + this.schedule[Integer](0, (s: Scheduler, x: Integer) => action(s), delayTime: Duration): Subscription + } + + /** + * Schedules a cancelable action to be executed in delayTime. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @param delayTime + * Time the action is to be delayed before executing. + * @return a subscription to be able to unsubscribe from action. + */ + private def schedule[T](state: T, action: (Scheduler, T) => Subscription, delayTime: Duration): Subscription = { + Subscription(asJavaScheduler.schedule(state, action, delayTime.length, delayTime.unit)) + } + + /** + * Schedules a cancelable action to be executed periodically. + * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing + * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. + * + * @param action The action to execute periodically. + * @param initialDelay Time to wait before executing the action for the first time. + * @param period The time interval to wait each time in between executing the action. + * @return A subscription to be able to unsubscribe from action. + */ + def schedule(initialDelay: Duration, period: Duration)(action: Scheduler => Subscription): Subscription = { + this.schedulePeriodically[Integer](0, (s: Scheduler, x:Integer) => action(s): Subscription, initialDelay: Duration, period: Duration): Subscription + } + + /** + * Schedules a cancelable action to be executed periodically. + * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing + * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. + * + * @param state + * State to pass into the action. + * @param action + * The action to execute periodically. + * @param initialDelay + * Time to wait before executing the action for the first time. + * @param period + * The time interval to wait each time in between executing the action. + * @return A subscription to be able to unsubscribe from action. + */ + private def schedulePeriodically[T](state: T, action: (Scheduler, T) => Subscription, initialDelay: Duration, period: Duration): Subscription = { + Subscription(asJavaScheduler.schedulePeriodically(state, action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit)) + } + + /** + * Schedules a cancelable action to be executed at dueTime. + * + * @param action Action to schedule. + * @param dueTime Time the action is to be executed. If in the past it will be executed immediately. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(dueTime: Date)(action: Scheduler => Subscription): Subscription = { + this.schedule(0: Integer, (s: Scheduler, x: Integer) => action(s): Subscription, dueTime: Date): Subscription + } + + /** + * Schedules a cancelable action to be executed at dueTime. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @param dueTime + * Time the action is to be executed. If in the past it will be executed immediately. + * @return a subscription to be able to unsubscribe from action. + */ + private def schedule[T](state: T, action: (Scheduler, T) => Subscription, dueTime: Date): Subscription = { + Subscription(asJavaScheduler.schedule(state, action, dueTime)) + } + + /** + * Schedules an action to be executed. + * + * @param action + * action + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(action: =>Unit): Subscription = { + Subscription(asJavaScheduler.schedule(()=>action)) + } + + /** + * Schedules an action to be executed in delayTime. + * + * @param action action + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(delayTime: Duration)(action: =>Unit): Subscription = { + Subscription(asJavaScheduler.schedule(()=>action, delayTime.length, delayTime.unit)) + } + + /** + * Schedules an action to be executed periodically. + * + * @param action + * The action to execute periodically. + * @param initialDelay + * Time to wait before executing the action for the first time. + * @param period + * The time interval to wait each time in between executing the action. + * @return A subscription to be able to unsubscribe from action. + */ + def schedule(initialDelay: Duration, period: Duration)(action: =>Unit): Subscription = { + Subscription(asJavaScheduler.schedulePeriodically(()=>action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit)) + } + + def scheduleRec(work: (=>Unit)=>Unit): Subscription = { + Subscription(asJavaScheduler.schedule(new Action1[Action0] { + def call(t1: Action0){ + work{ t1 } + } + })) + //action1[action0] + +// val subscription = new rx.subscriptions.MultipleAssignmentSubscription() +// +// subscription.setSubscription( +// this.schedule(scheduler => { +// def loop(): Unit = subscription.setSubscription(scheduler.schedule{ work{ loop() }}) +// loop() +// subscription +// })) +// subscription + } + + /** + * Returns the scheduler's notion of current absolute time in milliseconds. + */ + def now: Long = { + asJavaScheduler.now + } + + /** + * Parallelism available to a Scheduler. + * + * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. + * + * @return the scheduler's available degree of parallelism. + */ + def degreeOfParallelism: Int = { + asJavaScheduler.degreeOfParallelism + } + +} + +/** + * Provides constructors for Schedulers. + */ +object Scheduler { + private class WrapJavaScheduler(val asJavaScheduler: rx.Scheduler) extends Scheduler + + /** + * Constructs a Scala Scheduler from a Java Scheduler. + */ + def apply(s: rx.Scheduler): Scheduler = new WrapJavaScheduler(s) +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala b/language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala new file mode 100644 index 0000000000..9b36d3ca74 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala @@ -0,0 +1,23 @@ +package rx.lang.scala + +// Cannot yet have inner class because of this error message: +// "implementation restriction: nested class is not allowed in value class. +// This restriction is planned to be removed in subsequent releases." +private[scala] class WithFilter[+T] (p: T => Boolean, asJava: rx.Observable[_ <: T]) { + + import ImplicitFunctionConversions._ + + def map[B](f: T => B): Observable[B] = { + Observable[B](asJava.filter(p).map[B](f)) + } + + def flatMap[B](f: T => Observable[B]): Observable[B] = { + Observable[B](asJava.filter(p).flatMap[B]((x: T) => f(x).asJavaObservable)) + } + + def withFilter(q: T => Boolean): Observable[T] = { + Observable[T](asJava.filter((x: T) => p(x) && q(x))) + } + + // there is no foreach here, that's only available on BlockingObservable +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala b/language-adaptors/rxjava-scala/src/main/scala/concurrency/Schedulers.scala similarity index 80% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala rename to language-adaptors/rxjava-scala/src/main/scala/concurrency/Schedulers.scala index 960df660d4..8ba88ba2d0 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/concurrency/Schedulers.scala @@ -1,18 +1,3 @@ -/** - * Copyright 2013 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.lang.scala.concurrency import java.util.concurrent.Executor diff --git a/language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala new file mode 100644 index 0000000000..a76d95f096 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala @@ -0,0 +1,105 @@ +package rx.lang.scala.concurrency + +import scala.concurrent.duration.Duration +import rx.lang.scala.Scheduler +//import org.scalatest.junit.JUnitSuite + +/** + * Scheduler with artificial time, useful for testing. + * + * For example, you could test the `Observable.interval` operation using a `TestScheduler` as follows: + * + * {{{ + * @Test def testInterval() { + * import org.mockito.Matchers._ + * import org.mockito.Mockito._ + * + * val scheduler = TestScheduler() + * val observer = mock(classOf[rx.Observer[Long]]) + * + * val o = Observable.interval(1 second, scheduler) + * val sub = o.subscribe(observer) + * + * verify(observer, never).onNext(0L) + * verify(observer, never).onCompleted() + * verify(observer, never).onError(any(classOf[Throwable])) + * + * scheduler.advanceTimeTo(2 seconds) + * + * val inOrdr = inOrder(observer); + * inOrdr.verify(observer, times(1)).onNext(0L) + * inOrdr.verify(observer, times(1)).onNext(1L) + * inOrdr.verify(observer, never).onNext(2L) + * verify(observer, never).onCompleted() + * verify(observer, never).onError(any(classOf[Throwable])) + * + * sub.unsubscribe(); + * scheduler.advanceTimeTo(4 seconds) + * verify(observer, never).onNext(2L) + * verify(observer, times(1)).onCompleted() + * verify(observer, never).onError(any(classOf[Throwable])) + * } + * }}} + */ +class TestScheduler extends Scheduler { + val asJavaScheduler = new rx.concurrency.TestScheduler + + def advanceTimeBy(time: Duration) { + asJavaScheduler.advanceTimeBy(time.length, time.unit) + } + + def advanceTimeTo(time: Duration) { + asJavaScheduler.advanceTimeTo(time.length, time.unit) + } + + def triggerActions() { + asJavaScheduler.triggerActions() + } +} + +/** + * Provides constructors for `TestScheduler`. + */ +object TestScheduler { + def apply(): TestScheduler = { + new TestScheduler + } +} + +//private class UnitTest extends JUnitSuite { +// import org.junit.Test +// import scala.concurrent.duration._ +// import scala.language.postfixOps +// import rx.lang.scala.{Observable, Observer} +// +// @Test def testInterval() { +// import org.mockito.Matchers._ +// import org.mockito.Mockito._ +// +// val scheduler = TestScheduler() +// val observer = mock(classOf[rx.Observer[Long]]) +// +// val o = Observable.interval(1 second, scheduler) +// val sub = o.subscribe(observer) +// +// verify(observer, never).onNext(0L) +// verify(observer, never).onCompleted() +// verify(observer, never).onError(any(classOf[Throwable])) +// +// scheduler.advanceTimeTo(2 seconds) +// +// val inOrdr = inOrder(observer); +// inOrdr.verify(observer, times(1)).onNext(0L) +// inOrdr.verify(observer, times(1)).onNext(1L) +// inOrdr.verify(observer, never).onNext(2L) +// verify(observer, never).onCompleted() +// verify(observer, never).onError(any(classOf[Throwable])) +// +// sub.unsubscribe(); +// scheduler.advanceTimeTo(4 seconds) +// verify(observer, never).onNext(2L) +// verify(observer, times(1)).onCompleted() +// verify(observer, never).onError(any(classOf[Throwable])) +// } +//} + diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/observables/BlockingObservable.scala similarity index 95% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala rename to language-adaptors/rxjava-scala/src/main/scala/observables/BlockingObservable.scala index 8d9323ba32..f9e98efa63 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/observables/BlockingObservable.scala @@ -21,7 +21,7 @@ import rx.lang.scala.ImplicitFunctionConversions._ /** * An Observable that provides blocking operators. * - * You can obtain a BlockingObservable from an Observable using [[Observable.toBlockingObservable]] + * You can obtain a BlockingObservable from an Observable using [[rx.lang.scala.Observable.toBlockingObservable]] */ // constructor is private because users should use Observable.toBlockingObservable class BlockingObservable[+T] private[scala] (val asJava: rx.observables.BlockingObservable[_ <: T]) @@ -39,13 +39,13 @@ class BlockingObservable[+T] private[scala] (val asJava: rx.observables.Blocking * * * - * @param onNext + * @param f * the {@link Action1} to invoke for every item emitted by the {@link Observable} * @throws RuntimeException * if an error occurs */ def foreach(f: T => Unit): Unit = { - asJava.forEach(f); + asJava.forEach(f) } def withFilter(p: T => Boolean): WithFilter[T] = { @@ -117,14 +117,14 @@ class BlockingObservable[+T] private[scala] (val asJava: rx.observables.Blocking * Returns an {@link Iterator} that iterates over all items emitted by this {@link Observable}. */ def toIterable: Iterable[T] = { - asJava.toIterable().asScala: Iterable[T] // useless ascription because of compiler bug + asJava.toIterable.asScala: Iterable[T] // useless ascription because of compiler bug } /** * Returns a {@link List} that contains all items emitted by this {@link Observable}. */ def toList: List[T] = { - asJava.toIterable().asScala.toList: List[T] // useless ascription because of compiler bug + asJava.toIterable.asScala.toList: List[T] // useless ascription because of compiler bug } } diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala deleted file mode 100644 index 1165bd4620..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright 2013 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.lang.scala - -import java.util.Date - -import scala.concurrent.duration.Duration -import scala.concurrent.duration.DurationInt -import scala.language.postfixOps - -import org.junit.Before -import org.junit.Test -import org.mockito.Matchers.any -import org.mockito.Mockito.inOrder -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.times -import org.mockito.Mockito.verify -import org.scalatest.junit.JUnitSuite - -import rx.lang.scala.ImplicitFunctionConversions.scalaFunction0ProducingUnitToAction0 -import rx.lang.scala.ImplicitFunctionConversions.schedulerActionToFunc2 -import rx.lang.scala.concurrency.TestScheduler - - -/** - * Represents an object that schedules units of work. - */ -trait Scheduler { - def asJava: rx.Scheduler - - /** - * Schedules a cancelable action to be executed. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription): Subscription = { - asJava.schedule(state, action) - } - - /** - * Schedules a cancelable action to be executed in delayTime. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @param delayTime - * Time the action is to be delayed before executing. - * @param unit - * Time unit of the delay time. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription, delayTime: Duration): Subscription = { - asJava.schedule(state, action, delayTime.length, delayTime.unit) - } - - /** - * Schedules a cancelable action to be executed periodically. - * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing - * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. - * - * @param state - * State to pass into the action. - * @param action - * The action to execute periodically. - * @param initialDelay - * Time to wait before executing the action for the first time. - * @param period - * The time interval to wait each time in between executing the action. - * @return A subscription to be able to unsubscribe from action. - */ - def schedulePeriodically[T](state: T, action: (Scheduler, T) => Subscription, initialDelay: Duration, period: Duration): Subscription = { - asJava.schedulePeriodically(state, action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit) - } - - /** - * Schedules a cancelable action to be executed at dueTime. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @param dueTime - * Time the action is to be executed. If in the past it will be executed immediately. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription, dueTime: Date): Subscription = { - asJava.schedule(state, action, dueTime) - } - - /** - * Schedules an action to be executed. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - def schedule(action: () => Unit): Subscription = { - asJava.schedule(action) - } - - /** - * Schedules an action to be executed in delayTime. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - def schedule(action: () => Unit, delayTime: Duration): Subscription = { - asJava.schedule(action, delayTime.length, delayTime.unit) - } - - /** - * Schedules an action to be executed periodically. - * - * @param action - * The action to execute periodically. - * @param initialDelay - * Time to wait before executing the action for the first time. - * @param period - * The time interval to wait each time in between executing the action. - * @return A subscription to be able to unsubscribe from action. - */ - def schedulePeriodically(action: () => Unit, initialDelay: Duration, period: Duration): Subscription = { - asJava.schedulePeriodically(action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit) - } - - /** - * Returns the scheduler's notion of current absolute time in milliseconds. - */ - def now: Long = { - asJava.now - } - - /** - * Parallelism available to a Scheduler. - * - * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. - * - * @return the scheduler's available degree of parallelism. - */ - def degreeOfParallelism: Int = { - asJava.degreeOfParallelism - } - -} - -/** - * Provides constructors for Schedulers. - */ -object Scheduler { - private class WrapJavaScheduler(val asJava: rx.Scheduler) extends Scheduler - - /** - * Constructs a Scala Scheduler from a Java Scheduler. - */ - def apply(s: rx.Scheduler): Scheduler = new WrapJavaScheduler(s) -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala deleted file mode 100644 index a8090a887e..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2013 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.lang.scala.concurrency - -import scala.concurrent.duration.Duration -import rx.lang.scala.Scheduler -import org.scalatest.junit.JUnitSuite - -/** - * Scheduler with artificial time, useful for testing. - * - * For example, you could test the `Observable.interval` operation using a `TestScheduler` as follows: - * - * {{{ - * @Test def testInterval() { - * import org.mockito.Matchers._ - * import org.mockito.Mockito._ - * - * val scheduler = TestScheduler() - * val observer = mock(classOf[rx.Observer[Long]]) - * - * val o = Observable.interval(1 second, scheduler) - * val sub = o.subscribe(observer) - * - * verify(observer, never).onNext(0L) - * verify(observer, never).onCompleted() - * verify(observer, never).onError(any(classOf[Throwable])) - * - * scheduler.advanceTimeTo(2 seconds) - * - * val inOrdr = inOrder(observer); - * inOrdr.verify(observer, times(1)).onNext(0L) - * inOrdr.verify(observer, times(1)).onNext(1L) - * inOrdr.verify(observer, never).onNext(2L) - * verify(observer, never).onCompleted() - * verify(observer, never).onError(any(classOf[Throwable])) - * - * sub.unsubscribe(); - * scheduler.advanceTimeTo(4 seconds) - * verify(observer, never).onNext(2L) - * verify(observer, times(1)).onCompleted() - * verify(observer, never).onError(any(classOf[Throwable])) - * } - * }}} - */ -class TestScheduler extends Scheduler { - val asJava = new rx.concurrency.TestScheduler - - def advanceTimeBy(time: Duration) { - asJava.advanceTimeBy(time.length, time.unit) - } - - def advanceTimeTo(time: Duration) { - asJava.advanceTimeTo(time.length, time.unit) - } - - def triggerActions() { - asJava.triggerActions() - } -} - -/** - * Provides constructors for `TestScheduler`. - */ -object TestScheduler { - def apply(): TestScheduler = { - new TestScheduler - } -} - -private class UnitTest extends JUnitSuite { - import org.junit.Test - import scala.concurrent.duration._ - import scala.language.postfixOps - import rx.lang.scala.{Observable, Observer} - - @Test def testInterval() { - import org.mockito.Matchers._ - import org.mockito.Mockito._ - - val scheduler = TestScheduler() - val observer = mock(classOf[rx.Observer[Long]]) - - val o = Observable.interval(1 second, scheduler) - val sub = o.subscribe(observer) - - verify(observer, never).onNext(0L) - verify(observer, never).onCompleted() - verify(observer, never).onError(any(classOf[Throwable])) - - scheduler.advanceTimeTo(2 seconds) - - val inOrdr = inOrder(observer); - inOrdr.verify(observer, times(1)).onNext(0L) - inOrdr.verify(observer, times(1)).onNext(1L) - inOrdr.verify(observer, never).onNext(2L) - verify(observer, never).onCompleted() - verify(observer, never).onError(any(classOf[Throwable])) - - sub.unsubscribe(); - scheduler.advanceTimeTo(4 seconds) - verify(observer, never).onNext(2L) - verify(observer, times(1)).onCompleted() - verify(observer, never).onError(any(classOf[Throwable])) - } -} - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala deleted file mode 100644 index a3e61c0021..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2013 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.lang.scala - -import rx.concurrency.CurrentThreadScheduler - -/** - * Provides schedulers. - */ -package object concurrency { - - // These classes are not exposed to Scala users, but are accessible through rx.lang.scala.concurrency.Schedulers: - - // rx.concurrency.CurrentThreadScheduler - // rx.concurrency.ExecutorScheduler - // rx.concurrency.ImmediateScheduler - // rx.concurrency.NewThreadScheduler -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala deleted file mode 100644 index 2b43860b53..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright 2013 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.lang.scala - -/** - * Contains special Observables. - * - * In Scala, this package only contains [[BlockingObservable]]. - * In the corresponding Java package `rx.observables`, there is also a - * `GroupedObservable` and a `ConnectableObservable`, but these are not needed - * in Scala, because we use a pair `(key, observable)` instead of `GroupedObservable` - * and a pair `(startFunction, observable)` instead of `ConnectableObservable`. - */ -package object observables {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala deleted file mode 100644 index 8aa0e63760..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright 2013 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.lang - -import java.util.concurrent.TimeUnit -import java.util.Date - -/* - * Note that: - * - Scala users cannot use Java's types with variance without always using writing - * e.g. rx.Notification[_ <: T], so we create aliases fixing the variance - * - For consistency, we create aliases for all types which Scala users need - */ - -/** - * This package contains all classes that RxScala users need. - * - * It mirrors the structure of package `rx`, but implementation classes that RxScala users - * will not need are left out. - */ -package object scala { - - /* - * Here we're imitating C's preprocessor using Search & Replace. - * - * To activate the code needed to get nice Scaladoc, do the following replacements: - * /*//#ifdef SCALADOC --> //#ifdef SCALADOC - * *///#else --> /*//#else - * //#endif --> *///#endif - * - * To get back to the actual code, undo the above replacements. - * - */ - - /*//#ifdef SCALADOC - - /** - * Provides a mechanism for receiving push-based notifications. - * - * After an Observer calls an [[Observable]]'s `subscribe` method, the Observable - * calls the Observer's `onNext` method to provide notifications. A well-behaved Observable will - * call an Observer's `onCompleted` method exactly once or the Observer's `onError` method exactly once. - */ - trait Observer[-T] { - - /** - * Notifies the Observer that the [[Observable]] has finished sending push-based notifications. - * - * The [[Observable]] will not call this method if it calls `onError`. - */ - def onCompleted(): Unit - - /** - * Notifies the Observer that the [[Observable]] has experienced an error condition. - * - * If the [[Observable]] calls this method, it will not thereafter call `onNext` or `onCompleted`. - */ - def onError(e: Throwable): Unit - - /** - * Provides the Observer with new data. - * - * The [[Observable]] calls this closure 0 or more times. - * - * The [[Observable]] will not call this method again after it calls either `onCompleted` or `onError`. - */ - def onNext(arg: T): Unit - } - - /** - * Subscriptions are returned from all `Observable.subscribe` methods to allow unsubscribing. - * - * This interface is the equivalent of `IDisposable` in the .NET Rx implementation. - */ - trait Subscription { - /** - * Call this method to stop receiving notifications on the Observer that was registered when - * this Subscription was received. - */ - def unsubscribe(): Unit - } - - import language.implicitConversions - - private[scala] implicit def fakeSubscription2RxSubscription(s: Subscription): rx.Subscription = - new rx.Subscription { - def unsubscribe() = s.unsubscribe() - } - private[scala] implicit def rxSubscription2FakeSubscription(s: rx.Subscription): Subscription = - new Subscription { - def unsubscribe() = s.unsubscribe() - } - - private[scala] implicit def schedulerActionToFunc2[T](action: (Scheduler, T) => Subscription) = - new rx.util.functions.Func2[rx.Scheduler, T, rx.Subscription] { - def call(s: rx.Scheduler, t: T): rx.Subscription = { - action(ImplicitFunctionConversions.javaSchedulerToScalaScheduler(s), t) - } - } - - private[scala] implicit def fakeObserver2RxObserver[T](o: Observer[T]): rx.Observer[_ >: T] = ??? - private[scala] implicit def rxObserver2fakeObserver[T](o: rx.Observer[_ >: T]): Observer[T] = ??? - - *///#else - - type Observer[-T] = rx.Observer[_ >: T] - - type Subscription = rx.Subscription - - //#endif - - /** - * Allows to construct observables in a similar way as futures. - * - * Example: - * - * {{{ - * implicit val scheduler = Schedulers.threadPoolForIO - * val o: Observable[List[Friend]] = observable { - * session.getFriends - * } - * o.subscribe( - * friendList => println(friendList), - * err => println(err.getMessage) - * ) - * }}} - */ - def observable[T](body: => T)(implicit scheduler: Scheduler): Observable[T] = { - Observable(1).observeOn(scheduler).map(_ => body) - } -} - -/* - -These classes are considered unnecessary for Scala users, so we don't create aliases for them: - -rx.plugins.RxJavaErrorHandler -rx.plugins.RxJavaObservableExecutionHook -rx.plugins.RxJavaPlugins - -rx.subscriptions.BooleanSubscription -rx.subscriptions.CompositeSubscription -rx.subscriptions.Subscriptions - -*/ diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala deleted file mode 100644 index 07076772f5..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2013 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.lang.scala - -/** - * Provides the type `Subject`. - */ -package object subjects { - - /** - * A Subject is an Observable and an Observer at the same time. - * - * The Java Subject looks like this: - * {{{ - * public abstract class Subject extends Observable implements Observer - * }}} - */ - type Subject[-T, +R] = rx.subjects.Subject[_ >: T, _ <: R] - - // For nicer scaladoc, we would like to present something like this: - /* - trait Observable[+R] {} - trait Observer[-T] {} - trait Subject[-T, +R] extends Observable[R] with Observer[T] { } - */ - - // We don't make aliases to these types, because they are considered internal/not needed by users: - // rx.subjects.AsyncSubject - // rx.subjects.BehaviorSubject - // rx.subjects.PublishSubject - // rx.subjects.ReplaySubject - -} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala new file mode 100644 index 0000000000..ad23ce841c --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala @@ -0,0 +1,11 @@ +package rx.lang.scala.subjects + +import rx.lang.scala.Subject + +object AsyncSubject { + def apply[T](): AsyncSubject[T] = { + new AsyncSubject[T](rx.subjects.AsyncSubject.create()) + } +} + +class AsyncSubject[T] private[scala] (val asJavaSubject: rx.subjects.AsyncSubject[T]) extends Subject[T,T] {} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala new file mode 100644 index 0000000000..fdf873f096 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala @@ -0,0 +1,15 @@ +package rx.lang.scala.subjects + +import rx.lang.scala.Subject + +object BehaviorSubject { + def apply[T](value: T): BehaviorSubject[T] = { + new BehaviorSubject[T](rx.subjects.BehaviorSubject.createWithDefaultValue(value)) + } +} + +class BehaviorSubject[T] private[scala] (val asJavaSubject: rx.subjects.BehaviorSubject[T]) extends Subject[T,T] {} + + + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala new file mode 100644 index 0000000000..93989bfe32 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala @@ -0,0 +1,12 @@ +package rx.lang.scala.subjects + +import rx.lang.scala.Subject + +object PublishSubject { + def apply[T](value: T): PublishSubject[T] = { + new PublishSubject[T](rx.subjects.PublishSubject.create()) + } +} + +class PublishSubject[T] private[scala] (val asJavaSubject: rx.subjects.PublishSubject[T]) extends Subject[T,T] { + } diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala b/language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala new file mode 100644 index 0000000000..6d4698e163 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala @@ -0,0 +1,15 @@ +package rx.lang.scala.subjects + +import rx.lang.scala.Subject + +object ReplaySubject { + def apply[T](): ReplaySubject[T] = { + new ReplaySubject[T](rx.subjects.ReplaySubject.create()) + } +} + +class ReplaySubject[T] private[scala] (val asJavaSubject: rx.subjects.ReplaySubject[T]) extends Subject[T,T] { +} + + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala b/language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala new file mode 100644 index 0000000000..cb92df90d9 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala @@ -0,0 +1,11 @@ +package rx.lang.scala + +/** +* A Subject is an Observable and an Observer at the same time. +*/ +trait Subject[-T, +R] extends Observable[R] with Observer[T] { + val asJavaSubject: rx.subjects.Subject[_ >: T, _<: R] + def asJavaObservable: rx.Observable[_ <: R] = asJavaSubject + def asJavaObserver: rx.Observer[_ >: T] = asJavaSubject +} + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala new file mode 100644 index 0000000000..246c68db21 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala @@ -0,0 +1,38 @@ +package rx.lang.scala.subscriptions + +import rx.lang.scala._ + +object BooleanSubscription { + + /** + * Creates a [[rx.lang.scala.subscriptions.BooleanSubscription]]. + */ + def apply(): BooleanSubscription = { + new BooleanSubscription(new rx.subscriptions.BooleanSubscription()) + } + + /** + * Creates a [[rx.lang.scala.subscriptions.BooleanSubscription]] that invokes the specified action when unsubscribed. + */ + def apply(u: => Unit): BooleanSubscription = { + new BooleanSubscription(new rx.subscriptions.BooleanSubscription { + override def unsubscribe(): Unit = { + u + super.unsubscribe() + } + }) + } +} + +/** + * Represents a [[rx.lang.scala.Subscription]] that can be checked for status. + */ +class BooleanSubscription private[scala] (val asJavaSubscription: rx.subscriptions.BooleanSubscription) + extends Subscription { + + /** + * Checks whether the subscription has been unsubscribed. + */ + def isUnsubscribed: Boolean = asJavaSubscription.isUnsubscribed + +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala new file mode 100644 index 0000000000..6437013a4f --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala @@ -0,0 +1,61 @@ +package rx.lang.scala.subscriptions + +import rx.lang.scala._ + +object CompositeSubscription { + + /** + * Creates a [[rx.lang.scala.subscriptions.CompositeSubscription]] from a group of [[rx.lang.scala.Subscription]]. + */ + def apply(subscriptions: Subscription*): CompositeSubscription = { + new CompositeSubscription(new rx.subscriptions.CompositeSubscription(subscriptions.map(_.asJavaSubscription).toArray : _*)) + } + + /** + * Creates a [[rx.lang.scala.subscriptions.CompositeSubscription]]. + */ + def apply(): CompositeSubscription = { + new CompositeSubscription(new rx.subscriptions.CompositeSubscription()) + } + + /** + * Creates a [[rx.lang.scala.subscriptions.CompositeSubscription]]. + */ + def apply(subscription: rx.subscriptions.CompositeSubscription): CompositeSubscription = { + new CompositeSubscription(subscription) + } +} + +/** + * Represents a group of [[rx.lang.scala.Subscription]] that are disposed together. + */ +class CompositeSubscription private[scala] (val asJavaSubscription: rx.subscriptions.CompositeSubscription) + extends Subscription +{ + /** + * Adds a subscription to the group, + * or unsubscribes immediately is the [[rx.subscriptions.CompositeSubscription]] is unsubscribed. + * @param subscription the subscription to be added. + * @return the [[rx.subscriptions.CompositeSubscription]] itself. + */ + def +=(subscription: Subscription): this.type = { + asJavaSubscription.add(subscription.asJavaSubscription) + this + } + + /** + * Removes and unsubscribes a subscription to the group, + * @param subscription the subscription be removed. + * @return the [[rx.subscriptions.CompositeSubscription]] itself. + */ + def -=(subscription: Subscription): this.type = { + asJavaSubscription.remove(subscription.asJavaSubscription) + this + } + + /** + * Checks whether the subscription has been unsubscribed. + */ + def isUnsubscribed: Boolean = asJavaSubscription.isUnsubscribed + +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala new file mode 100644 index 0000000000..1cdd485d37 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala @@ -0,0 +1,54 @@ +package rx.lang.scala.subscriptions + +import rx.lang.scala._ + +object MultipleAssignmentSubscription { + + /** + * Creates a [[rx.lang.scala.subscriptions.MultipleAssignmentSubscription]] that invokes the specified action when unsubscribed. + */ + def apply(subscription: => Unit): MultipleAssignmentSubscription = { + val m = MultipleAssignmentSubscription() + m.subscription = Subscription{ subscription } + m + } + + /** + * Creates a [[rx.lang.scala.subscriptions.MultipleAssignmentSubscription]]. + */ + def apply(): MultipleAssignmentSubscription = { + new MultipleAssignmentSubscription(new rx.subscriptions.MultipleAssignmentSubscription()) + } +} + + + +/** + * Represents a [[rx.lang.scala.subscriptions.Subscription]] whose underlying subscription can be swapped for another subscription. + */ +class MultipleAssignmentSubscription private[scala] (val asJavaSubscription: rx.subscriptions.MultipleAssignmentSubscription) + extends Subscription { + + /** + * Gets the underlying subscription. + */ + def subscription: Subscription = Subscription(asJavaSubscription.getSubscription) + + /** + * Gets the underlying subscription + * @param that the new subscription + * @return the [[rx.lang.scala.subscriptions.MultipleAssignmentSubscription]] itself. + */ + def subscription_=(that: Subscription): this.type = { + asJavaSubscription.setSubscription(that.asJavaSubscription) + this + } + + /** + * Checks whether the subscription has been unsubscribed. + */ + def isUnsubscribed: Boolean = asJavaSubscription.isUnsubscribed + +} + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala new file mode 100644 index 0000000000..8ca4083c8d --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala @@ -0,0 +1,48 @@ +package rx.lang.scala.subscriptions + +import rx.lang.scala.Subscription +import java.util.concurrent.atomic.AtomicBoolean + + +object SerialSubscription { + + /** + * Creates a [[rx.lang.scala.subscriptions.SerialSubscription]]. + */ + def apply(): SerialSubscription = { + new SerialSubscription(new rx.subscriptions.SerialSubscription()) + } + + /** + * Creates a [[rx.lang.scala.subscriptions.SerialSubscription]] that invokes the specified action when unsubscribed. + */ + def apply(unsubscribe: => Unit): SerialSubscription = { + val s= SerialSubscription() + s.subscription = Subscription{ unsubscribe } + s + } +} + +/** + * Represents a [[rx.lang.scala.Subscription]] that can be checked for status. + */ +class SerialSubscription private[scala] (val asJavaSubscription: rx.subscriptions.SerialSubscription) + extends Subscription { + + private val _isUnsubscribed = new AtomicBoolean(false) + + /** + * Checks whether the subscription has been unsubscribed. + */ + def isUnsubscribed: Boolean = _isUnsubscribed.get() + + /** + * Unsubscribes this subscription, setting isUnsubscribed to true. + */ + override def unsubscribe(): Unit = { super.unsubscribe(); _isUnsubscribed.set(true) } + + def subscription_=(value: Subscription): Unit = asJavaSubscription.setSubscription(value.asJavaSubscription) + def subscription: Subscription = Subscription(asJavaSubscription.getSubscription) + +} + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala new file mode 100644 index 0000000000..ec4e7b5704 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala @@ -0,0 +1,83 @@ +/** + * Copyright 2013 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.lang.scala { + + /** + * Subscriptions are returned from all `Observable.subscribe` methods to allow unsubscribing. + * + * This interface is the equivalent of `IDisposable` in the .NET Rx implementation. + */ + trait Subscription { + + val asJavaSubscription: rx.Subscription + + /** + * Call this method to stop receiving notifications on the Observer that was registered when + * this Subscription was received. + */ + def unsubscribe(): Unit = asJavaSubscription.unsubscribe() + + /** + * Checks if the subscription is unsubscribed. + */ + def isUnsubscribed: Boolean + } +} + +package rx.lang.scala.subscriptions { + +import rx.lang.scala.Subscription +import java.util.concurrent.atomic.AtomicBoolean + + +object Subscription { + + /** + * Creates an [[rx.lang.scala.Subscription]] from an[[rx.Subscription]]. + */ + def apply(subscription: rx.Subscription): Subscription = { + subscription match { + case x: rx.subscriptions.BooleanSubscription => new BooleanSubscription(x) + case x: rx.subscriptions.CompositeSubscription => new CompositeSubscription(x) + case x: rx.subscriptions.MultipleAssignmentSubscription => new MultipleAssignmentSubscription(x) + case x: rx.subscriptions.SerialSubscription => new SerialSubscription(x) + case x: rx.Subscription => Subscription { x.unsubscribe() } + } + } + + /** + * Creates an [[rx.lang.scala.Subscription]] that invokes the specified action when unsubscribed. + */ + def apply(u: => Unit): Subscription = { + new Subscription () { + + private val _isUnsubscribed = new AtomicBoolean(false) + def isUnsubscribed = _isUnsubscribed.get() + + val asJavaSubscription = new rx.Subscription { + def unsubscribe() { u; _isUnsubscribed.set(true) } + } + } + } + } +} + + + + + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala new file mode 100644 index 0000000000..b856d97fd0 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala @@ -0,0 +1,24 @@ +package rx.lang + +package object scala { + + /** + * Allows to construct observables in a similar way as futures. + * + * Example: + * + * {{{ + * implicit val scheduler = Schedulers.threadPoolForIO + * val o: Observable[List[Friend]] = observable { + * session.getFriends + * } + * o.subscribe( + * friendList => println(friendList), + * err => println(err.getMessage) + * ) + * }}} + */ + def observable[T](body: => T)(implicit scheduler: Scheduler): Observable[T] = { + Observable(1).observeOn(scheduler).map(_ => body) + } +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala b/language-adaptors/rxjava-scala/src/main/scala/util/util.scala similarity index 67% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala rename to language-adaptors/rxjava-scala/src/main/scala/util/util.scala index ed19d849ab..fe58b2eeb4 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/util/util.scala @@ -16,31 +16,31 @@ package rx.lang.scala /** - * Provides [[Opening]]s, [[Closing]]s, and [[Timestamped]]. + * Provides [[rx.lang.scala.util.Opening]]s, [[rx.lang.scala.util.Closing]]s, and [[rx.util.Timestamped]]. */ package object util { /** * Tagging interface for objects which can open buffers. - * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ type Opening = rx.util.Opening /** * Creates an object which can open buffers. - * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ def Opening() = rx.util.Openings.create() /** * Tagging interface for objects which can close buffers. - * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ type Closing = rx.util.Closing /** * Creates an object which can close buffers. - * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ def Closing() = rx.util.Closings.create() diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala similarity index 91% rename from language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala rename to language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala index fe1747a1e6..2580d7297d 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -24,42 +24,42 @@ import org.junit.Assert._ import rx.lang.scala.concurrency.Schedulers import java.io.IOException -@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily +//@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @Test def intervalExample() { val o = Observable.interval(200 millis).take(5) o.subscribe(n => println("n = " + n)) - + // need to wait here because otherwise JUnit kills the thread created by interval() waitFor(o) - + println("done") } - + def msTicks(start: Long, step: Long): Observable[Long] = { // will be easier once we have Observable.generate method Observable.interval(step millis) map (_ * step + start) } - + def prefixedTicks(start: Long, step: Long, prefix: String): Observable[String] = { msTicks(start, step).map(prefix + _) } - + @Test def testTicks() { val o = prefixedTicks(5000, 500, "t = ").take(5) o.subscribe(output(_)) waitFor(o) } - + @Test def testSwitch() { // We do not have ultimate precision: Sometimes, 747 gets through, sometimes not val o = Observable.interval(1000 millis).map(n => prefixedTicks(0, 249, s"Observable#$n: ")) - .switch.take(16) + .switch.take(16) o.subscribe(output(_)) waitFor(o) } - + @Test def testSwitchOnObservableOfInt() { // Correctly rejected with error // "Cannot prove that Observable[Int] <:< Observable[Observable[U]]" @@ -69,27 +69,27 @@ class RxScalaDemo extends JUnitSuite { @Test def testObservableComparison() { val first = Observable(10, 11, 12) val second = Observable(10, 11, 12) - + val b1 = (first zip second) map (p => p._1 == p._2) forall (b => b) - + val equality = (a: Any, b: Any) => a == b val b2 = (first zip second) map (p => equality(p._1, p._2)) forall (b => b) - + assertTrue(b1.toBlockingObservable.single) assertTrue(b2.toBlockingObservable.single) } - + @Test def testObservableComparisonWithForComprehension() { val first = Observable(10, 11, 12) val second = Observable(10, 11, 12) - + val booleans = for ((n1, n2) <- (first zip second)) yield (n1 == n2) - + val b1 = booleans.forall(_ == true) // without `== true`, b1 is assigned the forall function - + assertTrue(b1.toBlockingObservable.single) } - + @Test def testStartWithIsUnnecessary() { val before = Observable(-2, -1, 0) val source = Observable(1, 2, 3) @@ -103,150 +103,150 @@ class RxScalaDemo extends JUnitSuite { o.subscribe(output(_)) waitFor(o) } - + def myInterval(period: Long): Observable[String] = { Observable.interval(period.millis).map(n => s"Obs-$period emits $n") } - + @Test def flattenManyExample() { val o = Observable.interval(500 millis).map(n => myInterval((n+1)*100)) val stopper = Observable.interval(5 seconds) o.flatten.takeUntil(stopper).toBlockingObservable.foreach(println(_)) } - + @Test def fattenSomeExample() { // To merge some observables which are all known already: Observable( - Observable.interval(200 millis), - Observable.interval(400 millis), - Observable.interval(800 millis) + Observable.interval(200 millis), + Observable.interval(400 millis), + Observable.interval(800 millis) ).flatten.take(12).toBlockingObservable.foreach(println(_)) - } - + } + @Test def rangeAndBufferExample() { val o = Observable(1 to 18) o.buffer(5).subscribe((l: Seq[Int]) => println(l.mkString("[", ", ", "]"))) } - + @Test def windowExample() { // this will be nicer once we have zipWithIndex - (for ((o, i) <- Observable(1 to 18).window(5) zip Observable(0 until 4); n <- o) - yield s"Observable#$i emits $n") - .subscribe(output(_)) + (for ((o, i) <- Observable(1 to 18).window(5) zip Observable(0 until 4); n <- o) + yield s"Observable#$i emits $n") + .subscribe(output(_)) } - + @Test def testReduce() { assertEquals(10, Observable(1, 2, 3, 4).reduce(_ + _).toBlockingObservable.single) } - + @Test def testForeach() { val numbers = Observable.interval(200 millis).take(3) - + // foreach is not available on normal Observables: // for (n <- numbers) println(n+10) - + // but on BlockingObservable, it is: for (n <- numbers.toBlockingObservable) println(n+10) } - + @Test def testForComprehension() { val observables = Observable(Observable(1, 2, 3), Observable(10, 20, 30)) val squares = (for (o <- observables; i <- o if i % 2 == 0) yield i*i) assertEquals(squares.toBlockingObservable.toList, List(4, 100, 400, 900)) } - + @Test def testTwoSubscriptionsToOneInterval() { val o = Observable.interval(100 millis).take(8) o.subscribe( - i => println(s"${i}a (on thread #${Thread.currentThread().getId()})") + i => println(s"${i}a (on thread #${Thread.currentThread().getId()})") ) o.subscribe( - i => println(s"${i}b (on thread #${Thread.currentThread().getId()})") + i => println(s"${i}b (on thread #${Thread.currentThread().getId()})") ) waitFor(o) } - + @Test def schedulersExample() { val o = Observable.interval(100 millis).take(8) o.observeOn(Schedulers.newThread).subscribe( - i => println(s"${i}a (on thread #${Thread.currentThread().getId()})") + i => println(s"${i}a (on thread #${Thread.currentThread().getId()})") ) o.observeOn(Schedulers.newThread).subscribe( - i => println(s"${i}b (on thread #${Thread.currentThread().getId()})") + i => println(s"${i}b (on thread #${Thread.currentThread().getId()})") ) waitFor(o) } - + @Test def testGroupByThenFlatMap() { val m = Observable(1, 2, 3, 4) val g = m.groupBy(i => i % 2) val t = g.flatMap((p: (Int, Observable[Int])) => p._2) - assertEquals(List(1, 2, 3, 4), t.toBlockingObservable.toList) + assertEquals(List(1, 2, 3, 4), t.toBlockingObservable.toList) } - + @Test def testGroupByThenFlatMapByForComprehension() { val m = Observable(1, 2, 3, 4) val g = m.groupBy(i => i % 2) val t = for ((i, o) <- g; n <- o) yield n - assertEquals(List(1, 2, 3, 4), t.toBlockingObservable.toList) + assertEquals(List(1, 2, 3, 4), t.toBlockingObservable.toList) } - + @Test def testGroupByThenFlatMapByForComprehensionWithTiming() { val m = Observable.interval(100 millis).take(4) val g = m.groupBy(i => i % 2) val t = for ((i, o) <- g; n <- o) yield n - assertEquals(List(0, 1, 2, 3), t.toBlockingObservable.toList) + assertEquals(List(0, 1, 2, 3), t.toBlockingObservable.toList) } @Test def timingTest() { val firstOnly = false val numbersByModulo3 = Observable.interval(1000 millis).take(9).groupBy(_ % 3) - + (for ((modulo, numbers) <- numbersByModulo3) yield { println("Observable for modulo" + modulo + " started") - + if (firstOnly) numbers.take(1) else numbers - }).flatten.toBlockingObservable.foreach(println(_)) + }).flatten.toBlockingObservable.foreach(println(_)) } - + @Test def timingTest1() { val numbersByModulo3 = Observable.interval(1000 millis).take(9).groupBy(_ % 3) - + val t0 = System.currentTimeMillis - + (for ((modulo, numbers) <- numbersByModulo3) yield { println("Observable for modulo" + modulo + " started at t = " + (System.currentTimeMillis - t0)) numbers.take(1) // <- TODO very unexpected //numbers }).flatten.toBlockingObservable.foreach(println(_)) } - + @Test def groupByExample() { val medalsByCountry = Olympics.mountainBikeMedals.groupBy(medal => medal.country) - - val firstMedalOfEachCountry = + + val firstMedalOfEachCountry = for ((country, medals) <- medalsByCountry; firstMedal <- medals.take(1)) yield firstMedal - + firstMedalOfEachCountry.subscribe(medal => { println(s"${medal.country} wins its first medal in ${medal.year}") }) - + waitFor(firstMedalOfEachCountry) } - + @Test def olympicsExample() { val (go, medals) = Olympics.mountainBikeMedals.publish medals.subscribe(println(_)) go() - waitFor(medals) + //waitFor(medals) } - + @Test def exampleWithoutPublish() { val unshared = Observable(1 to 4) unshared.subscribe(n => println(s"subscriber 1 gets $n")) unshared.subscribe(n => println(s"subscriber 2 gets $n")) } - + @Test def exampleWithPublish() { val unshared = Observable(1 to 4) val (startFunc, shared) = unshared.publish @@ -254,11 +254,11 @@ class RxScalaDemo extends JUnitSuite { shared.subscribe(n => println(s"subscriber 2 gets $n")) startFunc() } - + def doLater(waitTime: Duration, action: () => Unit): Unit = { Observable.interval(waitTime).take(1).subscribe(_ => action()) } - + @Test def exampleWithoutReplay() { val numbers = Observable.interval(1000 millis).take(6) val (startFunc, sharedNumbers) = numbers.publish @@ -268,7 +268,7 @@ class RxScalaDemo extends JUnitSuite { doLater(3500 millis, () => { sharedNumbers.subscribe(n => println(s"subscriber 2 gets $n")) }) waitFor(sharedNumbers) } - + @Test def exampleWithReplay() { val numbers = Observable.interval(1000 millis).take(6) val (startFunc, sharedNumbers) = numbers.replay @@ -278,111 +278,111 @@ class RxScalaDemo extends JUnitSuite { doLater(3500 millis, () => { sharedNumbers.subscribe(n => println(s"subscriber 2 gets $n")) }) waitFor(sharedNumbers) } - + @Test def testSingleOption() { assertEquals(None, Observable(1, 2).toBlockingObservable.singleOption) assertEquals(Some(1), Observable(1) .toBlockingObservable.singleOption) assertEquals(None, Observable() .toBlockingObservable.singleOption) } - + // We can't put a general average method into Observable.scala, because Scala's Numeric // does not have scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum) def doubleAverage(o: Observable[Double]): Observable[Double] = { for ((finalSum, finalCount) <- o.foldLeft((0.0, 0))({case ((sum, count), elem) => (sum+elem, count+1)})) - yield finalSum / finalCount + yield finalSum / finalCount } - + @Test def averageExample() { println(doubleAverage(Observable()).toBlockingObservable.single) println(doubleAverage(Observable(0)).toBlockingObservable.single) println(doubleAverage(Observable(4.44)).toBlockingObservable.single) println(doubleAverage(Observable(1, 2, 3.5)).toBlockingObservable.single) } - + @Test def testSum() { assertEquals(10, Observable(1, 2, 3, 4).sum.toBlockingObservable.single) assertEquals(6, Observable(4, 2).sum.toBlockingObservable.single) assertEquals(0, Observable[Int]().sum.toBlockingObservable.single) } - + @Test def testProduct() { assertEquals(24, Observable(1, 2, 3, 4).product.toBlockingObservable.single) assertEquals(8, Observable(4, 2).product.toBlockingObservable.single) assertEquals(1, Observable[Int]().product.toBlockingObservable.single) } - + @Test def mapWithIndexExample() { // We don't need mapWithIndex because we already have zipWithIndex, which we can easily // combine with map: Observable("a", "b", "c").zipWithIndex.map(pair => pair._1 + " has index " + pair._2) - .toBlockingObservable.foreach(println(_)) - + .toBlockingObservable.foreach(println(_)) + // Or even nicer with for-comprehension syntax: (for ((letter, index) <- Observable("a", "b", "c").zipWithIndex) yield letter + " has index " + index) - .toBlockingObservable.foreach(println(_)) + .toBlockingObservable.foreach(println(_)) } - + // source Observables are all known: @Test def zip3Example() { val o = Observable.zip(Observable(1, 2), Observable(10, 20), Observable(100, 200)) (for ((n1, n2, n3) <- o) yield s"$n1, $n2 and $n3") - .toBlockingObservable.foreach(println(_)) + .toBlockingObservable.foreach(println(_)) } // source Observables are in an Observable: @Test def zipManyObservableExample() { val observables = Observable(Observable(1, 2), Observable(10, 20), Observable(100, 200)) (for (seq <- Observable.zip(observables)) yield seq.mkString("(", ", ", ")")) - .toBlockingObservable.foreach(println(_)) + .toBlockingObservable.foreach(println(_)) } - + @Test def takeFirstWithCondition() { val condition: Int => Boolean = _ >= 3 assertEquals(3, Observable(1, 2, 3, 4).filter(condition).first.toBlockingObservable.single) } - + @Test def firstOrDefaultWithCondition() { val condition: Int => Boolean = _ >= 3 assertEquals(3, Observable(1, 2, 3, 4).filter(condition).firstOrElse(10).toBlockingObservable.single) assertEquals(10, Observable(-1, 0, 1).filter(condition).firstOrElse(10).toBlockingObservable.single) } - + def square(x: Int): Int = { println(s"$x*$x is being calculated on thread ${Thread.currentThread().getId()}") Thread.sleep(100) // calculating a square is heavy work :) x*x } - + def work(o1: Observable[Int]): Observable[String] = { println(s"map() is being called on thread ${Thread.currentThread().getId()}") o1.map(i => s"The square of $i is ${square(i)}") } - - @Test def parallelExample() { + + @Test def parallelExample() { val t0 = System.currentTimeMillis() Observable(1 to 10).parallel(work(_)).toBlockingObservable.foreach(println(_)) println(s"Work took ${System.currentTimeMillis()-t0} ms") } - + @Test def exampleWithoutParallel() { val t0 = System.currentTimeMillis() work(Observable(1 to 10)).toBlockingObservable.foreach(println(_)) println(s"Work took ${System.currentTimeMillis()-t0} ms") } - + @Test def toSortedList() { assertEquals(Seq(7, 8, 9, 10), Observable(10, 7, 8, 9).toSeq.map(_.sorted).toBlockingObservable.single) val f = (a: Int, b: Int) => b < a assertEquals(Seq(10, 9, 8, 7), Observable(10, 7, 8, 9).toSeq.map(_.sortWith(f)).toBlockingObservable.single) } - + @Test def timestampExample() { val timestamped = Observable.interval(100 millis).take(3).timestamp.toBlockingObservable for ((millis, value) <- timestamped if value > 0) { println(value + " at t = " + millis) } } - + @Test def materializeExample1() { def printObservable[T](o: Observable[T]): Unit = { import Notification._ @@ -392,15 +392,15 @@ class RxScalaDemo extends JUnitSuite { case OnError(err) => println("Error: " + err.getMessage) }) } - + val o1 = Observable.interval(100 millis).take(3) val o2 = Observable(new IOException("Oops")) printObservable(o1) - waitFor(o1) + //waitFor(o1) printObservable(o2) - waitFor(o2) + //waitFor(o2) } - + @Test def materializeExample2() { import Notification._ Observable(1, 2, 3).materialize.subscribe(n => n match { @@ -409,16 +409,16 @@ class RxScalaDemo extends JUnitSuite { case OnError(err) => println("Error: " + err.getMessage) }) } - + @Test def elementAtReplacement() { assertEquals("b", Observable("a", "b", "c").drop(1).first.toBlockingObservable.single) } - + @Test def elementAtOrDefaultReplacement() { assertEquals("b", Observable("a", "b", "c").drop(1).firstOrElse("!").toBlockingObservable.single) assertEquals("!!", Observable("a", "b", "c").drop(10).firstOrElse("!!").toBlockingObservable.single) } - + @Test def observableLikeFuture1() { implicit val scheduler = Schedulers.threadPoolForIO val o1 = observable { @@ -434,22 +434,22 @@ class RxScalaDemo extends JUnitSuite { println((o1 merge o2).first.toBlockingObservable.single) println(System.currentTimeMillis - t1) } - + @Test def observableLikeFuture2() { class Friend {} val session = new Object { def getFriends: List[Friend] = List(new Friend, new Friend) } - + implicit val scheduler = Schedulers.threadPoolForIO val o: Observable[List[Friend]] = observable { - session.getFriends + session.getFriends } o.subscribe( friendList => println(friendList), err => println(err.getMessage) ) - + Thread.sleep(1500) // or convert to BlockingObservable } @@ -459,10 +459,10 @@ class RxScalaDemo extends JUnitSuite { } def output(s: String): Unit = println(s) - + // blocks until obs has completed def waitFor[T](obs: Observable[T]): Unit = { obs.toBlockingObservable.toIterable.last } - -} + +} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala b/language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala new file mode 100644 index 0000000000..9be18085b9 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala @@ -0,0 +1,114 @@ +import org.junit.{Assert, Test} +import org.scalatest.junit.JUnitSuite +import rx.lang.scala.subscriptions.{MultipleAssignmentSubscription, CompositeSubscription, BooleanSubscription, Subscription} + +class SubscriptionTests extends JUnitSuite { + + @Test + def anonymousSubscriptionCreate() { + val subscription = Subscription{} + Assert.assertNotNull(subscription) + } + + @Test + def anonymousSubscriptionDispose() { + var unsubscribed = false + val subscription = Subscription{ unsubscribed = true } + Assert.assertFalse(unsubscribed) + subscription.unsubscribe() + Assert.assertTrue(unsubscribed) + } + + @Test + def emptySubscription() { + val subscription = Subscription() + subscription.unsubscribe() + } + + @Test + def booleanSubscription() { + val subscription = BooleanSubscription() + Assert.assertFalse(subscription.isUnsubscribed) + subscription.unsubscribe() + Assert.assertTrue(subscription.isUnsubscribed) + subscription.unsubscribe() + Assert.assertTrue(subscription.isUnsubscribed) + } + + @Test + def compositeSubscriptionAdd() { + + var u0 = false + val s0 = BooleanSubscription{ u0 = true } + + var u1 = false + val s1 = Subscription{ u1 = true } + + val composite = CompositeSubscription() + + Assert.assertFalse(composite.isUnsubscribed) + + composite += s0 + composite += s1 + + composite.unsubscribe() + + Assert.assertTrue(composite.isUnsubscribed) + Assert.assertTrue(s0.isUnsubscribed) + Assert.assertTrue(u0) + Assert.assertTrue(u1) + + val s2 = BooleanSubscription() + Assert.assertFalse(s2.isUnsubscribed) + composite += s2 + Assert.assertTrue(s2.isUnsubscribed) + + } + + @Test + def compositeSubscriptionRemove() { + + val s0 = BooleanSubscription() + val composite = CompositeSubscription() + + composite += s0 + Assert.assertFalse(s0.isUnsubscribed) + composite -= s0 + Assert.assertTrue(s0.isUnsubscribed) + + composite.unsubscribe() + + Assert.assertTrue(composite.isUnsubscribed) + } + + @Test + def multiAssignmentSubscriptionAdd() { + + val s0 = BooleanSubscription() + val s1 = BooleanSubscription() + val multiple = MultipleAssignmentSubscription() + + Assert.assertFalse(multiple.isUnsubscribed) + + multiple.subscription = s0 + Assert.assertEquals(s0.asJavaSubscription, multiple.subscription.asJavaSubscription) + + multiple.subscription = s1 + Assert.assertEquals(s1.asJavaSubscription, multiple.subscription.asJavaSubscription) + + Assert.assertFalse(s0.isUnsubscribed) + Assert.assertFalse(s1.isUnsubscribed) + + multiple.unsubscribe() + + Assert.assertTrue(multiple.isUnsubscribed) + Assert.assertFalse(s0.isUnsubscribed) + Assert.assertTrue(s1.isUnsubscribed) + + val s2 = BooleanSubscription() + Assert.assertFalse(s2.isUnsubscribed) + multiple.subscription = s2 + Assert.assertTrue(s2.isUnsubscribed) + } + +} diff --git a/language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala b/language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala new file mode 100644 index 0000000000..006909a5dd --- /dev/null +++ b/language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala @@ -0,0 +1,87 @@ +import org.junit.{Ignore, Assert, Test} +import org.scalatest.junit.JUnitSuite +import rx.lang.scala.Observable +import scala.Predef.String + +class UnitTestSuite extends JUnitSuite { + + // Tests which needn't be run: + +@Ignore +def testCovariance = { + //println("hey, you shouldn't run this test") + + val o1: Observable[Nothing] = Observable() + val o2: Observable[Int] = o1 + val o3: Observable[App] = o1 + val o4: Observable[Any] = o2 + val o5: Observable[Any] = o3 +} + +// Tests which have to be run: + +@Test + def testDematerialize() { + val o = Observable(1, 2, 3) + val mat = o.materialize + val demat = mat.dematerialize + + // correctly rejected: + //val wrongDemat = Observable("hello").dematerialize + + Assert.assertEquals(demat.toBlockingObservable.toIterable.toList, List(1, 2, 3)) +} + +// Test that Java's firstOrDefault propagates errors. +// If this changes (i.e. it suppresses errors and returns default) then Scala's firstOrElse +// should be changed accordingly. +@Test def testJavaFirstOrDefault() { + Assert.assertEquals(1, rx.Observable.from(1, 2).firstOrDefault(10).toBlockingObservable().single) + Assert.assertEquals(10, rx.Observable.empty().firstOrDefault(10).toBlockingObservable().single) + val msg = "msg6251" + var receivedMsg = "none" + try { + rx.Observable.error(new Exception(msg)).firstOrDefault(10).toBlockingObservable().single + } catch { + case e: Exception => receivedMsg = e.getCause().getMessage() + } + Assert.assertEquals(receivedMsg, msg) +} + +@Test def testFirstOrElse() { + def mustNotBeCalled: String = sys.error("this method should not be called") + def mustBeCalled: String = "this is the default value" + Assert.assertEquals("hello", Observable("hello").firstOrElse(mustNotBeCalled).toBlockingObservable.single) + Assert.assertEquals("this is the default value", Observable().firstOrElse(mustBeCalled).toBlockingObservable.single) +} + +@Test def testFirstOrElseWithError() { + val msg = "msg6251" + var receivedMsg = "none" + try { + Observable[Int](new Exception(msg)).firstOrElse(10).toBlockingObservable.single + } catch { + case e: Exception => receivedMsg = e.getCause().getMessage() + } + Assert.assertEquals(receivedMsg, msg) +} + + /* + @Test def testHead() { + val observer = mock(classOf[Observer[Int]]) + val o = Observable().head + val sub = o.subscribe(observer) + + verify(observer, never).onNext(any(classOf[Int])) + verify(observer, never).onCompleted() + verify(observer, times(1)).onError(any(classOf[NoSuchElementException])) + } + */ + + @Test def testTest() = { + val a: Observable[Int] = Observable() + Assert.assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) + //println("This UnitTestSuite.testTest() for rx.lang.scala.Observable") + } + +} diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala deleted file mode 100644 index c5c13d3070..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ /dev/null @@ -1,369 +0,0 @@ -/** - * Copyright 2013 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.lang.scala - -import java.util.Calendar - -import scala.collection.SortedMap -import scala.reflect.runtime.universe -import scala.reflect.runtime.universe.Symbol -import scala.reflect.runtime.universe.Type -import scala.reflect.runtime.universe.typeOf - -import org.junit.Ignore -import org.junit.Test -import org.scalatest.junit.JUnitSuite - -/** - * These tests can be used to check if all methods of the Java Observable have a corresponding - * method in the Scala Observable. - * - * These tests don't contain any assertions, so they will always succeed, but they print their - * results to stdout. - */ -class CompletenessTest extends JUnitSuite { - - // some frequently used comments: - val unnecessary = "[considered unnecessary in Scala land]" - val deprecated = "[deprecated in RxJava]" - val averageProblem = "[We can't have a general average method because Scala's `Numeric` does not have " + - "scalar multiplication (we would need to calculate `(1.0/numberOfElements)*sum`). " + - "You can use `fold` instead to accumulate `sum` and `numberOfElements` and divide at the end.]" - val commentForFirstWithPredicate = "[use `.filter(condition).first`]" - val fromFuture = "[TODO: Decide how Scala Futures should relate to Observables. Should there be a " + - "common base interface for Future and Observable? And should Futures also have an unsubscribe method?]" - - /** - * Maps each method from the Java Observable to its corresponding method in the Scala Observable - */ - val correspondence = defaultMethodCorrespondence ++ correspondenceChanges // ++ overrides LHS with RHS - - /** - * Creates default method correspondence mappings, assuming that Scala methods have the same - * name and the same argument types as in Java - */ - def defaultMethodCorrespondence: Map[String, String] = { - val allMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.Observable[_]]) - val tuples = for (javaM <- allMethods) yield (javaM, javaMethodSignatureToScala(javaM)) - tuples.toMap - } - - /** - * Manually added mappings from Java Observable methods to Scala Observable methods - */ - def correspondenceChanges = Map( - // manually added entries for Java instance methods - "aggregate(Func2[T, T, T])" -> "reduce((U, U) => U)", - "aggregate(R, Func2[R, _ >: T, R])" -> "foldLeft(R)((R, T) => R)", - "all(Func1[_ >: T, Boolean])" -> "forall(T => Boolean)", - "buffer(Long, Long, TimeUnit)" -> "buffer(Duration, Duration)", - "buffer(Long, Long, TimeUnit, Scheduler)" -> "buffer(Duration, Duration, Scheduler)", - "count()" -> "length", - "dematerialize()" -> "dematerialize(<:<[Observable[T], Observable[Notification[U]]])", - "elementAt(Int)" -> "[use `.drop(index).first`]", - "elementAtOrDefault(Int, T)" -> "[use `.drop(index).firstOrElse(default)`]", - "first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, - "firstOrDefault(T)" -> "firstOrElse(=> U)", - "firstOrDefault(Func1[_ >: T, Boolean], T)" -> "[use `.filter(condition).firstOrElse(default)`]", - "groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> "[use `groupBy` and `map`]", - "mapMany(Func1[_ >: T, _ <: Observable[_ <: R]])" -> "flatMap(T => Observable[R])", - "mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> "[combine `zipWithIndex` with `map` or with a for comprehension]", - "onErrorResumeNext(Func1[Throwable, _ <: Observable[_ <: T]])" -> "onErrorResumeNext(Throwable => Observable[U])", - "onErrorResumeNext(Observable[_ <: T])" -> "onErrorResumeNext(Observable[U])", - "onErrorReturn(Func1[Throwable, _ <: T])" -> "onErrorReturn(Throwable => U)", - "onExceptionResumeNext(Observable[_ <: T])" -> "onExceptionResumeNext(Observable[U])", - "parallel(Func1[Observable[T], Observable[R]])" -> "parallel(Observable[T] => Observable[R])", - "parallel(Func1[Observable[T], Observable[R]], Scheduler)" -> "parallel(Observable[T] => Observable[R], Scheduler)", - "reduce(Func2[T, T, T])" -> "reduce((U, U) => U)", - "reduce(R, Func2[R, _ >: T, R])" -> "foldLeft(R)((R, T) => R)", - "scan(Func2[T, T, T])" -> unnecessary, - "scan(R, Func2[R, _ >: T, R])" -> "scan(R)((R, T) => R)", - "skip(Int)" -> "drop(Int)", - "skipWhile(Func1[_ >: T, Boolean])" -> "dropWhile(T => Boolean)", - "skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary, - "startWith(Iterable[T])" -> "[unnecessary because we can just use `++` instead]", - "takeFirst()" -> "first", - "takeFirst(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, - "takeLast(Int)" -> "takeRight(Int)", - "takeWhileWithIndex(Func2[_ >: T, _ >: Integer, Boolean])" -> "[use `.zipWithIndex.takeWhile{case (elem, index) => condition}.map(_._1)`]", - "toList()" -> "toSeq", - "toSortedList()" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sorted)`]", - "toSortedList(Func2[_ >: T, _ >: T, Integer])" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sortWith(f))`]", - "where(Func1[_ >: T, Boolean])" -> "filter(T => Boolean)", - "window(Long, Long, TimeUnit)" -> "window(Duration, Duration)", - "window(Long, Long, TimeUnit, Scheduler)" -> "window(Duration, Duration, Scheduler)", - - // manually added entries for Java static methods - "average(Observable[Integer])" -> averageProblem, - "averageDoubles(Observable[Double])" -> averageProblem, - "averageFloats(Observable[Float])" -> averageProblem, - "averageLongs(Observable[Long])" -> averageProblem, - "create(OnSubscribeFunc[T])" -> "apply(Observer[T] => Subscription)", - "combineLatest(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "combineLatest(Observable[U])", - "concat(Observable[_ <: Observable[_ <: T]])" -> "concat(<:<[Observable[T], Observable[Observable[U]]])", - "defer(Func0[_ <: Observable[_ <: T]])" -> "defer(=> Observable[T])", - "empty()" -> "apply(T*)", - "error(Throwable)" -> "apply(Throwable)", - "from(Array[T])" -> "apply(T*)", - "from(Iterable[_ <: T])" -> "apply(T*)", - "from(Future[_ <: T])" -> fromFuture, - "from(Future[_ <: T], Long, TimeUnit)" -> fromFuture, - "from(Future[_ <: T], Scheduler)" -> fromFuture, - "just(T)" -> "apply(T*)", - "merge(Observable[_ <: T], Observable[_ <: T])" -> "merge(Observable[U])", - "merge(Observable[_ <: Observable[_ <: T]])" -> "flatten(<:<[Observable[T], Observable[Observable[U]]])", - "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[U])", - "mergeDelayError(Observable[_ <: Observable[_ <: T]])" -> "flattenDelayError(<:<[Observable[T], Observable[Observable[U]]])", - "range(Int, Int)" -> "apply(Range)", - "sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use `(first zip second) map (p => p._1 == p._2)`]", - "sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> "[use `(first zip second) map (p => equality(p._1, p._2))`]", - "sum(Observable[Integer])" -> "sum(Numeric[U])", - "sumDoubles(Observable[Double])" -> "sum(Numeric[U])", - "sumFloats(Observable[Float])" -> "sum(Numeric[U])", - "sumLongs(Observable[Long])" -> "sum(Numeric[U])", - "synchronize(Observable[T])" -> "synchronize", - "switchDo(Observable[_ <: Observable[_ <: T]])" -> deprecated, - "switchOnNext(Observable[_ <: Observable[_ <: T]])" -> "switch(<:<[Observable[T], Observable[Observable[U]]])", - "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method `zip` and `map`]", - "zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]", - "zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]" - ) ++ List.iterate("T", 9)(s => s + ", T").map( - // all 9 overloads of startWith: - "startWith(" + _ + ")" -> "[unnecessary because we can just use `++` instead]" - ).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( - // concat 2-9 - "concat(" + _ + ")" -> "[unnecessary because we can use `++` instead or `Observable(o1, o2, ...).concat`]" - ).drop(1).toMap ++ List.iterate("T", 10)(s => s + ", T").map( - // all 10 overloads of from: - "from(" + _ + ")" -> "apply(T*)" - ).toMap ++ (3 to 9).map(i => { - // zip3-9: - val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") - val funcParams = (1 to i).map(j => s"_ >: T$j, ").mkString("") - ("zip(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", unnecessary) - }).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( - // merge 3-9: - "merge(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flatten` instead]" - ).drop(2).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( - // mergeDelayError 3-9: - "mergeDelayError(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flattenDelayError` instead]" - ).drop(2).toMap ++ (3 to 9).map(i => { - // combineLatest 3-9: - val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") - val funcParams = (1 to i).map(j => s"_ >: T$j, ").mkString("") - ("combineLatest(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", "[If C# doesn't need it, Scala doesn't need it either ;-)]") - }).toMap - - def removePackage(s: String) = s.replaceAll("(\\w+\\.)+(\\w+)", "$2") - - def methodMembersToMethodStrings(members: Iterable[Symbol]): Iterable[String] = { - for (member <- members; alt <- member.asTerm.alternatives) yield { - val m = alt.asMethod - // multiple parameter lists in case of curried functions - val paramListStrs = for (paramList <- m.paramss) yield { - paramList.map( - symb => removePackage(symb.typeSignature.toString.replaceAll(",(\\S)", ", $1")) - ).mkString("(", ", ", ")") - } - val name = alt.asMethod.name.decoded - name + paramListStrs.mkString("") - } - } - - def getPublicInstanceMethods(tp: Type): Iterable[String] = { - // declarations: => only those declared in Observable - // members => also those of superclasses - methodMembersToMethodStrings(tp.declarations.filter(m => m.isMethod && m.isPublic)) - // TODO how can we filter out instance methods which were put into companion because - // of extends AnyVal in a way which does not depend on implementation-chosen name '$extension'? - .filter(! _.contains("$extension")) - } - - // also applicable for Java types - def getPublicInstanceAndCompanionMethods(tp: Type): Iterable[String] = - getPublicInstanceMethods(tp) ++ - getPublicInstanceMethods(tp.typeSymbol.companionSymbol.typeSignature) - - def printMethodSet(title: String, tp: Type) { - println("\n" + title) - println(title.map(_ => '-') + "\n") - getPublicInstanceMethods(tp).toList.sorted.foreach(println(_)) - } - - @Ignore // because spams output - @Test def printJavaInstanceMethods: Unit = { - printMethodSet("Instance methods of rx.Observable", - typeOf[rx.Observable[_]]) - } - - @Ignore // because spams output - @Test def printScalaInstanceMethods: Unit = { - printMethodSet("Instance methods of rx.lang.scala.Observable", - typeOf[rx.lang.scala.Observable[_]]) - } - - @Ignore // because spams output - @Test def printJavaStaticMethods: Unit = { - printMethodSet("Static methods of rx.Observable", - typeOf[rx.Observable[_]].typeSymbol.companionSymbol.typeSignature) - } - - @Ignore // because spams output - @Test def printScalaCompanionMethods: Unit = { - printMethodSet("Companion methods of rx.lang.scala.Observable", - typeOf[rx.lang.scala.Observable.type]) - } - - def javaMethodSignatureToScala(s: String): String = { - s.replaceAllLiterally("Long, TimeUnit", "Duration") - .replaceAll("Action0", "() => Unit") - // nested [] can't be parsed with regex, so these will have to be added manually - .replaceAll("Action1\\[([^]]*)\\]", "$1 => Unit") - .replaceAll("Action2\\[([^]]*), ([^]]*)\\]", "($1, $2) => Unit") - .replaceAll("Func0\\[([^]]*)\\]", "() => $1") - .replaceAll("Func1\\[([^]]*), ([^]]*)\\]", "$1 => $2") - .replaceAll("Func2\\[([^]]*), ([^]]*), ([^]]*)\\]", "($1, $2) => $3") - .replaceAllLiterally("_ <: ", "") - .replaceAllLiterally("_ >: ", "") - .replaceAll("(\\w+)\\(\\)", "$1") - } - - @Ignore // because spams output - @Test def printDefaultMethodCorrespondence: Unit = { - println("\nDefault Method Correspondence") - println( "-----------------------------\n") - val c = SortedMap(defaultMethodCorrespondence.toSeq : _*) - val len = c.keys.map(_.length).max + 2 - for ((javaM, scalaM) <- c) { - println(s""" %-${len}s -> %s,""".format("\"" + javaM + "\"", "\"" + scalaM + "\"")) - } - } - - @Ignore // because spams output - @Test def printCorrectedMethodCorrespondence: Unit = { - println("\nCorrected Method Correspondence") - println( "-------------------------------\n") - val c = SortedMap(correspondence.toSeq : _*) - for ((javaM, scalaM) <- c) { - println("%s -> %s,".format("\"" + javaM + "\"", "\"" + scalaM + "\"")) - } - } - - def checkMethodPresence(expectedMethods: Iterable[String], tp: Type): Unit = { - val actualMethods = getPublicInstanceAndCompanionMethods(tp).toSet - val expMethodsSorted = expectedMethods.toList.sorted - var good = 0 - var bad = 0 - for (m <- expMethodsSorted) if (actualMethods.contains(m) || m.charAt(0) == '[') { - good += 1 - } else { - bad += 1 - println(s"Warning: $m is NOT present in $tp") - } - val status = if (bad == 0) "SUCCESS" else "BAD" - println(s"$status: $bad out of ${bad+good} methods were not found in $tp") - } - - @Test def checkScalaMethodPresenceVerbose: Unit = { - println("\nTesting that all mentioned Scala methods exist") - println( "----------------------------------------------\n") - - val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable[_]]).toSet - var good = 0 - var bad = 0 - for ((javaM, scalaM) <- SortedMap(correspondence.toSeq :_*)) { - if (actualMethods.contains(scalaM) || scalaM.charAt(0) == '[') { - good += 1 - } else { - bad += 1 - println(s"Warning:") - println(s"$scalaM is NOT present in Scala Observable") - println(s"$javaM is the method in Java Observable generating this warning") - } - } - val status = if (bad == 0) "SUCCESS" else "BAD" - println(s"\n$status: $bad out of ${bad+good} methods were not found in Scala Observable") - } - - def setTodoForMissingMethods(corresp: Map[String, String]): Map[String, String] = { - val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable[_]]).toSet - for ((javaM, scalaM) <- corresp) yield - (javaM, if (actualMethods.contains(scalaM) || scalaM.charAt(0) == '[') scalaM else "[**TODO: missing**]") - } - - @Test def checkJavaMethodPresence: Unit = { - println("\nTesting that all mentioned Java methods exist") - println( "---------------------------------------------\n") - checkMethodPresence(correspondence.keys, typeOf[rx.Observable[_]]) - } - - @Ignore // because we prefer the verbose version - @Test def checkScalaMethodPresence: Unit = { - checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable[_]]) - } - - def scalaToJavaSignature(s: String) = - s.replaceAllLiterally("_ <:", "? extends") - .replaceAllLiterally("_ >:", "? super") - .replaceAllLiterally("[", "<") - .replaceAllLiterally("]", ">") - .replaceAllLiterally("Array", "T[]") - - def escapeJava(s: String) = - s.replaceAllLiterally("<", "<") - .replaceAllLiterally(">", ">") - - @Ignore // because spams output - @Test def printMarkdownCorrespondenceTable() { - def isInteresting(p: (String, String)): Boolean = - p._1.replaceAllLiterally("()", "") != p._2 - def groupingKey(p: (String, String)): (String, String) = - (if (p._1.startsWith("average")) "average" else p._1.takeWhile(_ != '('), p._2) - def formatJavaCol(name: String, alternatives: Iterable[String]): String = { - alternatives.toList.sorted.map(scalaToJavaSignature(_)).map(s => { - if (s.length > 64) { - val toolTip = escapeJava(s) - "" + name + "(...)" - } else { - "`" + s + "`" - } - }).mkString("
    ") - } - def formatScalaCol(s: String): String = - if (s.startsWith("[") && s.endsWith("]")) s.drop(1).dropRight(1) else "`" + s + "`" - def escape(s: String) = s.replaceAllLiterally("[", "<").replaceAllLiterally("]", ">") - - println(""" -## Comparison of Scala Observable and Java Observable - -Note: -* This table contains both static methods and instance methods. -* If a signature is too long, move your mouse over it to get the full signature. - - -| Java Method | Scala Method | -|-------------|--------------|""") - - val ps = setTodoForMissingMethods(correspondence) - - (for (((javaName, scalaCol), pairs) <- ps.groupBy(groupingKey(_)).toList.sortBy(_._1._1)) yield { - "| " + formatJavaCol(javaName, pairs.map(_._1)) + " | " + formatScalaCol(scalaCol) + " |" - }).foreach(println(_)) - println(s"\nThis table was generated on ${Calendar.getInstance().getTime()}.") - println(s"**Do not edit**. Instead, edit `${getClass().getCanonicalName()}`.") - } - -} From 5c467b30cc65c090cde592fd971116271de0fe2b Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 19 Nov 2013 17:12:44 -0800 Subject: [PATCH 276/333] Reorg Scala Structure - make Eclipse and Java/Scala interop happy --- .../rx/lang/scala/examples/MovieLibUsage.java | 22 +++++++++++-------- .../lang/scala/examples}/MovieLib.scala | 0 .../lang/scala/examples}/Olympics.scala | 0 .../scala}/ImplicitFunctionConversions.scala | 4 ++++ .../{ => rx/lang/scala}/Notification.scala | 0 .../{ => rx/lang/scala}/Observable.scala | 4 ++++ .../scala/{ => rx/lang/scala}/Observer.scala | 0 .../scala/{ => rx/lang/scala}/Scheduler.scala | 4 ++-- .../{ => rx/lang/scala}/WithFilter.scala | 5 +++++ .../lang/scala}/concurrency/Schedulers.scala | 0 .../scala}/concurrency/TestScheduler.scala | 0 .../observables/BlockingObservable.scala | 0 .../lang/scala}/subjects/AsyncSubject.scala | 0 .../scala}/subjects/BehaviorSubject.scala | 0 .../lang/scala}/subjects/PublishSubject.scala | 0 .../lang/scala}/subjects/ReplaySubject.scala | 0 .../lang/scala}/subjects/Subject.scala | 2 ++ .../subscriptions/BooleanSubscription.scala | 0 .../subscriptions/CompositeSubscription.scala | 0 .../MultiAssignmentSubscription.scala | 0 .../subscriptions/SerialSubscription.scala | 0 .../scala}/subscriptions/Subscription.scala | 0 .../lang/scala}/subscriptions/scala.scala | 1 + .../scala/{ => rx/lang/scala}/util/util.scala | 0 .../lang/scala/examples}/RxJavaDemos.scala | 7 ++++-- .../scala/examples}/SubscriptionTests.scala | 2 ++ .../lang/scala/examples}/UnitTestSuite.scala | 3 ++- 27 files changed, 40 insertions(+), 14 deletions(-) rename language-adaptors/rxjava-scala/src/examples/scala/{ => rx/lang/scala/examples}/MovieLib.scala (100%) rename language-adaptors/rxjava-scala/src/examples/scala/{ => rx/lang/scala/examples}/Olympics.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/ImplicitFunctionConversions.scala (97%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/Notification.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/Observable.scala (99%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/Observer.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/Scheduler.scala (98%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/WithFilter.scala (81%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/concurrency/Schedulers.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/concurrency/TestScheduler.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/observables/BlockingObservable.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subjects/AsyncSubject.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subjects/BehaviorSubject.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subjects/PublishSubject.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subjects/ReplaySubject.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subjects/Subject.scala (91%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/BooleanSubscription.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/CompositeSubscription.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/MultiAssignmentSubscription.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/SerialSubscription.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/Subscription.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/scala.scala (94%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/util/util.scala (100%) rename language-adaptors/rxjava-scala/src/test/scala/{ => rx/lang/scala/examples}/RxJavaDemos.scala (98%) rename language-adaptors/rxjava-scala/src/test/scala/{ => rx/lang/scala/examples}/SubscriptionTests.scala (98%) rename language-adaptors/rxjava-scala/src/test/scala/{ => rx/lang/scala/examples}/UnitTestSuite.scala (98%) diff --git a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java index 19a618d158..fde62e0dd8 100644 --- a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java +++ b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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 - * + * + * 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. @@ -18,7 +18,7 @@ import rx.Observable; import rx.lang.scala.examples.Movie; import rx.lang.scala.examples.MovieLib; - +import rx.util.functions.Action1; import static rx.lang.scala.ImplicitFunctionConversions.toScalaObservable; public class MovieLibUsage { @@ -29,13 +29,17 @@ public static void main(String[] args) { new Movie(3000), new Movie(1000), new Movie(2000) - ); + ); MovieLib lib = new MovieLib(toScalaObservable(movies)); - lib.longMovies().asJavaObservable().subscribe(m -> - System.out.println("A movie of length " + m.lengthInSeconds() + "s") - ); + lib.longMovies().asJavaObservable().subscribe(new Action1() { + + @Override + public void call(Movie m) { + System.out.println("A movie of length " + m.lengthInSeconds() + "s"); + } + }); } } diff --git a/language-adaptors/rxjava-scala/src/examples/scala/MovieLib.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/examples/scala/MovieLib.scala rename to language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala diff --git a/language-adaptors/rxjava-scala/src/examples/scala/Olympics.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/examples/scala/Olympics.scala rename to language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala similarity index 97% rename from language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index 67f24af638..458f5fedbc 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -21,6 +21,10 @@ import rx.lang.scala._ import rx.util.functions._ import scala.collection.Seq import rx.lang.scala.subscriptions.Subscription +import java.{lang => jlang} +import scala.language.implicitConversions +import rx.lang.scala.Observer +import rx.lang.scala.Scheduler /** * These function conversions convert between Scala functions and Rx `Func`s and `Action`s. diff --git a/language-adaptors/rxjava-scala/src/main/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/Notification.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala similarity index 99% rename from language-adaptors/rxjava-scala/src/main/scala/Observable.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index a0dfaf3601..0d8cbb7166 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -19,6 +19,10 @@ package rx.lang.scala import rx.util.functions.FuncN import rx.Observable.OnSubscribeFunc +import rx.lang.scala.Notification +import rx.lang.scala.ImplicitFunctionConversions +import rx.lang.scala.Observer +import rx.lang.scala.Scheduler /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/Observer.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/Observer.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala similarity index 98% rename from language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala index 10e5bc0d15..8060dbaa36 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala @@ -2,14 +2,14 @@ package rx.lang.scala import java.util.Date import scala.concurrent.duration.Duration -import scala.language.postfixOps import ImplicitFunctionConversions.scalaFunction0ProducingUnitToAction0 import ImplicitFunctionConversions.schedulerActionToFunc2 import rx.util.functions.{Action0, Action1, Func2} import rx.lang.scala.subscriptions.Subscription /** - * Represents an object that schedules units of work. + * Represents an object thatimport rx.lang.scala.ImplicitFunctionConversions + schedules units of work. */ trait Scheduler { def asJavaScheduler: rx.Scheduler diff --git a/language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala similarity index 81% rename from language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala index 9b36d3ca74..61b25b8784 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala @@ -1,5 +1,10 @@ package rx.lang.scala +import rx.lang.scala.ImplicitFunctionConversions + +import ImplicitFunctionConversions.scalaBooleanFunction1ToRxBooleanFunc1 +import ImplicitFunctionConversions.scalaFunction1ToRxFunc1 + // Cannot yet have inner class because of this error message: // "implementation restriction: nested class is not allowed in value class. // This restriction is planned to be removed in subsequent releases." diff --git a/language-adaptors/rxjava-scala/src/main/scala/concurrency/Schedulers.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/concurrency/Schedulers.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/observables/BlockingObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/observables/BlockingObservable.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala similarity index 91% rename from language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala index cb92df90d9..5631b1bdea 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala @@ -1,5 +1,7 @@ package rx.lang.scala +import rx.lang.scala.Observer + /** * A Subject is an Observable and an Observer at the same time. */ diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/Subscription.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/Subscription.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/scala.scala similarity index 94% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/scala.scala index b856d97fd0..d0c7fa1761 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/scala.scala @@ -1,5 +1,6 @@ package rx.lang +import rx.lang.scala.Scheduler package object scala { /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/util/util.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/util.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/util/util.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/util.scala diff --git a/language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala similarity index 98% rename from language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala rename to language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala index 2580d7297d..130c9f2aa8 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala @@ -16,13 +16,16 @@ package rx.lang.scala.examples import org.scalatest.junit.JUnitSuite -import scala.language.postfixOps import rx.lang.scala._ import scala.concurrent.duration._ -import org.junit.{Before, Test, Ignore} +import org.junit.Test import org.junit.Assert._ import rx.lang.scala.concurrency.Schedulers import java.io.IOException +import rx.lang.scala.examples.Olympics +import rx.lang.scala.Notification.OnCompleted +import rx.lang.scala.Notification.OnError +import rx.lang.scala.Notification.OnNext //@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { diff --git a/language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/SubscriptionTests.scala similarity index 98% rename from language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala rename to language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/SubscriptionTests.scala index 9be18085b9..aad6f22f95 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/SubscriptionTests.scala @@ -1,3 +1,5 @@ +package rx.lang.scala.examples + import org.junit.{Assert, Test} import org.scalatest.junit.JUnitSuite import rx.lang.scala.subscriptions.{MultipleAssignmentSubscription, CompositeSubscription, BooleanSubscription, Subscription} diff --git a/language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/UnitTestSuite.scala similarity index 98% rename from language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala rename to language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/UnitTestSuite.scala index 006909a5dd..0e93aeb69e 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/UnitTestSuite.scala @@ -1,7 +1,8 @@ +package rx.lang.scala.examples + import org.junit.{Ignore, Assert, Test} import org.scalatest.junit.JUnitSuite import rx.lang.scala.Observable -import scala.Predef.String class UnitTestSuite extends JUnitSuite { From 7fd5183b45a70e9e2ae1894b0b3b24bf180519a3 Mon Sep 17 00:00:00 2001 From: headinthebox Date: Tue, 19 Nov 2013 17:17:28 -0800 Subject: [PATCH 277/333] Updated README --- language-adaptors/rxjava-scala/README.md | 213 ++++++++++++++--------- 1 file changed, 128 insertions(+), 85 deletions(-) diff --git a/language-adaptors/rxjava-scala/README.md b/language-adaptors/rxjava-scala/README.md index d198abcdb8..5afb2392cc 100644 --- a/language-adaptors/rxjava-scala/README.md +++ b/language-adaptors/rxjava-scala/README.md @@ -1,101 +1,144 @@ -# Scala Adaptor for RxJava - -This adaptor allows to use RxJava in Scala with anonymous functions, e.g. +Alternative Rx bindings for Scala +================================= +The current RxScala binding attempt to optimize for seamless interop between Scala and Java. +The intended interop is illustrated by the following example where in Scala a class is defined that takes +an `Observable[Movie]` that is transformed using RxScala operators: ```scala -val o = Observable.interval(200 millis).take(5) -o.subscribe(n => println("n = " + n)) -Observable(1, 2, 3, 4).reduce(_ + _) +class MovieLib(val moviesStream: Observable[Movie]) { + val threshold = 1200 + def shortMovies: Observable[Movie] = ??? + def longMovies: Observable[Movie] = ??? +} ``` +which is then called in Java, passing a Java `Observable` to the constructor +```java +public void test() { + MovieLib lib = new MovieLib(Observable.from(...)); -For-comprehensions are also supported: - -```scala -val first = Observable(10, 11, 12) -val second = Observable(10, 11, 12) -val booleans = for ((n1, n2) <- (first zip second)) yield (n1 == n2) + lib.longMovies().subscribe(moviePrinter); +} ``` - -Further, this adaptor attempts to expose an API which is as Scala-idiomatic as possible. This means that certain methods have been renamed, their signature was changed, or static methods were changed to instance methods. Some examples: - +The technique used to obtain this transparency is to use a value class with a private constructor that implements +the Rx operators in an idiomatic Scala way, and a companion object that is used to construct instances in Scala ```scala - // instead of concat: -def ++[U >: T](that: Observable[U]): Observable[U] - -// instance method instead of static: -def zip[U](that: Observable[U]): Observable[(T, U)] - -// the implicit evidence argument ensures that dematerialize can only be called on Observables of Notifications: -def dematerialize[U](implicit evidence: T <:< Notification[U]): Observable[U] - -// additional type parameter U with lower bound to get covariance right: -def onErrorResumeNext[U >: T](resumeFunction: Throwable => Observable[U]): Observable[U] +object Observable { + def apply[T](asJava: rx.Observable[_ <: T]): Observable[T] = { new Observable[T](asJava) } +} -// curried in Scala collections, so curry fold also here: -def fold[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] - -// using Duration instead of (long timepan, TimeUnit duration): -def sample(duration: Duration): Observable[T] - -// called skip in Java, but drop in Scala -def drop(n: Int): Observable[T] - -// there's only mapWithIndex in Java, because Java doesn't have tuples: -def zipWithIndex: Observable[(T, Int)] - -// corresponds to Java's toList: -def toSeq: Observable[Seq[T]] - -// the implicit evidence argument ensures that switch can only be called on Observables of Observables: -def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] - -// Java's from becomes apply, and we use Scala Range -def apply(range: Range): Observable[Int] - -// use Bottom type: -def never: Observable[Nothing] +class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) extends AnyVal { + // Idiomatic Scala friendly definitions of Rx operators +} ``` - -Also, the Scala Observable is fully covariant in its type parameter, whereas the Java Observable only achieves partial covariance due to limitations of Java's type system (or if you can fix this, your suggestions are very welcome). - -For more examples, see [RxScalaDemo.scala](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala). - -Scala code using Rx should only import members from `rx.lang.scala` and below. - - -## Documentation - -The API documentation can be found [here](http://rxscala.github.io/scaladoc/index.html#rx.lang.scala.Observable). - -You can build the API documentation yourself by running `./gradlew scaladoc` in the RxJava root directory. - -Then navigate to `RxJava/language-adaptors/rxjava-scala/build/docs/scaladoc/index.html` to display it. - - -## Binaries - -Binaries and dependency information for Maven, Ivy, Gradle and others can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22rxjava-scala%22). - -Example for Maven: - -```xml - - com.netflix.rxjava - rxjava-scala - x.y.z - +Since `rx.lang.scala.Observable[T] extends AnyVal`, the underlying representation of `rx.lang.scala.Observable[T]` +is the same as `rx.Observable`. Because `rx.lang.scala.Observable[T]` is an opaque type in Scala, +the Scala programmer only sees the Scala-friendly operators. + +However, in the current the illusion of interop is quickly lost when going beyond this simple example. +For example but type `Notification[T]` and `Scheduler[T]` are defined using wrappers, +and hence they are not compatible with `Notification` respectively `Scheduler`. +For instance, when materializing an `Observable[T]` in Scala to an `Observable[Notification[T]]`, +we lost the seamless interop with `Observable>` on the Java side. + +However, the real problems with seamless interop show up when we try to creating bindings for other Rx types. +In particular types that have inheritance or more structure. + +For example, RxScala currently defines a type synonym `type Observer[-T] = rx.Observer[_ >: T]`, +but no further bindings for observers. +Similarly, for subjects RxScala defines `type Subject[-T, +R] = rx.subjects.Subject[_ >: T, _ <: R]`. +The problem with these definitions is that on the Java side, subjects are defined as: +```scala +public abstract class Subject extends Observable implements Observer { …} ``` +without binding any of the Rx subjects. + +The consequence is that `Subject[S,T]` in Scala is unrelated to `rx.lang.scala.Observable[T]` in Scala, +but shows up as a `rx.Observable[T]`. The problem however is that if we want to expose subjects in Scala +such that they derive from both `Observable[S]` and `Observer[T]` we cannot use the `extend AnyVal` trick +we used for `Observable[T]` and immediately lose transparent interop with Java. + +The problem is even worse because `AsyncSubject`, `BehaviorSubject`, … all derive from `Subject`, +so if we want them to derive from a common base `Subject[T,T]` type in Scala we lose transparency for those as well. +And again, if we expose the various subjects by extending `AnyVal`, they are useless in Scala because they do not inherit +from a common base type. To avoid implementing all methods of observable and observer on each specific subject +we might add implicit conversions to `Observable[T]` and `Observer[T]` but that still does not give Scala users +a native `Subject[S,T]` type. +```scala +object AsyncSubject { + def apply[T](): AsyncSubject[T] = + new AsyncSubject[T](rx.subjects.AsyncSubject.create()) +} -and for Ivy: +class AsyncSubject[T] private [scala] (val inner: rx.subjects.AsyncSubject[T]) + extends AnyVal +{ … } -```xml - +implicit final def asObservable[T](subject: AsyncSubject[T]): Observable[T] = + Observable(subject.inner) + +implicit final def asObserver[T](subject: AsyncSubject[T]): Observer[T] = + subject.inner ``` +The inheritance problem is not just limited to subjects, but also surfaces for subscriptions. +Rx scala currently defines `type Subscription = rx.Subscription` using a type synonym as well, +and we run into exactly the same problems as with subjects when we try to bind the +various Rx subscriptions `BooleanSubscription`, `SerialSubscription`, etc. -and for sbt: +Since we cannot wrap Rx types in Scala such that they are both (a) transparently interoperable with Java, +and (b) feel native and idiomatic to Scala, we should decide in favor of optimizing RxScala for Scala +and consumption of Rx values from Java but not for Scala as a producer. +If we take that approach, we can make bindings that feels like a completely native Scala library, +without needing any complications of the Scala side. ```scala -libraryDependencies ++= Seq( - "com.netflix.rxjava" % "rxjava-scala" % "x.y.z" -) +object Observer { …} +trait Observable[+T] { + def asJavaObservable: rx.Observable[_ <: T] +} + +object Observer {…} +trait Observer[-T] { + def asJavaObserver: rx.Observer[_ >: T] +} + +object Subject {…} +trait Subject[-T, +R] extends Observable[R] with Observer[T] { + val asJavaSubject: rx.subjects.Subject[_ >: T, _<: R] +} + +object Scheduler {…} +trait Scheduler { + def asJavaScheduler: rx.Scheduler; +} + +object Notification {…} +trait Notification[+T] { + def asJavaNotification: rx.Notification[_ <: T] +} + +object Subscription {…} +trait Subscription { + def asJavaSubscription: rx.Subscription +} +``` +You pay the price when crossing the Scala/Java interop boundary, which is where it should be. +The proper way is to put the burden of interop on the Scala side, in case you want to create +a reusable Rx-based library in Scala, or wrap and unwrap on the Java side. +```java +public static void main(String[] args) { + + Observable movies = Observable.from(new Movie(3000), new Movie(1000), new Movie(2000)); + MovieLib lib = new MovieLib(toScalaObservable(movies)); + lib.longMovies().asJavaObservable().subscribe(m -> + System.out.println("A movie of length " + m.lengthInSeconds() + "s") + ); +} ``` +Delegation versus Inheritance +----------------------------- +The obvious thought is that using delegation instead of inheritance (http://c2.com/cgi/wiki?DelegationIsInheritance) +will lead to excessive wrapping, since all Scala types wrap and delegate to an underlying RxJava implementation. +Note however, that the wrapping happens at query generation time and incurs no overhead when messages are flowing +through the pipeline. Say we have a query `xs.map(f).filter(p).subscribe(o)`. Even though the Scala types are wrappers, +the callback that is registered with xs is something like `x => { val y = f(x); if(p(y)){ o.asJavaObserver.onNext(y) }}` +and hence there is no additional runtime penalty. \ No newline at end of file From 1e7f7014b8a6a37dac939f186695123101c25751 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 19 Nov 2013 17:23:21 -0800 Subject: [PATCH 278/333] 0.15.0-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 0c0db8d722..dfb204fc38 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.14.12-SNAPSHOT +version=0.15.0-SNAPSHOT From 895dad4d2a1bee922aa2ce0b66a6233c423e847c Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Wed, 20 Nov 2013 01:30:30 +0000 Subject: [PATCH 279/333] [Gradle Release Plugin] - pre tag commit: '0.15.0'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index dfb204fc38..e32751579c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.15.0-SNAPSHOT +version=0.15.0 From 589d4fac8df8faa3a1db16a9f00764751d8c1799 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Wed, 20 Nov 2013 01:30:33 +0000 Subject: [PATCH 280/333] [Gradle Release Plugin] - new version commit: '0.15.1-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e32751579c..85ec9eaec9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.15.0 +version=0.15.1-SNAPSHOT From f2820484b61c5686edba4911e41a8b3dc75716a7 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 19 Nov 2013 17:38:00 -0800 Subject: [PATCH 281/333] Version 0.15.0 --- CHANGES.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 4eef2f974b..bed49e2bcb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,17 @@ # RxJava Releases # +### Version 0.15.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.15.0%22)) ### + +This release contains a refactor of the Scala Bindings by @headinthebox that results in some breaking changes. +The previous solution ended up not working well in all cases for idiomatic Scala usage. Java/Scala interop has been changed and is no longer transparent so as to optimize for native Scala usage. +Read the [rxjava-scala README](https://github.com/Netflix/RxJava/tree/master/language-adaptors/rxjava-scala) for more information. + +* [Pull 503](https://github.com/Netflix/RxJava/pull/503) New Scala Bindings +* [Pull 502](https://github.com/Netflix/RxJava/pull/502) Fix ObserveOn and add ParallelMerge Scheduler overload +* [Pull 499](https://github.com/Netflix/RxJava/pull/499) ObserveOn Refactor +* [Pull 492](https://github.com/Netflix/RxJava/pull/492) Implement the scheduler overloads for Range, From, StartWith +* [Pull 496](https://github.com/Netflix/RxJava/pull/496) Add contravariant for min and max + ### Version 0.14.11 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.11%22)) ### * [Pull 486](https://github.com/Netflix/RxJava/pull/486) BugFix: AsyncSubject From c2d8da2b5dc01d6772d0700640f784a4d5a5eb29 Mon Sep 17 00:00:00 2001 From: DavidMGross Date: Wed, 20 Nov 2013 17:48:54 -0800 Subject: [PATCH 282/333] Update Observable.java Javadoc changes: * add links to RxJava wiki * correct obsolete urls * remove unneeded version string from Linq URLs * standardize javadoc formatting * a few other minor javadoc corrections --- rxjava-core/src/main/java/rx/Observable.java | 555 +++++++++++++------ 1 file changed, 397 insertions(+), 158 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index ce2b883764..53327b371d 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -130,10 +130,10 @@ * The documentation for this interface makes use of marble diagrams. The * following legend explains these diagrams: *

    - * + * *

    * For more information see the - * RxJava Wiki + * RxJava Wiki * * @param the type of the item emitted by the Observable */ @@ -195,7 +195,7 @@ protected Observable(OnSubscribeFunc onSubscribe) { * in which multiple Observers will receive their notifications. *

    * For more information see the - * RxJava Wiki + * RxJava Wiki * * @param observer the Observer * @return a {@link Subscription} reference with which the {@link Observer} @@ -276,7 +276,7 @@ public Subscription subscribe(Observer observer) { * Observers will receive their notifications. *

    * For more information see the - * RxJava Wiki + * RxJava Wiki * * @param observer the Observer * @param scheduler the {@link Scheduler} on which Observers subscribe to @@ -478,6 +478,7 @@ public Subscription subscribe(final Action1 onNext, final Action1Observable.publish() and Observable.multicast() */ public ConnectableObservable multicast(Subject subject) { return OperationMulticast.multicast(this, subject); @@ -547,7 +548,7 @@ public Subscription onSubscribe(Observer observer) { * Creates an Observable that will execute the given function when an * {@link Observer} subscribes to it. *

    - * + * *

    * Write the function you pass to create so that it behaves as * an Observable: It should invoke the Observer's @@ -568,6 +569,7 @@ public Subscription onSubscribe(Observer observer) { * allow the Observer to cancel the subscription * @return an Observable that, when an {@link Observer} subscribes to it, * will execute the given function + * @see create() */ public static Observable create(OnSubscribeFunc func) { return new Observable(func); @@ -583,6 +585,7 @@ public static Observable create(OnSubscribeFunc func) { * @return an Observable that returns no data to the {@link Observer} and * immediately invokes the {@link Observer}'s * {@link Observer#onCompleted() onCompleted} method + * @see empty() * @see MSDN: Observable.Empty Method */ public static Observable empty() { @@ -603,6 +606,7 @@ public static Observable empty() { * immediately invokes the {@link Observer}'s * {@link Observer#onCompleted() onCompleted} method with the * specified scheduler + * @see empty() * @see MSDN: Observable.Empty Method (IScheduler) */ public static Observable empty(Scheduler scheduler) { @@ -621,6 +625,7 @@ public static Observable empty(Scheduler scheduler) { * @return an Observable that invokes the {@link Observer}'s * {@link Observer#onError onError} method when the Observer * subscribes to it + * @see error() * @see MSDN: Observable.Throw Method */ public static Observable error(Throwable exception) { @@ -640,6 +645,7 @@ public static Observable error(Throwable exception) { * @return an Observable that invokes the {@link Observer}'s * {@link Observer#onError onError} method with the specified * scheduler + * @see error() * @see MSDN: Observable.Throw Method */ public static Observable error(Throwable exception, Scheduler scheduler) { @@ -649,7 +655,7 @@ public static Observable error(Throwable exception, Scheduler scheduler) /** * Converts an {@link Iterable} sequence into an Observable. *

    - * + * *

    * Note: the entire iterable sequence is immediately emitted each time an * {@link Observer} subscribes. Since this occurs before the @@ -661,6 +667,7 @@ public static Observable error(Throwable exception, Scheduler scheduler) * type of items to be emitted by the resulting Observable * @return an Observable that emits each item in the source {@link Iterable} * sequence + * @see from() */ public static Observable from(Iterable iterable) { return create(OperationToObservableIterable.toObservableIterable(iterable)); @@ -677,6 +684,7 @@ public static Observable from(Iterable iterable) { * type of items to be emitted by the resulting Observable * @return an Observable that emits each item in the source {@link Iterable} * sequence with the specified scheduler + * @see from() * @see MSDN: Observable.ToObservable */ public static Observable from(Iterable iterable, Scheduler scheduler) { @@ -686,7 +694,7 @@ public static Observable from(Iterable iterable, Scheduler s /** * Converts an Array into an Observable. *

    - * + * *

    * Note: the entire array is immediately emitted each time an * {@link Observer} subscribes. Since this occurs before the @@ -697,6 +705,7 @@ public static Observable from(Iterable iterable, Scheduler s * @param the type of items in the Array and the type of items to be * emitted by the resulting Observable * @return an Observable that emits each item in the source Array + * @see from() */ public static Observable from(T[] items) { return create(OperationToObservableIterable.toObservableIterable(Arrays.asList(items))); @@ -705,7 +714,7 @@ public static Observable from(T[] items) { /** * Converts an item into an Observable that emits that item. *

    - * + * *

    * Note: the item is immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -716,6 +725,7 @@ public static Observable from(T[] items) { * @param the type of the item, and the type of the item to be * emitted by the resulting Observable * @return an Observable that emits the item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -726,7 +736,7 @@ public static Observable from(T t1) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -738,6 +748,7 @@ public static Observable from(T t1) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -748,7 +759,7 @@ public static Observable from(T t1, T t2) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -761,6 +772,7 @@ public static Observable from(T t1, T t2) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -771,7 +783,7 @@ public static Observable from(T t1, T t2, T t3) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -785,6 +797,7 @@ public static Observable from(T t1, T t2, T t3) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -795,7 +808,7 @@ public static Observable from(T t1, T t2, T t3, T t4) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -810,6 +823,7 @@ public static Observable from(T t1, T t2, T t3, T t4) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -820,7 +834,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -836,6 +850,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -846,7 +861,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -863,6 +878,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -873,7 +889,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -891,6 +907,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -901,7 +918,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -920,6 +937,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -930,7 +948,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -950,6 +968,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -961,7 +980,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * Generates an Observable that emits a sequence of integers within a * specified range. *

    - * + * *

    * Note: the entire range is immediately emitted each time an * {@link Observer} subscribes. Since this occurs before the @@ -971,6 +990,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * @param start the value of the first integer in the sequence * @param count the number of sequential integers to generate * @return an Observable that emits a range of sequential integers + * @see range() * @see Observable.Range Method (Int32, Int32) */ public static Observable range(int start, int count) { @@ -981,11 +1001,12 @@ public static Observable range(int start, int count) { * Generates an Observable that emits a sequence of integers within a * specified range with the specified scheduler. *

    - * + * * @param start the value of the first integer in the sequence * @param count the number of sequential integers to generate * @param scheduler the scheduler to run the generator loop on * @return an Observable that emits a range of sequential integers + * @see range() * @see Observable.Range Method (Int32, Int32, IScheduler) */ public static Observable range(int start, int count, Scheduler scheduler) { @@ -997,7 +1018,7 @@ public static Observable range(int start, int count, Scheduler schedule * Observable for each new Observer that subscribes. That is, for each * subscriber, the actuall Observable is determined by the factory function. *

    - * + * *

    * The defer operator allows you to defer or delay emitting items from an * Observable until such time as an Observer subscribes to the Observable. @@ -1010,6 +1031,7 @@ public static Observable range(int start, int count, Scheduler schedule * @param the type of the items emitted by the Observable * @return an Observable whose {@link Observer}s trigger an invocation of * the given Observable factory function + * @see defer() */ public static Observable defer(Func0> observableFactory) { return create(OperationDefer.defer(observableFactory)); @@ -1033,6 +1055,7 @@ public static Observable defer(Func0> o * {@link Observer#onNext onNext} method * @param the type of that item * @return an Observable that emits a single item and then completes + * @see just() */ public static Observable just(T value) { List list = new ArrayList(); @@ -1053,6 +1076,7 @@ public static Observable just(T value) { * @param scheduler the scheduler to send the single element on * @return an Observable that emits a single item and then completes on a * specified scheduler + * @see just() */ public static Observable just(T value, Scheduler scheduler) { return just(value).observeOn(scheduler); @@ -1071,6 +1095,7 @@ public static Observable just(T value, Scheduler scheduler) { * @return an Observable that emits items that are the result of flattening * the items emitted by the Observables emitted by the * {@code source} Observable + * @see merge() * @see MSDN: Observable.Merge Method */ public static Observable merge(Observable> source) { @@ -1090,6 +1115,7 @@ public static Observable merge(Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1112,6 +1138,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1135,6 +1162,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1159,6 +1187,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1184,6 +1213,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1210,6 +1240,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1237,6 +1268,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1265,6 +1297,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1283,6 +1316,7 @@ public static Observable merge(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ public static Observable concat(Observable> observables) { @@ -1300,6 +1334,7 @@ public static Observable concat(Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1321,6 +1356,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1342,6 +1378,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1364,6 +1401,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1387,6 +1425,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1411,6 +1450,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1436,6 +1476,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1462,6 +1503,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1491,6 +1533,7 @@ public static Observable concat(Observable t1, ObservablemergeDelayError() * @see MSDN: Observable.Merge Method */ public static Observable mergeDelayError(Observable> source) { @@ -1518,6 +1561,7 @@ public static Observable mergeDelayError(ObservablemergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1548,6 +1592,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t3 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1580,6 +1625,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t4 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1612,6 +1658,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t5 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1645,6 +1692,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t6 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1679,6 +1727,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t7 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1714,6 +1763,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t8 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1750,6 +1800,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t9 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1762,13 +1813,14 @@ public static Observable mergeDelayError(Observable t1, Obse * Returns an Observable that never sends any items or notifications to an * {@link Observer}. *

    - * + * *

    * This Observable is useful primarily for testing purposes. * * @param the type of items (not) emitted by the Observable * @return an Observable that never sends any items or notifications to an * {@link Observer} + * @see never() */ public static Observable never() { return new NeverObservable(); @@ -1779,11 +1831,12 @@ public static Observable never() { * that emits the items emitted by the most recently published of those * Observables. *

    - * + * * * @param sequenceOfSequences the source Observable that emits Observables * @return an Observable that emits only the items emitted by the most * recently published Observable + * @see switchOnNext() * @deprecated use {@link #switchOnNext} */ @Deprecated @@ -1796,11 +1849,12 @@ public static Observable switchDo(Observable - * + * * * @param sequenceOfSequences the source Observable that emits Observables * @return an Observable that emits only the items emitted by the most * recently published Observable + * @see switchOnNext() */ public static Observable switchOnNext(Observable> sequenceOfSequences) { return create(OperationSwitch.switchDo(sequenceOfSequences)); @@ -1810,7 +1864,7 @@ public static Observable switchOnNext(Observable - * + * *

    * A well-behaved Observable does not interleave its invocations of the * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, @@ -1824,6 +1878,7 @@ public static Observable switchOnNext(Observablesynchronize() */ public Observable synchronize() { return create(OperationSynchronize.synchronize(this)); @@ -1835,7 +1890,7 @@ public Observable synchronize() { * accomplished by acquiring a mutual-exclusion lock for the object * provided as the lock parameter. *

    - * + * *

    * A well-behaved Observable does not interleave its invocations of the * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, @@ -1850,6 +1905,7 @@ public Observable synchronize() { * @return an Observable that is a chronologically well-behaved version of * the source Observable, and that synchronously notifies its * {@link Observer}s + * @see synchronize() */ public Observable synchronize(Object lock) { return create(OperationSynchronize.synchronize(this, lock)); @@ -1866,12 +1922,13 @@ public static Observable synchronize(Observable source) { /** * Emits an item each time interval (containing a sequential number). *

    - * + * * * @param interval interval size in time units (see below) * @param unit time units to use for the interval size * @return an Observable that emits an item each time interval - * @see MSDN: Observable.Interval + * @see interval() + * @see MSDN: Observable.Interval */ public static Observable interval(long interval, TimeUnit unit) { return create(OperationInterval.interval(interval, unit)); @@ -1880,13 +1937,14 @@ public static Observable interval(long interval, TimeUnit unit) { /** * Emits an item each time interval (containing a sequential number). *

    - * + * * * @param interval interval size in time units (see below) * @param unit time units to use for the interval size * @param scheduler the scheduler to use for scheduling the items * @return an Observable that emits an item each time interval - * @see MSDN: Observable.Interval + * @see interval() + * @see MSDN: Observable.Interval */ public static Observable interval(long interval, TimeUnit unit, Scheduler scheduler) { return create(OperationInterval.interval(interval, unit, scheduler)); @@ -1899,7 +1957,7 @@ public static Observable interval(long interval, TimeUnit unit, Scheduler * Note: If events keep firing faster than the timeout then no data will be * emitted. *

    - * + * *

    * Information on debounce vs throttle: *

    @@ -1914,6 +1972,7 @@ public static Observable interval(long interval, TimeUnit unit, Scheduler * @param unit the {@link TimeUnit} for the timeout * @return an {@link Observable} that filters out items that are too * quickly followed by newer items + * @see debounce() * @see #throttleWithTimeout(long, TimeUnit) */ public Observable debounce(long timeout, TimeUnit unit) { @@ -1927,7 +1986,7 @@ public Observable debounce(long timeout, TimeUnit unit) { * Note: If events keep firing faster than the timeout then no data will be * emitted. *

    - * + * *

    * Information on debounce vs throttle: *

    @@ -1944,6 +2003,7 @@ public Observable debounce(long timeout, TimeUnit unit) { * timers that handle the timeout for each event * @return an {@link Observable} that filters out items that are too * quickly followed by newer items + * @see debounce() * @see #throttleWithTimeout(long, TimeUnit, Scheduler) */ public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) { @@ -1957,7 +2017,7 @@ public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) * Note: If events keep firing faster than the timeout then no data will be * emitted. *

    - * + * *

    * Information on debounce vs throttle: *

    @@ -1972,6 +2032,7 @@ public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) * @param unit the {@link TimeUnit} for the timeout * @return an {@link Observable} that filters out items that are too * quickly followed by newer items + * @see throttleWithTimeout() * @see #debounce(long, TimeUnit) */ public Observable throttleWithTimeout(long timeout, TimeUnit unit) { @@ -1985,7 +2046,7 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit) { * Note: If events keep firing faster than the timeout then no data will be * emitted. *

    - * + * *

    * Information on debounce vs throttle: *

    @@ -2002,6 +2063,7 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit) { * timers that handle the timeout for each event * @return an {@link Observable} that filters out items that are too * quickly followed by newer items + * @see throttleWithTimeout() * @see #debounce(long, TimeUnit, Scheduler) */ public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler scheduler) { @@ -2015,12 +2077,13 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler * This differs from {@link #throttleLast} in that this only tracks passage * of time whereas {@link #throttleLast} ticks at scheduled intervals. *

    - * + * * * @param windowDuration time to wait before sending another item after * emitting the last item * @param unit the unit of time for the specified timeout * @return an Observable that performs the throttle operation + * @see throttleFirst() */ public Observable throttleFirst(long windowDuration, TimeUnit unit) { return create(OperationThrottleFirst.throttleFirst(this, windowDuration, unit)); @@ -2033,7 +2096,7 @@ public Observable throttleFirst(long windowDuration, TimeUnit unit) { * This differs from {@link #throttleLast} in that this only tracks passage * of time whereas {@link #throttleLast} ticks at scheduled intervals. *

    - * + * * * @param skipDuration time to wait before sending another item after * emitting the last item @@ -2041,6 +2104,7 @@ public Observable throttleFirst(long windowDuration, TimeUnit unit) { * @param scheduler the {@link Scheduler} to use internally to manage the * timers that handle timeout for each event * @return an Observable that performs the throttle operation + * @see throttleFirst() */ public Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler scheduler) { return create(OperationThrottleFirst.throttleFirst(this, skipDuration, unit, scheduler)); @@ -2054,12 +2118,13 @@ public Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler s * scheduled interval whereas {@link #throttleFirst} does not tick, it just * tracks passage of time. *

    - * + * * * @param intervalDuration duration of windows within which the last item * will be emitted * @param unit the unit of time for the specified interval * @return an Observable that performs the throttle operation + * @see throttleLast() * @see #sample(long, TimeUnit) */ public Observable throttleLast(long intervalDuration, TimeUnit unit) { @@ -2074,12 +2139,13 @@ public Observable throttleLast(long intervalDuration, TimeUnit unit) { * scheduled interval whereas {@link #throttleFirst} does not tick, it just * tracks passage of time. *

    - * + * * * @param intervalDuration duration of windows within which the last item * will be emitted * @param unit the unit of time for the specified interval * @return an Observable that performs the throttle operation + * @see throttleLast() * @see #sample(long, TimeUnit, Scheduler) */ public Observable throttleLast(long intervalDuration, TimeUnit unit, Scheduler scheduler) { @@ -2090,10 +2156,11 @@ public Observable throttleLast(long intervalDuration, TimeUnit unit, Schedule * Wraps each item emitted by a source Observable in a {@link Timestamped} * object. *

    - * + * * * @return an Observable that emits timestamped items from the source * Observable + * @see timestamp() */ public Observable> timestamp() { return create(OperationTimestamp.timestamp(this)); @@ -2102,7 +2169,7 @@ public Observable> timestamp() { /** * Converts a {@link Future} into an Observable. *

    - * + * *

    * You can convert any object that supports the {@link Future} interface * into an Observable that emits the return value of the {@link Future#get} @@ -2116,6 +2183,7 @@ public Observable> timestamp() { * @param the type of object that the {@link Future} returns, and also * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source Future + * @see from() */ public static Observable from(Future future) { return create(OperationToObservableFuture.toObservableFuture(future)); @@ -2124,7 +2192,7 @@ public static Observable from(Future future) { /** * Converts a {@link Future} into an Observable. *

    - * + * *

    * You can convert any object that supports the {@link Future} interface * into an Observable that emits the return value of the {@link Future#get} @@ -2139,6 +2207,7 @@ public static Observable from(Future future) { * @param the type of object that the {@link Future} returns, and also * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source Future + * @see from() */ public static Observable from(Future future, Scheduler scheduler) { return create(OperationToObservableFuture.toObservableFuture(future)).subscribeOn(scheduler); @@ -2147,7 +2216,7 @@ public static Observable from(Future future, Scheduler sched /** * Converts a {@link Future} into an Observable with timeout. *

    - * + * *

    * You can convert any object that supports the {@link Future} interface * into an Observable that emits the return value of the {link Future#get} @@ -2163,6 +2232,7 @@ public static Observable from(Future future, Scheduler sched * @param the type of object that the {@link Future} returns, and also * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source {@link Future} + * @see from() */ public static Observable from(Future future, long timeout, TimeUnit unit) { return create(OperationToObservableFuture.toObservableFuture(future, timeout, unit)); @@ -2172,13 +2242,14 @@ public static Observable from(Future future, long timeout, T * Returns an Observable that emits Boolean values that indicate whether the * pairs of items emitted by two source Observables are equal. *

    - * + * * * @param first the first Observable to compare * @param second the second Observable to compare * @param the type of items emitted by each Observable * @return an Observable that emits Booleans that indicate whether the * corresponding items emitted by the source Observables are equal + * @see sequenceEqual() */ public static Observable sequenceEqual(Observable first, Observable second) { return sequenceEqual(first, second, new Func2() { @@ -2194,7 +2265,7 @@ public Boolean call(T first, T second) { * pairs of items emitted by two source Observables are equal based on the * results of a specified equality function. *

    - * + * * * @param first the first Observable to compare * @param second the second Observable to compare @@ -2203,6 +2274,7 @@ public Boolean call(T first, T second) { * @param the type of items emitted by each Observable * @return an Observable that emits Booleans that indicate whether the * corresponding items emitted by the source Observables are equal + * @see sequenceEqual() */ public static Observable sequenceEqual(Observable first, Observable second, Func2 equality) { return zip(first, second, equality); @@ -2233,6 +2305,7 @@ public static Observable sequenceEqual(Observable firs * each of the source Observables, results in an item that will * be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable o1, Observable o2, Func2 zipFunction) { return create(OperationZip.zip(o1, o2, zipFunction)); @@ -2265,6 +2338,7 @@ public static Observable zip(Observable o1, Observa * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Func3 zipFunction) { return create(OperationZip.zip(o1, o2, o3, zipFunction)); @@ -2298,6 +2372,7 @@ public static Observable zip(Observable o1, Obs * each of the source Observables, results in an item that will * be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Func4 zipFunction) { return create(OperationZip.zip(o1, o2, o3, o4, zipFunction)); @@ -2332,6 +2407,7 @@ public static Observable zip(Observable o1, * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 zipFunction) { return create(OperationZip.zip(o1, o2, o3, o4, o5, zipFunction)); @@ -2365,6 +2441,7 @@ public static Observable zip(Observable * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Func6 zipFunction) { @@ -2400,6 +2477,7 @@ public static Observable zip(Observablezip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Func7 zipFunction) { @@ -2436,6 +2514,7 @@ public static Observable zip(Observablezip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Func8 zipFunction) { @@ -2473,6 +2552,7 @@ public static Observable zip(Observablezip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9, Func9 zipFunction) { @@ -2493,6 +2573,7 @@ public static Observable zip(Observab * source observable values * @return an Observable that combines the source Observables with the * given combine function + * @see combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Func2 combineFunction) { return create(OperationCombineLatest.combineLatest(o1, o2, combineFunction)); @@ -2513,6 +2594,7 @@ public static Observable combineLatest(Observable o * source observable values * @return an Observable that combines the source Observables with the * given combine function + * @see combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Func3 combineFunction) { return create(OperationCombineLatest.combineLatest(o1, o2, o3, combineFunction)); @@ -2534,6 +2616,7 @@ public static Observable combineLatest(ObservablecombineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Func4 combineFunction) { @@ -2557,6 +2640,7 @@ public static Observable combineLatest(ObservablecombineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 combineFunction) { @@ -2581,6 +2665,7 @@ public static Observable combineLatest(ObservablecombineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Func6 combineFunction) { @@ -2606,6 +2691,7 @@ public static Observable combineLatest(Observable * source observable values * @return an Observable that combines the source Observables with the * given combine function + * @see combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Func7 combineFunction) { @@ -2632,6 +2718,7 @@ public static Observable combineLatest(Observ * source observable values * @return an Observable that combines the source Observables with the * given combine function + * @see combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Func8 combineFunction) { @@ -2659,6 +2746,7 @@ public static Observable combineLatest(Ob * source observable values * @return an Observable that combines the source Observables with the * given combine function + * @see combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9, Func9 combineFunction) { @@ -2668,7 +2756,7 @@ public static Observable combineLates /** * Creates an Observable that produces buffers of collected items. *

    - * + * *

    * This Observable produces connected, non-overlapping buffers. The current * buffer is emitted and replaced with a new buffer when the Observable @@ -2687,6 +2775,7 @@ public static Observable combineLates * buffers, which are emitted when the current {@link Observable} * created with the {@link Func0} argument produces a * {@link rx.util.Closing} object + * @see buffer() */ public Observable> buffer(Func0> bufferClosingSelector) { return create(OperationBuffer.buffer(this, bufferClosingSelector)); @@ -2695,7 +2784,7 @@ public Observable> buffer(Func0> /** * Creates an Observable which produces buffers of collected values. *

    - * + * *

    * This Observable produces buffers. Buffers are created when the specified * bufferOpenings Observable produces a {@link rx.util.Opening} @@ -2715,6 +2804,7 @@ public Observable> buffer(Func0> * @return an {@link Observable} that produces buffers that are created and * emitted when the specified {@link Observable}s publish certain * objects + * @see buffer() */ public Observable> buffer(Observable bufferOpenings, Func1> bufferClosingSelector) { return create(OperationBuffer.buffer(this, bufferOpenings, bufferClosingSelector)); @@ -2723,7 +2813,7 @@ public Observable> buffer(Observable bufferOpenings, /** * Creates an Observable that produces buffers of collected items. *

    - * + * *

    * This Observable produces connected, non-overlapping buffers, each * containing count items. When the source Observable completes @@ -2733,6 +2823,7 @@ public Observable> buffer(Observable bufferOpenings, * @param count the maximum size of each buffer before it should be emitted * @return an {@link Observable} that produces connected, non-overlapping * buffers containing at most "count" items + * @see buffer() */ public Observable> buffer(int count) { return create(OperationBuffer.buffer(this, count)); @@ -2741,7 +2832,7 @@ public Observable> buffer(int count) { /** * Creates an Observable which produces buffers of collected items. *

    - * + * *

    * This Observable produces buffers every skip items, each * containing count items. When the source Observable @@ -2756,6 +2847,7 @@ public Observable> buffer(int count) { * @return an {@link Observable} that produces buffers every * skip item containing at most count * items + * @see buffer() */ public Observable> buffer(int count, int skip) { return create(OperationBuffer.buffer(this, count, skip)); @@ -2764,7 +2856,7 @@ public Observable> buffer(int count, int skip) { /** * Creates an Observable that produces buffers of collected values. *

    - * + * *

    * This Observable produces connected, non-overlapping buffers, each of a * fixed duration specified by the timespan argument. When the @@ -2777,6 +2869,7 @@ public Observable> buffer(int count, int skip) { * argument * @return an {@link Observable} that produces connected, non-overlapping * buffers with a fixed duration + * @see buffer() */ public Observable> buffer(long timespan, TimeUnit unit) { return create(OperationBuffer.buffer(this, timespan, unit)); @@ -2785,7 +2878,7 @@ public Observable> buffer(long timespan, TimeUnit unit) { /** * Creates an Observable that produces buffers of collected values. *

    - * + * *

    * This Observable produces connected, non-overlapping buffers, each of a * fixed duration specified by the timespan argument. When the @@ -2800,6 +2893,7 @@ public Observable> buffer(long timespan, TimeUnit unit) { * and start of a buffer * @return an {@link Observable} that produces connected, non-overlapping * buffers with a fixed duration + * @see buffer() */ public Observable> buffer(long timespan, TimeUnit unit, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, unit, scheduler)); @@ -2813,7 +2907,7 @@ public Observable> buffer(long timespan, TimeUnit unit, Scheduler schedu * first). When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each buffer collects values before it * should be emitted and replaced with a new buffer @@ -2823,6 +2917,7 @@ public Observable> buffer(long timespan, TimeUnit unit, Scheduler schedu * @return an {@link Observable} that produces connected, non-overlapping * buffers that are emitted after a fixed duration or when the * buffer reaches maximum capacity (whichever occurs first) + * @see buffer() */ public Observable> buffer(long timespan, TimeUnit unit, int count) { return create(OperationBuffer.buffer(this, timespan, unit, count)); @@ -2836,7 +2931,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count) { * first). When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each buffer collects values before it * should be emitted and replaced with a new buffer @@ -2848,6 +2943,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count) { * @return an {@link Observable} that produces connected, non-overlapping * buffers that are emitted after a fixed duration or when the * buffer has reached maximum capacity (whichever occurs first) + * @see buffer() */ public Observable> buffer(long timespan, TimeUnit unit, int count, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, unit, count, scheduler)); @@ -2861,7 +2957,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count, Sched * source Observable completes or encounters an error, the current buffer is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each buffer collects values before it * should be emitted @@ -2871,6 +2967,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count, Sched * and timeshift arguments * @return an {@link Observable} that produces new buffers periodically and * emits these after a fixed timespan has elapsed. + * @see buffer() */ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) { return create(OperationBuffer.buffer(this, timespan, timeshift, unit)); @@ -2884,7 +2981,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) * source Observable completes or encounters an error, the current buffer is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each buffer collects values before it * should be emitted @@ -2896,6 +2993,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) * and start of a buffer * @return an {@link Observable} that produces new buffers periodically and * emits these after a fixed timespan has elapsed + * @see buffer() */ public Observable> buffer(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, timeshift, unit, scheduler)); @@ -2910,7 +3008,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit, * then be used to create a new Observable to listen for the end of the next * window. *

    - * + * * * @param closingSelector the {@link Func0} used to produce an * {@link Observable} for every window created. When this @@ -2920,6 +3018,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit, * windows, which are emitted when the current {@link Observable} * created with the closingSelector argument emits a * {@link rx.util.Closing} object. + * @see window() */ public Observable> window(Func0> closingSelector) { return create(OperationWindow.window(this, closingSelector)); @@ -2933,7 +3032,7 @@ public Observable> window(Func0 - * + * * * @param windowOpenings the {@link Observable} that, when it produces a * {@link rx.util.Opening} object, causes another @@ -2946,6 +3045,7 @@ public Observable> window(Func0window() */ public Observable> window(Observable windowOpenings, Func1> closingSelector) { return create(OperationWindow.window(this, windowOpenings, closingSelector)); @@ -2958,11 +3058,12 @@ public Observable> window(Observable windowOpen * encounters an error, the current window is emitted, and the event is * propagated. *

    - * + * * * @param count the maximum size of each window before it should be emitted * @return an {@link Observable} that produces connected, non-overlapping * windows containing at most count items + * @see window() */ public Observable> window(int count) { return create(OperationWindow.window(this, count)); @@ -2975,7 +3076,7 @@ public Observable> window(int count) { * completes or encounters an error, the current window is emitted and the * event is propagated. *

    - * + * * * @param count the maximum size of each window before it should be emitted * @param skip how many items need to be skipped before starting a new @@ -2983,6 +3084,7 @@ public Observable> window(int count) { * are equal this is the same operation as {@link #window(int)}. * @return an {@link Observable} that produces windows every "skipped" * items containing at most count items + * @see window() */ public Observable> window(int count, int skip) { return create(OperationWindow.window(this, count, skip)); @@ -2995,7 +3097,7 @@ public Observable> window(int count, int skip) { * Observable completes or encounters an error, the current window is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects items before it * should be emitted and replaced with a new window @@ -3003,6 +3105,7 @@ public Observable> window(int count, int skip) { * argument * @return an {@link Observable} that produces connected, non-overlapping * windows with a fixed duration + * @see window() */ public Observable> window(long timespan, TimeUnit unit) { return create(OperationWindow.window(this, timespan, unit)); @@ -3015,7 +3118,7 @@ public Observable> window(long timespan, TimeUnit unit) { * source Observable completes or encounters an error, the current window is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects items before it * should be emitted and replaced with a new window @@ -3025,6 +3128,7 @@ public Observable> window(long timespan, TimeUnit unit) { * and start of a window * @return an {@link Observable} that produces connected, non-overlapping * windows with a fixed duration + * @see window() */ public Observable> window(long timespan, TimeUnit unit, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, unit, scheduler)); @@ -3038,7 +3142,7 @@ public Observable> window(long timespan, TimeUnit unit, Scheduler * reached first). When the source Observable completes or encounters an * error, the current window is emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects values before it * should be emitted and replaced with a new window @@ -3048,6 +3152,7 @@ public Observable> window(long timespan, TimeUnit unit, Scheduler * @return an {@link Observable} that produces connected, non-overlapping * windows that are emitted after a fixed duration or when the * window has reached maximum capacity (whichever occurs first) + * @see window() */ public Observable> window(long timespan, TimeUnit unit, int count) { return create(OperationWindow.window(this, timespan, unit, count)); @@ -3061,7 +3166,7 @@ public Observable> window(long timespan, TimeUnit unit, int count) * first). When the source Observable completes or encounters an error, the * current window is emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects values before it * should be emitted and replaced with a new window @@ -3073,6 +3178,7 @@ public Observable> window(long timespan, TimeUnit unit, int count) * @return an {@link Observable} that produces connected non-overlapping * windows that are emitted after a fixed duration or when the * window has reached maximum capacity (whichever occurs first). + * @see window() */ public Observable> window(long timespan, TimeUnit unit, int count, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, unit, count, scheduler)); @@ -3086,7 +3192,7 @@ public Observable> window(long timespan, TimeUnit unit, int count, * source Observable completes or encounters an error, the current window is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects values before it * should be emitted @@ -3096,6 +3202,7 @@ public Observable> window(long timespan, TimeUnit unit, int count, * and timeshift arguments * @return an {@link Observable} that produces new windows periodically and * emits these after a fixed timespan has elapsed + * @see window() */ public Observable> window(long timespan, long timeshift, TimeUnit unit) { return create(OperationWindow.window(this, timespan, timeshift, unit)); @@ -3109,7 +3216,7 @@ public Observable> window(long timespan, long timeshift, TimeUnit * source Observable completes or encounters an error, the current window is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects values before it * should be emitted @@ -3121,6 +3228,7 @@ public Observable> window(long timespan, long timeshift, TimeUnit * and start of a window * @return an {@link Observable} that produces new windows periodically and * emits these after a fixed timespan has elapsed + * @see window() */ public Observable> window(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, timeshift, unit, scheduler)); @@ -3149,6 +3257,7 @@ public Observable> window(long timespan, long timeshift, TimeUnit * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable> ws, final FuncN zipFunction) { return ws.toList().mapMany(new Func1>, Observable>() { @@ -3182,6 +3291,7 @@ public Observable call(List> wsList) { * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Iterable> ws, FuncN zipFunction) { return create(OperationZip.zip(ws, zipFunction)); @@ -3197,6 +3307,7 @@ public static Observable zip(Iterable> ws, FuncN< * the filter * @return an Observable that emits only those items in the original * Observable that the filter evaluates as {@code true} + * @see filter() */ public Observable filter(Func1 predicate) { return create(OperationFilter.filter(this, predicate)); @@ -3209,6 +3320,7 @@ public Observable filter(Func1 predicate) { * * * @return an Observable of sequentially distinct items + * @see distinctUntilChanged() * @see MSDN: Observable.distinctUntilChanged */ public Observable distinctUntilChanged() { @@ -3226,6 +3338,7 @@ public Observable distinctUntilChanged() { * value that is used for deciding whether an item is * sequentially distinct from another one or not * @return an Observable of sequentially distinct items + * @see distinctUntilChanged() * @see MSDN: Observable.distinctUntilChanged */ public Observable distinctUntilChanged(Func1 keySelector) { @@ -3239,6 +3352,7 @@ public Observable distinctUntilChanged(Func1 keyS * * * @return an Observable of distinct items + * @see distinct() * @see MSDN: Observable.distinct */ public Observable distinct() { @@ -3255,6 +3369,7 @@ public Observable distinct() { * value that is used to decide whether an item is * distinct from another one or not * @return an Observable that emits distinct items + * @see distinct() * @see MSDN: Observable.distinct */ public Observable distinct(Func1 keySelector) { @@ -3273,6 +3388,7 @@ public Observable distinct(Func1 keySelector) { * or equal to the number of elements in * the source sequence * @throws IndexOutOfBoundsException if index is less than 0 + * @see elementAt() */ public Observable elementAt(int index) { return create(OperationElementAt.elementAt(this, index)); @@ -3290,6 +3406,7 @@ public Observable elementAt(int index) { * the source sequence, or the default item if the index is outside * the bounds of the source sequence * @throws IndexOutOfBoundsException if index is less than 0 + * @see elementAtOrDefault() */ public Observable elementAtOrDefault(int index, T defaultValue) { return create(OperationElementAt.elementAtOrDefault(this, index, defaultValue)); @@ -3308,6 +3425,7 @@ public Observable elementAtOrDefault(int index, T defaultValue) { * * @param predicate the condition to test every element * @return a subscription function for creating the target Observable + * @see exists() * @see MSDN: Observable.Any Note: the description in this page is wrong. */ public Observable exists(Func1 predicate) { @@ -3322,6 +3440,7 @@ public Observable exists(Func1 predicate) { * @param element the item to search in the sequence * @return an Observable that emits true if the item is in the * source sequence + * @see contains() * @see MSDN: Observable.Contains */ public Observable contains(final T element) { @@ -3337,12 +3456,13 @@ public Boolean call(T t1) { * {@link Observer#onCompleted onCompleted} or * {@link Observer#onError onError}. *

    - * + * * * @param action an {@link Action0} to be invoked when the source * Observable finishes * @return an Observable that emits the same items as the source Observable, * then invokes the {@link Action0} + * @see finallyDo() * @see MSDN: Observable.Finally Method */ public Observable finallyDo(Action0 action) { @@ -3365,6 +3485,7 @@ public Observable finallyDo(Action0 action) { * transformation function to each item emitted by the source * Observable and merging the results of the Observables obtained * from this transformation. + * @see flatMap() * @see #mapMany(Func1) */ public Observable flatMap(Func1> func) { @@ -3381,6 +3502,7 @@ public Observable flatMap(Func1where() * @see #filter(Func1) */ public Observable where(Func1 predicate) { @@ -3396,7 +3518,8 @@ public Observable where(Func1 predicate) { * @param func a function to apply to each item emitted by the Observable * @return an Observable that emits the items from the source Observable, * transformed by the given function - * @see MSDN: Observable.Select + * @see map() + * @see MSDN: Observable.Select */ public Observable map(Func1 func) { return create(OperationMap.map(this, func)); @@ -3413,7 +3536,8 @@ public Observable map(Func1 func) { * additional parameter. * @return an Observable that emits the items from the source Observable, * transformed by the given function - * @see MSDN: Observable.Select + * @see mapWithIndex() + * @see MSDN: Observable.Select */ public Observable mapWithIndex(Func2 func) { return create(OperationMap.mapWithIndex(this, func)); @@ -3435,6 +3559,7 @@ public Observable mapWithIndex(Func2 fun * transformation function to each item emitted by the source * Observable and merging the results of the Observables obtained * from this transformation. + * @see mapMany() * @see #flatMap(Func1) */ public Observable mapMany(Func1> func) { @@ -3450,6 +3575,7 @@ public Observable mapMany(Func1materialize() * @see MSDN: Observable.materialize */ public Observable> materialize() { @@ -3460,12 +3586,13 @@ public Observable> materialize() { * Asynchronously subscribes and unsubscribes Observers on the specified * {@link Scheduler}. *

    - * + * * * @param scheduler the {@link Scheduler} to perform subscription and * unsubscription actions on * @return the source Observable modified so that its subscriptions and * unsubscriptions happen on the specified {@link Scheduler} + * @see subscribeOn() */ public Observable subscribeOn(Scheduler scheduler) { return create(OperationSubscribeOn.subscribeOn(this, scheduler)); @@ -3475,11 +3602,12 @@ public Observable subscribeOn(Scheduler scheduler) { * Asynchronously notify {@link Observer}s on the specified * {@link Scheduler}. *

    - * + * * * @param scheduler the {@link Scheduler} to notify {@link Observer}s on * @return the source Observable modified so that its {@link Observer}s are * notified on the specified {@link Scheduler} + * @see observeOn() */ public Observable observeOn(Scheduler scheduler) { return create(OperationObserveOn.observeOn(this, scheduler)); @@ -3491,13 +3619,14 @@ public Observable observeOn(Scheduler scheduler) { * objects emitted by the source Observable into the items or notifications * they represent. *

    - * + * * * @return an Observable that emits the items and notifications embedded in * the {@link Notification} objects emitted by the source Observable - * @see MSDN: Observable.dematerialize * @throws Throwable if the source Observable is not of type - * {@code Observable>}. + * {@code Observable>} + * @see dematerialize() + * @see MSDN: Observable.dematerialize */ @SuppressWarnings("unchecked") public Observable dematerialize() { @@ -3531,6 +3660,7 @@ public Observable dematerialize() { * take over if the source Observable encounters an * error * @return the original Observable, with appropriately modified behavior + * @see onErrorResumeNext() */ public Observable onErrorResumeNext(final Func1> resumeFunction) { return create(OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction(this, resumeFunction)); @@ -3563,6 +3693,7 @@ public Observable onErrorResumeNext(final Func1onErrorResumeNext() */ public Observable onErrorResumeNext(final Observable resumeSequence) { return create(OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable(this, resumeSequence)); @@ -3600,6 +3731,7 @@ public Observable onErrorResumeNext(final Observable resumeSeque * take over if the source Observable encounters an * error * @return the original Observable, with appropriately modified behavior + * @see onExceptionResumeNextViaObservable() */ public Observable onExceptionResumeNext(final Observable resumeSequence) { return create(OperationOnExceptionResumeNextViaObservable.onExceptionResumeNextViaObservable(this, resumeSequence)); @@ -3610,7 +3742,7 @@ public Observable onExceptionResumeNext(final Observable resumeS * rather than invoking {@link Observer#onError onError} if it encounters an * error. *

    - * + * *

    * By default, when an Observable encounters an error that prevents it from * emitting the expected item to its {@link Observer}, the Observable @@ -3630,6 +3762,7 @@ public Observable onExceptionResumeNext(final Observable resumeS * Observable will emit if the source Observable * encounters an error * @return the original Observable with appropriately modified behavior + * @see onErrorReturn() */ public Observable onErrorReturn(Func1 resumeFunction) { return create(OperationOnErrorReturn.onErrorReturn(this, resumeFunction)); @@ -3656,7 +3789,8 @@ public Observable onErrorReturn(Func1 resumeFunction) * @return an Observable that emits a single item that is the result of * accumulating the output from the source Observable * @throws IllegalArgumentException if the Observable sequence is empty - * @see MSDN: Observable.Aggregate + * @see reduce() + * @see MSDN: Observable.Aggregate * @see Wikipedia: Fold (higher-order function) */ public Observable reduce(Func2 accumulator) { @@ -3676,7 +3810,8 @@ public Observable reduce(Func2 accumulator) { * * @return an Observable that emits the number of counted elements of the * source Observable as its single item - * @see MSDN: Observable.Count + * @see count() + * @see MSDN: Observable.Count */ public Observable count() { return reduce(0, new Func2() { @@ -3696,7 +3831,8 @@ public Integer call(Integer t1, T t2) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see MSDN: Observable.Sum + * @see sum() + * @see MSDN: Observable.Sum */ public static Observable sum(Observable source) { return OperationSum.sum(source); @@ -3711,7 +3847,8 @@ public static Observable sum(Observable source) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see MSDN: Observable.Sum + * @see sumLongs() + * @see MSDN: Observable.Sum */ public static Observable sumLongs(Observable source) { return OperationSum.sumLongs(source); @@ -3726,7 +3863,8 @@ public static Observable sumLongs(Observable source) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see MSDN: Observable.Sum + * @see sumFloats() + * @see MSDN: Observable.Sum */ public static Observable sumFloats(Observable source) { return OperationSum.sumFloats(source); @@ -3741,7 +3879,8 @@ public static Observable sumFloats(Observable source) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see MSDN: Observable.Sum + * @see sumDoubles() + * @see MSDN: Observable.Sum */ public static Observable sumDoubles(Observable source) { return OperationSum.sumDoubles(source); @@ -3757,7 +3896,8 @@ public static Observable sumDoubles(Observable source) { * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item * @throws IllegalArgumentException if the Observable sequence is empty - * @see MSDN: Observable.Average + * @see average() + * @see MSDN: Observable.Average */ public static Observable average(Observable source) { return OperationAverage.average(source); @@ -3772,7 +3912,8 @@ public static Observable average(Observable source) { * @param source source observable to compute the average of * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item - * @see MSDN: Observable.Average + * @see averageLongs() + * @see MSDN: Observable.Average */ public static Observable averageLongs(Observable source) { return OperationAverage.averageLongs(source); @@ -3787,7 +3928,8 @@ public static Observable averageLongs(Observable source) { * @param source source observable to compute the average of * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item - * @see MSDN: Observable.Average + * @see averageFloats() + * @see MSDN: Observable.Average */ public static Observable averageFloats(Observable source) { return OperationAverage.averageFloats(source); @@ -3802,7 +3944,8 @@ public static Observable averageFloats(Observable source) { * @param source source observable to compute the average of * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item - * @see MSDN: Observable.Average + * @see averageDoubles() + * @see MSDN: Observable.Average */ public static Observable averageDoubles(Observable source) { return OperationAverage.averageDoubles(source); @@ -3834,6 +3977,7 @@ public static > Observable min(Observable * @return an Observable that emits the minimum value according to the * specified comparator * @throws IllegalArgumentException if the source is empty + * @see min() * @see MSDN: Observable.Min */ public Observable min(Comparator comparator) { @@ -3850,6 +3994,7 @@ public Observable min(Comparator comparator) { * @param selector the key selector function * @return an Observable that emits a List of the items with the minimum key * value + * @see minBy() * @see MSDN: Observable.MinBy */ public > Observable> minBy(Func1 selector) { @@ -3867,6 +4012,7 @@ public > Observable> minBy(Func1 s * @param comparator the comparator used to compare key values * @return an Observable that emits a List of the elements with the minimum * key value according to the specified comparator + * @see minBy() * @see MSDN: Observable.MinBy */ public Observable> minBy(Func1 selector, Comparator comparator) { @@ -3882,6 +4028,7 @@ public Observable> minBy(Func1 selector, Comparator * @param source an Observable to determine the maximum item of * @return an Observable that emits the maximum element * @throws IllegalArgumentException if the source is empty + * @see max() * @see MSDN: Observable.Max */ public static > Observable max(Observable source) { @@ -3899,6 +4046,7 @@ public static > Observable max(Observable * @return an Observable that emits the maximum item according to the * specified comparator * @throws IllegalArgumentException if the source is empty + * @see max() * @see MSDN: Observable.Max */ public Observable max(Comparator comparator) { @@ -3914,6 +4062,7 @@ public Observable max(Comparator comparator) { * @param selector the key selector function * @return an Observable that emits a List of the items with the maximum key * value + * @see maxBy() * @see MSDN: Observable.MaxBy */ public > Observable> maxBy(Func1 selector) { @@ -3931,6 +4080,7 @@ public > Observable> maxBy(Func1 s * @param comparator the comparator used to compare key values * @return an Observable that emits a List of the elements with the maximum * key value according to the specified comparator + * @see maxBy() * @see MSDN: Observable.MaxBy */ public Observable> maxBy(Func1 selector, Comparator comparator) { @@ -3946,6 +4096,7 @@ public Observable> maxBy(Func1 selector, Comparator * * @return a {@link ConnectableObservable} that upon connection causes the * source Observable to emit items to its {@link Observer}s + * @see replay() */ public ConnectableObservable replay() { return OperationMulticast.multicast(this, ReplaySubject. create()); @@ -3968,6 +4119,7 @@ public ConnectableObservable replay() { * * @param retryCount number of retry attempts before failing * @return an Observable with retry logic + * @see retry() */ public Observable retry(int retryCount) { return create(OperationRetry.retry(this, retryCount)); @@ -3990,6 +4142,7 @@ public Observable retry(int retryCount) { * output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. * * @return an Observable with retry logic + * @see retry() */ public Observable retry() { return create(OperationRetry.retry(this)); @@ -4000,7 +4153,7 @@ public Observable retry() { * auto-subscribes to the source Observable rather than returning a * {@link ConnectableObservable}. *

    - * + * *

    * This is useful when you want an Observable to cache responses and you * can't control the subscribe/unsubscribe behavior of all the @@ -4013,6 +4166,7 @@ public Observable retry() { * * @return an Observable that, when first subscribed to, caches all of its * notifications for the benefit of subsequent subscribers. + * @see cache() */ public Observable cache() { return create(OperationCache.cache(this)); @@ -4030,6 +4184,7 @@ public Observable cache() { * {@code Observable} * @return an Observable with the output of the {@link Func1} executed on a * {@link Scheduler} + * @see parallel() */ public Observable parallel(Func1, Observable> f) { return OperationParallel.parallel(this, f); @@ -4047,6 +4202,7 @@ public Observable parallel(Func1, Observable> f) { * @param s a {@link Scheduler} to perform the work on * @return an Observable with the output of the {@link Func1} executed on a * {@link Scheduler} + * @see parallel() */ public Observable parallel(final Func1, Observable> f, final Scheduler s) { @@ -4055,34 +4211,51 @@ public Observable parallel(final Func1, Observable> f, f /** - * Merges an Observable> to Observable> - * with number of inner Observables as defined by parallelObservables. + * Merges an Observable<Observable<T>> to + * Observable<Observable<T>> with the number of + * inner Observables defined by parallelObservables. + *

    + * For example, if the original + * Observable<Observable<T>> has 100 Observables to + * be emitted and parallelObservables is 8, the 100 will be + * grouped onto 8 output Observables. *

    - * For example, if the original Observable> has 100 Observables to be emitted and parallelObservables - * is defined as 8, the 100 will be grouped onto 8 output Observables. + * This is a mechanism for efficiently processing n number of + * Observables on a smaller m number of resources (typically CPU + * cores). *

    - * This is a mechanism for efficiently processing N number of Observables on a smaller N number of resources (typically CPU cores). + * * - * @param parallelObservables - * the number of Observables to merge into. - * @return an Observable of Observables constrained to number defined by parallelObservables. + * @param parallelObservables the number of Observables to merge into + * @return an Observable of Observables constrained to number defined by + * parallelObservables + * @see parallelMerge() */ public static Observable> parallelMerge(Observable> source, int parallelObservables) { return OperationParallelMerge.parallelMerge(source, parallelObservables); } /** - * Merges an Observable> to Observable> - * with number of inner Observables as defined by parallelObservables and runs each Observable on the defined Scheduler. + * Merges an Observable<Observable<T>> to + * Observable<Observable<T>> with the number of + * inner Observables defined by parallelObservables and runs + * each Observable on the defined Scheduler. *

    - * For example, if the original Observable> has 100 Observables to be emitted and parallelObservables - * is defined as 8, the 100 will be grouped onto 8 output Observables. + * For example, if the original + * Observable<Observable<T>> has 100 Observables to + * be emitted and parallelObservables is 8, the 100 will be + * grouped onto 8 output Observables. *

    - * This is a mechanism for efficiently processing N number of Observables on a smaller N number of resources (typically CPU cores). + * This is a mechanism for efficiently processing n number of + * Observables on a smaller m number of resources (typically CPU + * cores). + *

    + * * - * @param parallelObservables - * the number of Observables to merge into. - * @return an Observable of Observables constrained to number defined by parallelObservables. + * @param parallelObservables the number of Observables to merge into + * @return an Observable of Observables constrained to number defined by + * parallelObservables. + * @see parallelMerge() */ public static Observable> parallelMerge(Observable> source, int parallelObservables, Scheduler scheduler) { return OperationParallelMerge.parallelMerge(source, parallelObservables, scheduler); @@ -4094,10 +4267,11 @@ public static Observable> parallelMerge(Observable - * + * * * @return a {@link ConnectableObservable} that upon connection causes the * source Observable to emit items to its {@link Observer}s + * @see publish() */ public ConnectableObservable publish() { return OperationMulticast.multicast(this, PublishSubject. create()); @@ -4107,9 +4281,10 @@ public ConnectableObservable publish() { * Returns a {@link ConnectableObservable} that shares a single subscription * that contains the last notification only. *

    - * + * * * @return a {@link ConnectableObservable} + * @see publishLast() */ public ConnectableObservable publishLast() { return OperationMulticast.multicast(this, AsyncSubject. create()); @@ -4119,7 +4294,8 @@ public ConnectableObservable publishLast() { * Synonymous with reduce(). *

    * - * + * + * @see aggregate() * @see #reduce(Func2) */ public Observable aggregate(Func2 accumulator) { @@ -4148,6 +4324,7 @@ public Observable aggregate(Func2 accumulator) { * @return an Observable that emits a single item that is the result of * accumulating the output from the items emitted by the source * Observable + * @see reduce() * @see MSDN: Observable.Aggregate * @see Wikipedia: Fold (higher-order function) */ @@ -4160,6 +4337,7 @@ public Observable reduce(R initialValue, Func2 accumulat *

    * * + * @see aggregate() * @see #reduce(Object, Func2) */ public Observable aggregate(R initialValue, Func2 accumulator) { @@ -4187,7 +4365,8 @@ public Observable aggregate(R initialValue, Func2 accumu * accumulator call * @return an Observable that emits the results of each call to the * accumulator function - * @see MSDN: Observable.Scan + * @see scan() + * @see MSDN: Observable.Scan */ public Observable scan(Func2 accumulator) { return create(OperationScan.scan(this, accumulator)); @@ -4197,12 +4376,13 @@ public Observable scan(Func2 accumulator) { * Returns an Observable that emits the results of sampling the items * emitted by the source Observable at a specified time interval. *

    - * + * * * @param period the sampling rate * @param unit the {@link TimeUnit} in which period is defined * @return an Observable that emits the results of sampling the items * emitted by the source Observable at the specified time interval + * @see sample() */ public Observable sample(long period, TimeUnit unit) { return create(OperationSample.sample(this, period, unit)); @@ -4212,13 +4392,14 @@ public Observable sample(long period, TimeUnit unit) { * Returns an Observable that emits the results of sampling the items * emitted by the source Observable at a specified time interval. *

    - * + * * * @param period the sampling rate * @param unit the {@link TimeUnit} in which period is defined * @param scheduler the {@link Scheduler} to use when sampling * @return an Observable that emits the results of sampling the items * emitted by the source Observable at the specified time interval + * @see sample() */ public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { return create(OperationSample.sample(this, period, unit, scheduler)); @@ -4246,7 +4427,8 @@ public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { * accumulator call * @return an Observable that emits the results of each call to the * accumulator function - * @see MSDN: Observable.Scan + * @see scan() + * @see MSDN: Observable.Scan */ public Observable scan(R initialValue, Func2 accumulator) { return create(OperationScan.scan(this, initialValue, accumulator)); @@ -4256,12 +4438,13 @@ public Observable scan(R initialValue, Func2 accumulator * Returns an Observable that emits a Boolean that indicates whether all of * the items emitted by the source Observable satisfy a condition. *

    - * + * * * @param predicate a function that evaluates an item and returns a Boolean * @return an Observable that emits true if all items emitted * by the source Observable satisfy the predicate; otherwise, * false + * @see all() */ public Observable all(Func1 predicate) { return create(OperationAll.all(this, predicate)); @@ -4281,6 +4464,7 @@ public Observable all(Func1 predicate) { * @return an Observable that is identical to the source Observable except * that it does not emit the first num items that the * source emits + * @see skip() */ public Observable skip(int num) { return create(OperationSkip.skip(this, num)); @@ -4294,8 +4478,9 @@ public Observable skip(int num) { * * @return an Observable that emits only the very first item from the * source, or none if the source Observable completes without - * emitting a single item. - * @see MSDN: Observable.First + * emitting a single item + * @see first() + * @see MSDN: Observable.First */ public Observable first() { return take(1); @@ -4310,8 +4495,9 @@ public Observable first() { * @param predicate the condition any source emitted item has to satisfy * @return an Observable that emits only the very first item satisfying the * given condition from the source, or none if the source Observable - * completes without emitting a single matching item. - * @see MSDN: Observable.First + * completes without emitting a single matching item + * @see first() + * @see MSDN: Observable.First */ public Observable first(Func1 predicate) { return skipWhile(not(predicate)).take(1); @@ -4328,7 +4514,8 @@ public Observable first(Func1 predicate) { * @return an Observable that emits only the very first item from the * source, or a default value if the source Observable completes * without emitting a single item - * @see MSDN: Observable.FirstOrDefault + * @see firstOrDefault() + * @see MSDN: Observable.FirstOrDefault */ public Observable firstOrDefault(T defaultValue) { return create(OperationFirstOrDefault.firstOrDefault(this, defaultValue)); @@ -4346,7 +4533,8 @@ public Observable firstOrDefault(T defaultValue) { * doesn't emit anything that satisfies the given condition * @return an Observable that emits only the very first item from the source * that satisfies the given condition, or a default value otherwise - * @see MSDN: Observable.FirstOrDefault + * @see firstOrDefault() + * @see MSDN: Observable.FirstOrDefault */ public Observable firstOrDefault(Func1 predicate, T defaultValue) { return create(OperationFirstOrDefault.firstOrDefault(this, predicate, defaultValue)); @@ -4356,12 +4544,12 @@ public Observable firstOrDefault(Func1 predicate, T defau * Returns the elements of the specified sequence or the specified default * value in a singleton sequence if the sequence is empty. *

    - * + * * * @param defaultValue the value to return if the sequence is empty * @return an Observable that emits the specified default value if the * source is empty; otherwise, the items emitted by the source - * + * @see defaultIfEmpty() * @see MSDN: Observable.DefaultIfEmpty */ public Observable defaultIfEmpty(T defaultValue) { @@ -4384,6 +4572,7 @@ public Observable defaultIfEmpty(T defaultValue) { * from the source Observable, or all of the items from the source * Observable if that Observable emits fewer than num * items + * @see take() */ public Observable take(final int num) { return create(OperationTake.take(this, num)); @@ -4393,13 +4582,14 @@ public Observable take(final int num) { * Returns an Observable that emits items emitted by the source Observable * so long as a specified condition is true. *

    - * + * * * @param predicate a function that evaluates an item emitted by the source * Observable and returns a Boolean * @return an Observable that emits the items from the source Observable so * long as each item satisfies the condition defined by * predicate + * @see takeWhile() */ public Observable takeWhile(final Func1 predicate) { return create(OperationTakeWhile.takeWhile(this, predicate)); @@ -4410,7 +4600,7 @@ public Observable takeWhile(final Func1 predicate) { * so long as a given predicate remains true, where the predicate can * operate on both the item and its index relative to the complete sequence. *

    - * + * * * @param predicate a function to test each item emitted by the source * Observable for a condition; the second parameter of the @@ -4418,6 +4608,7 @@ public Observable takeWhile(final Func1 predicate) { * @return an Observable that emits items from the source Observable so long * as the predicate continues to return true for each * item, then completes + * @see takeWhileWithIndex() */ public Observable takeWhileWithIndex(final Func2 predicate) { return create(OperationTakeWhile.takeWhileWithIndex(this, predicate)); @@ -4431,8 +4622,9 @@ public Observable takeWhileWithIndex(final Func2MSDN: Observable.First + * emitting a single item + * @see first() + * @see MSDN: Observable.First * @see #first() */ public Observable takeFirst() { @@ -4448,8 +4640,9 @@ public Observable takeFirst() { * @param predicate the condition any source emitted item has to satisfy * @return an Observable that emits only the very first item satisfying the * given condition from the source, or none if the source Observable - * completes without emitting a single matching item. - * @see MSDN: Observable.First + * completes without emitting a single matching item + * @see first() + * @see MSDN: Observable.First * @see #first(Func1) */ public Observable takeFirst(Func1 predicate) { @@ -4460,12 +4653,13 @@ public Observable takeFirst(Func1 predicate) { * Returns an Observable that emits only the last count items * emitted by the source Observable. *

    - * + * * * @param count the number of items to emit from the end of the sequence * emitted by the source Observable * @return an Observable that emits only the last count items * emitted by the source Observable + * @see takeLast() */ public Observable takeLast(final int count) { return create(OperationTakeLast.takeLast(this, count)); @@ -4475,7 +4669,7 @@ public Observable takeLast(final int count) { * Returns an Observable that emits the items from the source Observable * only until the other Observable emits an item. *

    - * + * * * @param other the Observable whose first emitted item will cause * takeUntil to stop emitting items from the @@ -4483,6 +4677,7 @@ public Observable takeLast(final int count) { * @param the type of items emitted by other * @return an Observable that emits the items of the source Observable until * such time as other emits its first item + * @see takeUntil() */ public Observable takeUntil(Observable other) { return OperationTakeUntil.takeUntil(this, other); @@ -4501,7 +4696,8 @@ public Observable takeUntil(Observable other) { * as a second parameter. * @return an Observable that emits all items from the source Observable as * soon as the condition becomes false - * @see MSDN: Observable.SkipWhile + * @see skipWhileWithIndex() + * @see MSDN: Observable.SkipWhile */ public Observable skipWhileWithIndex(Func2 predicate) { return create(OperationSkipWhile.skipWhileWithIndex(this, predicate)); @@ -4518,7 +4714,8 @@ public Observable skipWhileWithIndex(Func2 predi * Observable for a condition * @return an Observable that emits all items from the source Observable as * soon as the condition becomes false - * @see MSDN: Observable.SkipWhile + * @see skipWhile() + * @see MSDN: Observable.SkipWhile */ public Observable skipWhile(Func1 predicate) { return create(OperationSkipWhile.skipWhile(this, predicate)); @@ -4533,15 +4730,14 @@ public Observable skipWhile(Func1 predicate) { * from the front of the queue and produced on the result sequence. This * causes elements to be delayed. *

    - * + * * * @param count number of elements to bypass at the end of the source * sequence * @return an Observable sequence emitting the source sequence items * except for the bypassed ones at the end - * * @throws IndexOutOfBoundsException if count is less than zero - * + * @see skipLast() * @see MSDN: Observable.SkipLast */ public Observable skipLast(int count) { @@ -4568,6 +4764,7 @@ public Observable skipLast(int count) { * * @return an Observable that emits a single item: a List containing all of * the items emitted by the source Observable. + * @see toList() */ public Observable> toList() { return create(OperationToObservableList.toObservableList(this)); @@ -4586,6 +4783,7 @@ public Observable> toList() { * all other items emitted by the Observable * @return an Observable that emits the items from the source Observable in * sorted order + * @see toSortedList() */ public Observable> toSortedList() { return create(OperationToObservableSortedList.toSortedList(this)); @@ -4602,6 +4800,7 @@ public Observable> toSortedList() { * indicates their sort order * @return an Observable that emits the items from the source Observable in * sorted order + * @see toSortedList() */ public Observable> toSortedList(Func2 sortFunction) { return create(OperationToObservableSortedList.toSortedList(this, sortFunction)); @@ -4616,6 +4815,7 @@ public Observable> toSortedList(Func2 sor * @param values Iterable of the items you want the modified Observable to * emit first * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(Iterable values) { return concat(Observable. from(values), this); @@ -4627,9 +4827,11 @@ public Observable startWith(Iterable values) { *

    * * - * @param values iterable of the items you want the modified Observable to emit first + * @param values iterable of the items you want the modified Observable to + * emit first * @param scheduler the scheduler to emit the prepended values on * @return an Observable that exhibits the modified behavior + * @see startWith() * @see MSDN: Observable.StartWith */ public Observable startWith(Iterable values, Scheduler scheduler) { @@ -4645,6 +4847,7 @@ public Observable startWith(Iterable values, Scheduler scheduler) { * @param values the items you want the modified Observable to emit first * @param scheduler the scheduler to emit the prepended values on * @return an Observable that exhibits the modified behavior + * @see startWith() * @see MSDN: Observable.StartWith */ public Observable startWith(T[] values, Scheduler scheduler) { @@ -4659,6 +4862,7 @@ public Observable startWith(T[] values, Scheduler scheduler) { * * @param t1 item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1) { return concat(Observable. from(t1), this); @@ -4673,6 +4877,7 @@ public Observable startWith(T t1) { * @param t1 first item to emit * @param t2 second item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2) { return concat(Observable. from(t1, t2), this); @@ -4688,6 +4893,7 @@ public Observable startWith(T t1, T t2) { * @param t2 second item to emit * @param t3 third item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3) { return concat(Observable. from(t1, t2, t3), this); @@ -4704,6 +4910,7 @@ public Observable startWith(T t1, T t2, T t3) { * @param t3 third item to emit * @param t4 fourth item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4) { return concat(Observable. from(t1, t2, t3, t4), this); @@ -4721,6 +4928,7 @@ public Observable startWith(T t1, T t2, T t3, T t4) { * @param t4 fourth item to emit * @param t5 fifth item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5) { return concat(Observable. from(t1, t2, t3, t4, t5), this); @@ -4739,6 +4947,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5) { * @param t5 fifth item to emit * @param t6 sixth item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { return concat(Observable. from(t1, t2, t3, t4, t5, t6), this); @@ -4758,6 +4967,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { * @param t6 sixth item to emit * @param t7 seventh item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7), this); @@ -4778,6 +4988,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { * @param t7 seventh item to emit * @param t8 eighth item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8), this); @@ -4799,6 +5010,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { * @param t8 eighth item to emit * @param t9 ninth item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8, t9), this); @@ -4809,7 +5021,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T * criterion, and emits these grouped items as {@link GroupedObservable}s, * one GroupedObservable per group. *

    - * + * * * @param keySelector a function that extracts the key from an item * @param elementSelector a function to map a source item to an item in a @@ -4821,6 +5033,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T * which corresponds to a unique key value and emits items * representing items from the source Observable that share that key * value + * @see groupBy */ public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) { return create(OperationGroupBy.groupBy(this, keySelector, elementSelector)); @@ -4831,7 +5044,7 @@ public Observable> groupBy(final Func1 - * + * * * @param keySelector a function that extracts the key for each item * @param the key type @@ -4839,6 +5052,7 @@ public Observable> groupBy(final Func1groupBy */ public Observable> groupBy(final Func1 keySelector) { return create(OperationGroupBy.groupBy(this, keySelector)); @@ -4851,9 +5065,10 @@ public Observable> groupBy(final Func1any operator but renamed in * RxJava to better match Java naming idioms. *

    - * + * * * @return an Observable that emits a Boolean + * @see isEmpty() * @see MSDN: Observable.Any */ public Observable isEmpty() { @@ -4865,9 +5080,10 @@ public Observable isEmpty() { * source or an IllegalArgumentException if the source * {@link Observable} is empty. *

    - * + * * * @return + * @see last() */ public Observable last() { return create(OperationLast.last(this)); @@ -4887,11 +5103,12 @@ public BlockingObservable toBlockingObservable() { /** * Converts the items emitted by an Observable to the specified type. *

    - * + * * * @param klass the target class type which the items will be converted to * @return an Observable that emits each item from the source Observable * converted to the specified type + * @see cast() * @see MSDN: Observable.Cast */ public Observable cast(final Class klass) { @@ -4901,12 +5118,13 @@ public Observable cast(final Class klass) { /** * Filters the items emitted by an Observable based on the specified type. *

    - * + * * * @param klass the class type to filter the items emitted by the source * Observable * @return an Observable that emits items from the source Observable of * type klass. + * @see ofClass() * @see MSDN: Observable.OfType */ public Observable ofType(final Class klass) { @@ -4921,11 +5139,11 @@ public Boolean call(T t) { * Ignores all items emitted by an Observable and only calls * onCompleted or onError. *

    - * + * * * @return an empty Observable that only calls onCompleted or * onError - * + * @see ignoreElements() * @see MSDN: Observable.IgnoreElements */ public Observable ignoreElements() { @@ -4938,13 +5156,14 @@ public Observable ignoreElements() { * isn't received within the specified timeout duration starting from its * predecessor, a TimeoutException is propagated to the observer. *

    - * + * * * @param timeout maximum duration between values before a timeout occurs * @param timeUnit the unit of time which applies to the * timeout argument. * @return the source Observable with a TimeoutException in * case of a timeout + * @see timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit) { @@ -4958,7 +5177,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit) { * predecessor, the other observable sequence is used to produce future * messages from that point on. *

    - * + * * * @param timeout maximum duration between values before a timeout occurs * @param timeUnit the unit of time which applies to the @@ -4966,6 +5185,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit) { * @param other sequence to return in case of a timeout * @return the source sequence switching to the other sequence in case of a * timeout + * @see timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Observable other) { @@ -4978,7 +5198,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Observable - * + * * * @param timeout maximum duration between values before a timeout occurs * @param timeUnit the unit of time which applies to the @@ -4986,6 +5206,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, ObservableTimeoutException in case * of a timeout + * @see timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { @@ -4999,7 +5220,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler schedule * predecessor, the other observable sequence is used to produce future * messages from that point on. *

    - * + * * * @param timeout maximum duration between values before a timeout occurs * @param timeUnit the unit of time which applies to the @@ -5008,6 +5229,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler schedule * @param scheduler Scheduler to run the timeout timers on * @return the source sequence switching to the other sequence in case of a * timeout + * @see timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Observable other, Scheduler scheduler) { @@ -5018,9 +5240,10 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Observable - * + * * * @return an Observable that emits time interval information items + * @see timeInterval() * @see MSDN: Observable.TimeInterval */ public Observable> timeInterval() { @@ -5031,10 +5254,11 @@ public Observable> timeInterval() { * Records the time interval between consecutive items emitted by an * Observable, using the specified Scheduler to compute time intervals. *

    - * + * * * @param scheduler Scheduler used to compute time intervals * @return an Observable that emits time interval information items + * @see timeInterval() * @see MSDN: Observable.TimeInterval */ public Observable> timeInterval(Scheduler scheduler) { @@ -5044,13 +5268,14 @@ public Observable> timeInterval(Scheduler scheduler) { /** * Constructs an Observable that depends on a resource object. *

    - * + * * * @param resourceFactory the factory function to obtain a resource object * that depends on the Observable * @param observableFactory the factory function to obtain an Observable * @return the Observable whose lifetime controls the lifetime of the * dependent resource object + * @see using() * @see MSDN: Observable.Using */ public static Observable using(Func0 resourceFactory, Func1> observableFactory) { @@ -5060,12 +5285,13 @@ public static Observable using(Func0 - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first * @return an Observable that reflects whichever of the given Observables * reacted first + * @see amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2) { @@ -5075,13 +5301,14 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first * @param o3 an Observable competing to react first * @return an Observable that reflects whichever of the given Observables * reacted first + * @see amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3) { @@ -5091,7 +5318,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5099,6 +5326,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4) { @@ -5108,7 +5336,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5117,6 +5345,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5) { @@ -5126,7 +5355,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5136,6 +5365,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6) { @@ -5145,7 +5375,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5156,6 +5386,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7) { @@ -5165,7 +5396,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5177,6 +5408,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8) { @@ -5186,7 +5418,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5199,6 +5431,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9) { @@ -5208,11 +5441,12 @@ public static Observable amb(Observable o1, Observable - * + * * * @param sources Observable sources competing to react first * @return an Observable that reflects whichever of the given Observables * reacted first + * @see amb() * @see MSDN: Observable.Amb */ public static Observable amb(Iterable> sources) { @@ -5223,11 +5457,12 @@ public static Observable amb(Iterable> /** * Invokes an action for each item emitted by the Observable. *

    - * + * * * @param observer the action to invoke for each item emitted in the source * sequence * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(Observer observer) { @@ -5237,11 +5472,12 @@ public Observable doOnEach(Observer observer) { /** * Invokes an action for each item emitted by an Observable. *

    - * + * * * @param onNext the action to invoke for each item in the source * sequence * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext) { @@ -5266,10 +5502,11 @@ public void onNext(T args) { /** * Invokes an action if onError is called from the Observable. *

    - * + * * * @param onError the action to invoke if onError is invoked * @return the source sequence with the side-effecting behavior applied + * @see doOnError() * @see MSDN: Observable.Do */ public Observable doOnError(final Action1 onError) { @@ -5295,11 +5532,12 @@ public void onNext(T args) { } * Invokes an action when onCompleted is called by the * Observable. *

    - * + * * * @param onCompleted the action to invoke when onCompleted is * called * @return the source sequence with the side-effecting behavior applied + * @see doOnCompleted() * @see MSDN: Observable.Do */ public Observable doOnCompleted(final Action0 onCompleted) { @@ -5328,6 +5566,7 @@ public void onNext(T args) { } * @param onError the action to invoke when the source Observable calls * onError * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext, final Action1 onError) { @@ -5351,7 +5590,6 @@ public void onNext(T args) { return create(OperationDoOnEach.doOnEach(this, observer)); } - /** * Invokes an action for each item emitted by an Observable. * @@ -5361,6 +5599,7 @@ public void onNext(T args) { * @param onCompleted the action to invoke when the source Observable calls * onCompleted * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext, final Action1 onError, final Action0 onCompleted) { From 1e7eabd9493f8bd8454c96cd98ebb756dee70074 Mon Sep 17 00:00:00 2001 From: akarnokd Date: Thu, 21 Nov 2013 15:16:51 +0100 Subject: [PATCH 283/333] Operators: And, Then, When Issue #23, Issue #88, Issue #100 --- rxjava-core/src/main/java/rx/Observable.java | 173 +++++++- .../src/main/java/rx/joins/ActivePlan0.java | 37 ++ .../src/main/java/rx/joins/ActivePlan1.java | 49 +++ .../src/main/java/rx/joins/ActivePlan2.java | 54 +++ .../src/main/java/rx/joins/ActivePlan3.java | 64 +++ .../src/main/java/rx/joins/JoinObserver.java | 26 ++ .../src/main/java/rx/joins/JoinObserver1.java | 107 +++++ .../src/main/java/rx/joins/ObserverBase.java | 72 ++++ .../src/main/java/rx/joins/Pattern.java | 25 ++ .../src/main/java/rx/joins/Pattern1.java | 45 +++ .../src/main/java/rx/joins/Pattern2.java | 54 +++ .../src/main/java/rx/joins/Pattern3.java | 55 +++ rxjava-core/src/main/java/rx/joins/Plan0.java | 46 +++ rxjava-core/src/main/java/rx/joins/Plan1.java | 80 ++++ rxjava-core/src/main/java/rx/joins/Plan2.java | 79 ++++ rxjava-core/src/main/java/rx/joins/Plan3.java | 83 ++++ .../rx/operators/OperationJoinPatterns.java | 131 ++++++ .../SingleAssignmentSubscription.java | 81 ++++ .../main/java/rx/util/functions/Actions.java | 72 ++++ .../java/rx/operators/OperationJoinsTest.java | 382 ++++++++++++++++++ 20 files changed, 1714 insertions(+), 1 deletion(-) create mode 100644 rxjava-core/src/main/java/rx/joins/ActivePlan0.java create mode 100644 rxjava-core/src/main/java/rx/joins/ActivePlan1.java create mode 100644 rxjava-core/src/main/java/rx/joins/ActivePlan2.java create mode 100644 rxjava-core/src/main/java/rx/joins/ActivePlan3.java create mode 100644 rxjava-core/src/main/java/rx/joins/JoinObserver.java create mode 100644 rxjava-core/src/main/java/rx/joins/JoinObserver1.java create mode 100644 rxjava-core/src/main/java/rx/joins/ObserverBase.java create mode 100644 rxjava-core/src/main/java/rx/joins/Pattern.java create mode 100644 rxjava-core/src/main/java/rx/joins/Pattern1.java create mode 100644 rxjava-core/src/main/java/rx/joins/Pattern2.java create mode 100644 rxjava-core/src/main/java/rx/joins/Pattern3.java create mode 100644 rxjava-core/src/main/java/rx/joins/Plan0.java create mode 100644 rxjava-core/src/main/java/rx/joins/Plan1.java create mode 100644 rxjava-core/src/main/java/rx/joins/Plan2.java create mode 100644 rxjava-core/src/main/java/rx/joins/Plan3.java create mode 100644 rxjava-core/src/main/java/rx/operators/OperationJoinPatterns.java create mode 100644 rxjava-core/src/main/java/rx/subscriptions/SingleAssignmentSubscription.java create mode 100644 rxjava-core/src/main/java/rx/util/functions/Actions.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationJoinsTest.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 53327b371d..23541eafad 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -26,6 +26,8 @@ import java.util.concurrent.TimeUnit; import rx.concurrency.Schedulers; +import rx.joins.Pattern2; +import rx.joins.Plan0; import rx.observables.BlockingObservable; import rx.observables.ConnectableObservable; import rx.observables.GroupedObservable; @@ -51,6 +53,7 @@ import rx.operators.OperationFirstOrDefault; import rx.operators.OperationGroupBy; import rx.operators.OperationInterval; +import rx.operators.OperationJoinPatterns; import rx.operators.OperationLast; import rx.operators.OperationMap; import rx.operators.OperationMaterialize; @@ -5662,5 +5665,173 @@ private boolean isInternalImplementation(Object o) { return isInternal; } } - + /** + * Creates a pattern that matches when both observable sequences have an available element. + * @param right Observable sequence to match with the left sequence. + * @return Pattern object that matches when both observable sequences have an available element. + * @see MSDN: Observable.And + * @throws NullPointerException if right is null + */ + public Pattern2 and(Observable right) { + return OperationJoinPatterns.and(this, right); + } + /** + * Matches when the observable sequence has an available element and projects the element by invoking the selector function. + * @param selector Selector that will be invoked for elements in the source sequence. + * @return Plan that produces the projected results, to be fed (with other plans) to the When operator. + * @see MSDN: Observable.Then + * @throws NullPointerException if selector is null + */ + public Plan0 then(Func1 selector) { + return OperationJoinPatterns.then(this, selector); + } + /** + * Joins together the results from several patterns. + * @param plans A series of plans created by use of the Then operator on patterns. + * @return An observable sequence with the results from matching several patterns. + * @see MSDN: Observable.When + * @throws NullPointerException if plans is null + */ + public static Observable when(Plan0... plans) { + return create(OperationJoinPatterns.when(plans)); + } + /** + * Joins together the results from several patterns. + * @param plans A series of plans created by use of the Then operator on patterns. + * @return An observable sequence with the results from matching several patterns. + * @see MSDN: Observable.When + * @throws NullPointerException if plans is null + */ + public static Observable when(Iterable> plans) { + if (plans == null) { + throw new NullPointerException("plans"); + } + return create(OperationJoinPatterns.when(plans)); + } + /** + * Joins the results from a pattern. + * @param p1 the plan to join + * @return An observable sequence with the results from matching a pattern + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1) { + return create(OperationJoinPatterns.when(p1)); + } + /** + * Joins together the results from several patterns. + * @param p1 a plan + * @param p2 a plan + * @return An observable sequence with the results from matching several patterns + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2) { + return create(OperationJoinPatterns.when(p1, p2)); + } + /** + * Joins together the results from several patterns. + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @return An observable sequence with the results from matching several patterns + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3) { + return create(OperationJoinPatterns.when(p1, p2, p3)); + } + /** + * Joins together the results from several patterns. + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @return An observable sequence with the results from matching several patterns + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4)); + } + /** + * Joins together the results from several patterns. + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @return An observable sequence with the results from matching several patterns + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5)); + } + /** + * Joins together the results from several patterns. + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @return An observable sequence with the results from matching several patterns + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6)); + } + /** + * Joins together the results from several patterns. + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @param p7 a plan + * @return An observable sequence with the results from matching several patterns + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7)); + } + /** + * Joins together the results from several patterns. + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @param p7 a plan + * @param p8 a plan + * @return An observable sequence with the results from matching several patterns + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8)); + } + /** + * Joins together the results from several patterns. + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @param p7 a plan + * @param p8 a plan + * @param p9 a plan + * @return An observable sequence with the results from matching several patterns + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8, Plan0 p9) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8, p9)); + } } diff --git a/rxjava-core/src/main/java/rx/joins/ActivePlan0.java b/rxjava-core/src/main/java/rx/joins/ActivePlan0.java new file mode 100644 index 0000000000..d8690136b7 --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/ActivePlan0.java @@ -0,0 +1,37 @@ +/** + * Copyright 2013 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.joins; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents an activated plan. + */ +public abstract class ActivePlan0 { + protected final Map joinObservers = new HashMap(); + + public abstract void match(); + + protected void addJoinObserver(JoinObserver joinObserver) { + joinObservers.put(joinObserver, joinObserver); + } + protected void dequeue() { + for (JoinObserver jo : joinObservers.values()) { + jo.dequeue(); + } + } +} diff --git a/rxjava-core/src/main/java/rx/joins/ActivePlan1.java b/rxjava-core/src/main/java/rx/joins/ActivePlan1.java new file mode 100644 index 0000000000..65e50e9908 --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/ActivePlan1.java @@ -0,0 +1,49 @@ +/** + * Copyright 2013 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.joins; + +import rx.Notification; +import rx.util.functions.Action0; +import rx.util.functions.Action1; + +/** + * Represents an active plan. + */ +public class ActivePlan1 extends ActivePlan0 { + private final Action1 onNext; + private final Action0 onCompleted; + private final JoinObserver1 first; + public ActivePlan1(JoinObserver1 first, Action1 onNext, Action0 onCompleted) { + this.onNext = onNext; + this.onCompleted = onCompleted; + this.first = first; + addJoinObserver(first); + } + + @Override + public void match() { + if (!first.queue().isEmpty()) { + Notification n1 = first.queue().peek(); + if (n1.isOnCompleted()) { + onCompleted.call(); + } else { + dequeue(); + onNext.call(n1.getValue()); + } + } + } + +} diff --git a/rxjava-core/src/main/java/rx/joins/ActivePlan2.java b/rxjava-core/src/main/java/rx/joins/ActivePlan2.java new file mode 100644 index 0000000000..3ae19714ce --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/ActivePlan2.java @@ -0,0 +1,54 @@ +/** + * Copyright 2013 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.joins; + +import rx.Notification; +import rx.util.functions.Action0; +import rx.util.functions.Action2; + +/** + * Represents an active plan. + */ +public class ActivePlan2 extends ActivePlan0 { + private final Action2 onNext; + private final Action0 onCompleted; + private final JoinObserver1 first; + private final JoinObserver1 second; + public ActivePlan2(JoinObserver1 first, JoinObserver1 second, Action2 onNext, Action0 onCompleted) { + this.onNext = onNext; + this.onCompleted = onCompleted; + this.first = first; + this.second = second; + addJoinObserver(first); + addJoinObserver(second); + } + + @Override + public void match() { + if (!first.queue().isEmpty() && !second.queue().isEmpty()) { + Notification n1 = first.queue().peek(); + Notification n2 = second.queue().peek(); + + if (n1.isOnCompleted() || n2.isOnCompleted()) { + onCompleted.call(); + } else { + dequeue(); + onNext.call(n1.getValue(), n2.getValue()); + } + } + } + +} diff --git a/rxjava-core/src/main/java/rx/joins/ActivePlan3.java b/rxjava-core/src/main/java/rx/joins/ActivePlan3.java new file mode 100644 index 0000000000..cff23dd752 --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/ActivePlan3.java @@ -0,0 +1,64 @@ +/** + * Copyright 2013 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.joins; + +import rx.Notification; +import rx.util.functions.Action0; +import rx.util.functions.Action3; + +/** + * Represents an active plan. + */ +public class ActivePlan3 extends ActivePlan0 { + private final Action3 onNext; + private final Action0 onCompleted; + private final JoinObserver1 first; + private final JoinObserver1 second; + private final JoinObserver1 third; + public ActivePlan3(JoinObserver1 first, + JoinObserver1 second, + JoinObserver1 third, + Action3 onNext, + Action0 onCompleted) { + this.onNext = onNext; + this.onCompleted = onCompleted; + this.first = first; + this.second = second; + this.third = third; + addJoinObserver(first); + addJoinObserver(second); + addJoinObserver(third); + } + + @Override + public void match() { + if (!first.queue().isEmpty() + && !second.queue().isEmpty() + && !third.queue().isEmpty()) { + Notification n1 = first.queue().peek(); + Notification n2 = second.queue().peek(); + Notification n3 = third.queue().peek(); + + if (n1.isOnCompleted() || n2.isOnCompleted() || n3.isOnCompleted()) { + onCompleted.call(); + } else { + dequeue(); + onNext.call(n1.getValue(), n2.getValue(), n3.getValue()); + } + } + } + +} diff --git a/rxjava-core/src/main/java/rx/joins/JoinObserver.java b/rxjava-core/src/main/java/rx/joins/JoinObserver.java new file mode 100644 index 0000000000..d191ed8958 --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/JoinObserver.java @@ -0,0 +1,26 @@ +/** + * Copyright 2013 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.joins; + +import rx.Subscription; + +/** + * Base interface to manage joined observations. + */ +public interface JoinObserver extends Subscription { + void subscribe(Object gate); + void dequeue(); +} diff --git a/rxjava-core/src/main/java/rx/joins/JoinObserver1.java b/rxjava-core/src/main/java/rx/joins/JoinObserver1.java new file mode 100644 index 0000000000..873d3d1a7f --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/JoinObserver1.java @@ -0,0 +1,107 @@ +/** + * Copyright 2013 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.joins; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import rx.Notification; +import rx.Observable; +import rx.subscriptions.SingleAssignmentSubscription; +import rx.util.functions.Action1; + +/** + * Default implementation of a join observer. + */ +public final class JoinObserver1 extends ObserverBase> implements JoinObserver { + private Object gate; + private final Observable source; + private final Action1 onError; + private final List activePlans; + private final Queue> queue; + private final SingleAssignmentSubscription subscription; + private volatile boolean done; + + public JoinObserver1(Observable source, Action1 onError) { + this.source = source; + this.onError = onError; + queue = new LinkedList>(); + subscription = new SingleAssignmentSubscription(); + activePlans = new ArrayList(); + } + public Queue> queue() { + return queue; + } + public void addActivePlan(ActivePlan0 activePlan) { + activePlans.add(activePlan); + } + @Override + public void subscribe(Object gate) { + this.gate = gate; + subscription.set(source.materialize().subscribe(this)); + } + + @Override + public void dequeue() { + queue.remove(); + } + + @Override + protected void onNextCore(Notification args) { + synchronized (gate) { + if (!done) { + if (args.isOnError()) { + onError.call(args.getThrowable()); + return; + } + queue.add(args); + + // remark: activePlans might change while iterating + for (ActivePlan0 a : new ArrayList(activePlans)) { + a.match(); + } + } + } + } + + @Override + protected void onErrorCore(Throwable e) { + // not expected + } + + @Override + protected void onCompletedCore() { + // not expected or ignored + } + + + void removeActivePlan(ActivePlan0 activePlan) { + activePlans.remove(activePlan); + if (activePlans.isEmpty()) { + unsubscribe(); + } + } + + @Override + public void unsubscribe() { + if (!done) { + done = true; + subscription.unsubscribe(); + } + } + +} diff --git a/rxjava-core/src/main/java/rx/joins/ObserverBase.java b/rxjava-core/src/main/java/rx/joins/ObserverBase.java new file mode 100644 index 0000000000..f1144a8ad2 --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/ObserverBase.java @@ -0,0 +1,72 @@ +/** + * Copyright 2013 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.joins; + +import java.util.concurrent.atomic.AtomicBoolean; +import rx.Observer; + +/** + * Implements an observer that ensures proper event delivery + * semantics to its abstract onXxxxCore methods. + */ +public abstract class ObserverBase implements Observer { + private final AtomicBoolean completed = new AtomicBoolean(); + + @Override + public void onNext(T args) { + if (!completed.get()) { + onNextCore(args); + } + } + + @Override + public void onError(Throwable e) { + if (completed.compareAndSet(false, true)) { + onErrorCore(e); + } + } + + @Override + public void onCompleted() { + if (completed.compareAndSet(false, true)) { + onCompletedCore(); + } + } + /** + * Implement this method to react to the receival of a new element in the sequence. + */ + protected abstract void onNextCore(T args); + /** + * Implement this method to react to the occurrence of an exception. + */ + protected abstract void onErrorCore(Throwable e); + /** + * Implement this method to react to the end of the sequence. + */ + protected abstract void onCompletedCore(); + /** + * Try to trigger the error state. + * @param t + * @return false if already completed + */ + protected boolean fail(Throwable t) { + if (completed.compareAndSet(false, true)) { + onErrorCore(t); + return true; + } + return false; + } +} diff --git a/rxjava-core/src/main/java/rx/joins/Pattern.java b/rxjava-core/src/main/java/rx/joins/Pattern.java new file mode 100644 index 0000000000..1dc65ca3b8 --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/Pattern.java @@ -0,0 +1,25 @@ +/** + * Copyright 2013 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.joins; + +/** + * Base interface for join patterns. + * @see MSDN: Pattern + */ +public interface Pattern { + +} diff --git a/rxjava-core/src/main/java/rx/joins/Pattern1.java b/rxjava-core/src/main/java/rx/joins/Pattern1.java new file mode 100644 index 0000000000..324daf79fa --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/Pattern1.java @@ -0,0 +1,45 @@ +/** + * Copyright 2013 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.joins; + +import rx.Observable; +import rx.util.functions.Func1; + +/** + * Represents a join pattern over one observable sequence. + */ +public class Pattern1 implements Pattern { + private final Observable first; + public Pattern1(Observable first) { + this.first = first; + } + public Observable first() { + return first; + } + /** + * Matches when all observable sequences have an available + * element and projects the elements by invoking the selector function. + * @param selector the function that will be invoked for elements in the source sequences. + * @return + * @throws NullPointerException if selector is null + */ + public Plan0 then(Func1 selector) { + if (selector == null) { + throw new NullPointerException(); + } + return new Plan1(this, selector); + } +} diff --git a/rxjava-core/src/main/java/rx/joins/Pattern2.java b/rxjava-core/src/main/java/rx/joins/Pattern2.java new file mode 100644 index 0000000000..6f82a66d49 --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/Pattern2.java @@ -0,0 +1,54 @@ +/** + * Copyright 2013 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.joins; + +import rx.Observable; +import rx.util.functions.Func2; + +/** + * Represents a join pattern over observable sequences. + */ +public class Pattern2 implements Pattern { + private final Observable first; + private final Observable second; + public Pattern2(Observable first, Observable second) { + this.first = first; + this.second = second; + } + public Observable first() { + return first; + } + public Observable second() { + return second; + } + /** + * Creates a pattern that matches when all three observable sequences have an available element. + * @param other Observable sequence to match with the two previous sequences. + * @return Pattern object that matches when all observable sequences have an available element. + */ + public Pattern3 and(Observable other) { + if (other == null) { + throw new NullPointerException(); + } + return new Pattern3(first, second, other); + } + public Plan0 then(Func2 selector) { + if (selector == null) { + throw new NullPointerException(); + } + return new Plan2(this, selector); + } +} diff --git a/rxjava-core/src/main/java/rx/joins/Pattern3.java b/rxjava-core/src/main/java/rx/joins/Pattern3.java new file mode 100644 index 0000000000..c43b9d0b9e --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/Pattern3.java @@ -0,0 +1,55 @@ +/** + * Copyright 2013 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.joins; + +import rx.Observable; +import rx.util.functions.Func3; + +/** + * Represents a join pattern over observable sequences. + */ +public class Pattern3 implements Pattern { + private final Observable first; + private final Observable second; + private final Observable third; + public Pattern3(Observable first, Observable second, + Observable third) { + this.first = first; + this.second = second; + this.third = third; + } + public Observable first() { + return first; + } + public Observable second() { + return second; + } + public Observable third() { + return third; + } +// public Pattern4 and(Observable other) { +// if (other == null) { +// throw new NullPointerException(); +// } +// return new Pattern4(first, second, third, other); +// } + public Plan0 then(Func3 selector) { + if (selector == null) { + throw new NullPointerException(); + } + return new Plan3(this, selector); + } +} diff --git a/rxjava-core/src/main/java/rx/joins/Plan0.java b/rxjava-core/src/main/java/rx/joins/Plan0.java new file mode 100644 index 0000000000..2a647aff3d --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/Plan0.java @@ -0,0 +1,46 @@ +/** + * Copyright 2013 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.joins; + +import java.util.Map; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Action1; + +/** + * Represents an execution plan for join patterns. + */ +public abstract class Plan0 { + public abstract ActivePlan0 activate(Map externalSubscriptions, + Observer observer, Action1 deactivate); + + @SuppressWarnings("unchecked") + public static JoinObserver1 createObserver( + Map externalSubscriptions, + Observable observable, + Action1 onError + ) { + JoinObserver1 observer; + JoinObserver nonGeneric = externalSubscriptions.get(observable); + if (nonGeneric == null) { + observer = new JoinObserver1(observable, onError); + externalSubscriptions.put(observable, observer); + } else { + observer = (JoinObserver1)nonGeneric; + } + return observer; + } +} diff --git a/rxjava-core/src/main/java/rx/joins/Plan1.java b/rxjava-core/src/main/java/rx/joins/Plan1.java new file mode 100644 index 0000000000..b64742e27b --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/Plan1.java @@ -0,0 +1,80 @@ +/** + * Copyright 2013 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.joins; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import rx.Observer; +import rx.util.functions.Action0; +import rx.util.functions.Action1; +import rx.util.functions.Func1; +import rx.util.functions.Actions; + +/** + * Represents an execution plan for join patterns. + */ +public class Plan1 extends Plan0 { + protected Pattern1 expression; + protected Func1 selector; + + public Plan1(Pattern1 expression, Func1 selector) { + this.expression = expression; + this.selector = selector; + } + + public Pattern1 expression() { + return expression; + } + public Func1 selector() { + return selector; + } + + @Override + public ActivePlan0 activate(Map externalSubscriptions, final Observer observer, final Action1 deactivate) { + Action1 onError = Actions.onErrorFrom(observer); + + final JoinObserver1 firstJoinObserver = createObserver(externalSubscriptions, expression.first(), onError); + + final AtomicReference> self = new AtomicReference>(); + + ActivePlan1 activePlan = new ActivePlan1(firstJoinObserver, new Action1() { + @Override + public void call(T1 t1) { + R result; + try { + result = selector.call(t1); + } catch (Throwable t) { + observer.onError(t); + return; + } + observer.onNext(result); + } + }, + new Action0() { + @Override + public void call() { + firstJoinObserver.removeActivePlan(self.get()); + deactivate.call(self.get()); + } + }); + + self.set(activePlan); + + firstJoinObserver.addActivePlan(activePlan); + return activePlan; + } + +} diff --git a/rxjava-core/src/main/java/rx/joins/Plan2.java b/rxjava-core/src/main/java/rx/joins/Plan2.java new file mode 100644 index 0000000000..71b931ea0d --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/Plan2.java @@ -0,0 +1,79 @@ +/** + * Copyright 2013 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.joins; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import rx.Observer; +import static rx.joins.Plan0.createObserver; +import rx.util.functions.Action0; +import rx.util.functions.Action1; +import rx.util.functions.Action2; +import rx.util.functions.Actions; +import rx.util.functions.Func2; + +/** + * Represents an execution plan for join patterns. + */ +public class Plan2 extends Plan0 { + protected Pattern2 expression; + protected Func2 selector; + public Plan2(Pattern2 expression, Func2 selector) { + this.expression = expression; + this.selector = selector; + } + + @Override + public ActivePlan0 activate(Map externalSubscriptions, + final Observer observer, final Action1 deactivate) { + Action1 onError = Actions.onErrorFrom(observer); + + final JoinObserver1 firstJoinObserver = createObserver(externalSubscriptions, expression.first(), onError); + final JoinObserver1 secondJoinObserver = createObserver(externalSubscriptions, expression.second(), onError); + + final AtomicReference> self = new AtomicReference>(); + + ActivePlan2 activePlan = new ActivePlan2(firstJoinObserver, secondJoinObserver, new Action2() { + @Override + public void call(T1 t1, T2 t2) { + R result; + try { + result = selector.call(t1, t2); + } catch (Throwable t) { + observer.onError(t); + return; + } + observer.onNext(result); + } + }, + new Action0() { + @Override + public void call() { + firstJoinObserver.removeActivePlan(self.get()); + secondJoinObserver.removeActivePlan(self.get()); + deactivate.call(self.get()); + } + }); + + self.set(activePlan); + + firstJoinObserver.addActivePlan(activePlan); + secondJoinObserver.addActivePlan(activePlan); + + return activePlan; + } + +} diff --git a/rxjava-core/src/main/java/rx/joins/Plan3.java b/rxjava-core/src/main/java/rx/joins/Plan3.java new file mode 100644 index 0000000000..386ef82dfe --- /dev/null +++ b/rxjava-core/src/main/java/rx/joins/Plan3.java @@ -0,0 +1,83 @@ +/** + * Copyright 2013 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.joins; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import rx.Observer; +import static rx.joins.Plan0.createObserver; +import rx.util.functions.Action0; +import rx.util.functions.Action1; +import rx.util.functions.Action3; +import rx.util.functions.Actions; +import rx.util.functions.Func3; + +/** + * Represents an execution plan for join patterns. + */ +public class Plan3 extends Plan0 { + protected Pattern3 expression; + protected Func3 selector; + public Plan3(Pattern3 expression, Func3 selector) { + this.expression = expression; + this.selector = selector; + } + + @Override + public ActivePlan0 activate(Map externalSubscriptions, + final Observer observer, final Action1 deactivate) { + Action1 onError = Actions.onErrorFrom(observer); + + final JoinObserver1 firstJoinObserver = createObserver(externalSubscriptions, expression.first(), onError); + final JoinObserver1 secondJoinObserver = createObserver(externalSubscriptions, expression.second(), onError); + final JoinObserver1 thirdJoinObserver = createObserver(externalSubscriptions, expression.third(), onError); + + final AtomicReference> self = new AtomicReference>(); + + ActivePlan3 activePlan = new ActivePlan3(firstJoinObserver, secondJoinObserver, + thirdJoinObserver, new Action3() { + @Override + public void call(T1 t1, T2 t2, T3 t3) { + R result; + try { + result = selector.call(t1, t2, t3); + } catch (Throwable t) { + observer.onError(t); + return; + } + observer.onNext(result); + } + }, + new Action0() { + @Override + public void call() { + firstJoinObserver.removeActivePlan(self.get()); + secondJoinObserver.removeActivePlan(self.get()); + thirdJoinObserver.removeActivePlan(self.get()); + deactivate.call(self.get()); + } + }); + + self.set(activePlan); + + firstJoinObserver.addActivePlan(activePlan); + secondJoinObserver.addActivePlan(activePlan); + thirdJoinObserver.addActivePlan(activePlan); + + return activePlan; + } + +} diff --git a/rxjava-core/src/main/java/rx/operators/OperationJoinPatterns.java b/rxjava-core/src/main/java/rx/operators/OperationJoinPatterns.java new file mode 100644 index 0000000000..6ff1c4346e --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationJoinPatterns.java @@ -0,0 +1,131 @@ + /** + * Copyright 2013 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.operators; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.joins.ActivePlan0; +import rx.joins.JoinObserver; +import rx.joins.Pattern1; +import rx.joins.Pattern2; +import rx.joins.Plan0; +import rx.subjects.PublishSubject; +import rx.subscriptions.CompositeSubscription; +import rx.util.functions.Action1; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +/** + * Join patterns: And, Then, When. + */ +public class OperationJoinPatterns { + /** + * Creates a pattern that matches when both observable sequences have an available element. + */ + public static Pattern2 and(/* this */Observable left, Observable right) { + if (left == null) { + throw new NullPointerException("left"); + } + if (right == null) { + throw new NullPointerException("right"); + } + return new Pattern2(left, right); + } + /** + * Matches when the observable sequence has an available element and projects the element by invoking the selector function. + */ + public static Plan0 then(/* this */Observable source, Func1 selector) { + if (source == null) { + throw new NullPointerException("source"); + } + if (selector == null) { + throw new NullPointerException("selector"); + } + return new Pattern1(source).then(selector); + } + /** + * Joins together the results from several patterns. + */ + public static OnSubscribeFunc when(Plan0... plans) { + if (plans == null) { + throw new NullPointerException("plans"); + } + return when(Arrays.asList(plans)); + } + /** + * Joins together the results from several patterns. + */ + public static OnSubscribeFunc when(final Iterable> plans) { + if (plans == null) { + throw new NullPointerException("plans"); + } + return new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(final Observer t1) { + final Map externalSubscriptions = new HashMap(); + final Object gate = new Object(); + final List activePlans = new ArrayList(); + + final Observer out = new Observer() { + @Override + public void onNext(R args) { + t1.onNext(args); + } + @Override + public void onError(Throwable e) { + for (JoinObserver po : externalSubscriptions.values()) { + po.unsubscribe(); + } + t1.onError(e); + } + @Override + public void onCompleted() { + t1.onCompleted(); + } + }; + + try { + for (Plan0 plan : plans) { + activePlans.add(plan.activate(externalSubscriptions, out, new Action1() { + @Override + public void call(ActivePlan0 activePlan) { + activePlans.remove(activePlan); + if (activePlans.isEmpty()) { + out.onCompleted(); + } + } + })); + } + } catch (Throwable t) { + return Observable.error(t).subscribe(t1); + } + CompositeSubscription group = new CompositeSubscription(); + for (JoinObserver jo : externalSubscriptions.values()) { + jo.subscribe(gate); + group.add(jo); + } + return group; + } + }; + } +} diff --git a/rxjava-core/src/main/java/rx/subscriptions/SingleAssignmentSubscription.java b/rxjava-core/src/main/java/rx/subscriptions/SingleAssignmentSubscription.java new file mode 100644 index 0000000000..492c4e6dbf --- /dev/null +++ b/rxjava-core/src/main/java/rx/subscriptions/SingleAssignmentSubscription.java @@ -0,0 +1,81 @@ +/** + * Copyright 2013 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.subscriptions; + +import java.util.concurrent.atomic.AtomicReference; +import rx.Subscription; + +/** + * A subscription that allows only a single resource to be assigned. + *

    + * If this subscription is live, no other subscription may be set() and + * yields an {@link IllegalStateException}. + *

    + * If the unsubscribe has been called, setting a new subscription will + * unsubscribe it immediately. + */ +public final class SingleAssignmentSubscription implements Subscription { + /** Holds the current resource. */ + private final AtomicReference current = new AtomicReference(); + /** Sentinel for the unsubscribed state. */ + private static final Subscription SENTINEL = new Subscription() { + @Override + public void unsubscribe() { + } + }; + /** + * Returns the current subscription or null if not yet set. + */ + public Subscription get() { + Subscription s = current.get(); + if (s == SENTINEL) { + return Subscriptions.empty(); + } + return s; + } + /** + * Sets a new subscription if not already set. + * @param s the new subscription + * @throws IllegalStateException if this subscription is live and contains + * another subscription. + */ + public void set(Subscription s) { + if (current.compareAndSet(null, s)) { + return; + } + if (current.get() != SENTINEL) { + throw new IllegalStateException("Subscription already set"); + } + if (s != null) { + s.unsubscribe(); + } + } + @Override + public void unsubscribe() { + Subscription old = current.getAndSet(SENTINEL); + if (old != null) { + old.unsubscribe(); + } + } + /** + * Test if this subscription is already unsubscribed. + */ + public boolean isUnsubscribed() { + return current.get() == SENTINEL; + } + +} diff --git a/rxjava-core/src/main/java/rx/util/functions/Actions.java b/rxjava-core/src/main/java/rx/util/functions/Actions.java new file mode 100644 index 0000000000..2bffbb9ec8 --- /dev/null +++ b/rxjava-core/src/main/java/rx/util/functions/Actions.java @@ -0,0 +1,72 @@ +/** + * Copyright 2013 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.util.functions; + +import rx.Observer; +import rx.util.functions.Action0; +import rx.util.functions.Action1; + +/** + * Utility class for the Action interfaces. + */ +public final class Actions { + private Actions() { throw new IllegalStateException("No instances!"); } + /** + * Extracts a method reference to the observer's onNext method + * in the form of an Action1. + *

    Java 8: observer::onNext

    + * @param observer the observer to use + * @return an action which calls the observer's onNext method. + */ + public static Action1 onNextFrom(final Observer observer) { + return new Action1() { + @Override + public void call(T t1) { + observer.onNext(t1); + } + }; + } + /** + * Extracts a method reference to the observer's onError method + * in the form of an Action1. + *

    Java 8: observer::onError

    + * @param observer the observer to use + * @return an action which calls the observer's onError method. + */ + public static Action1 onErrorFrom(final Observer observer) { + return new Action1() { + @Override + public void call(Throwable t1) { + observer.onError(t1); + } + }; + } + /** + * Extracts a method reference to the observer's onCompleted method + * in the form of an Action0. + *

    Java 8: observer::onCompleted

    + * @param observer the observer to use + * @return an action which calls the observer's onCompleted method. + */ + public static Action0 onCompletedFrom(final Observer observer) { + return new Action0() { + @Override + public void call() { + observer.onCompleted(); + } + }; + } +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationJoinsTest.java b/rxjava-core/src/test/java/rx/operators/OperationJoinsTest.java new file mode 100644 index 0000000000..8a3b0c79c9 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationJoinsTest.java @@ -0,0 +1,382 @@ +/** + * Copyright 2013 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.operators; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import static org.mockito.Matchers.any; +import org.mockito.Mock; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import org.mockito.MockitoAnnotations; +import rx.Observable; +import rx.Observer; +import rx.joins.Plan0; +import rx.subjects.PublishSubject; +import rx.util.functions.Func1; +import rx.util.functions.Func2; +import rx.util.functions.Func3; +import rx.util.functions.Functions; + +public class OperationJoinsTest { + @Mock + Observer observer; + + + Func2 add2 = new Func2() { + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + }; + Func2 mul2 = new Func2() { + @Override + public Integer call(Integer t1, Integer t2) { + return t1 * t2; + } + }; + Func2 sub2 = new Func2() { + @Override + public Integer call(Integer t1, Integer t2) { + return t1 - t2; + } + }; + + Func3 add3 = new Func3() { + @Override + public Integer call(Integer t1, Integer t2, Integer t3) { + return t1 + t2 + t3; + } + }; + Func1 func1Throw = new Func1() { + @Override + public Integer call(Integer t1) { + throw new RuntimeException("Forced failure"); + } + }; + Func2 func2Throw = new Func2() { + @Override + public Integer call(Integer t1, Integer t2) { + throw new RuntimeException("Forced failure"); + } + }; + Func3 func3Throw = new Func3() { + @Override + public Integer call(Integer t1, Integer t2, Integer t3) { + throw new RuntimeException("Forced failure"); + } + }; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + + @Test(expected = NullPointerException.class) + public void and2ArgumentNull() { + Observable some = Observable.just(1); + some.and(null); + } + @Test(expected = NullPointerException.class) + public void and3argumentNull() { + Observable some = Observable.just(1); + some.and(some).and(null); + } + @Test + public void and2() { + Observable some = Observable.just(1); + + Observable m = Observable.when(some.and(some).then(add2)); + + m.subscribe(observer); + + verify(observer, never()).onError(any(Throwable.class)); + verify(observer, times(1)).onNext(2); + verify(observer, times(1)).onCompleted(); + } + @Test + public void and2Error1() { + Observable error = Observable.error(new RuntimeException("Forced failure")); + + Observable some = Observable.just(1); + + Observable m = Observable.when(error.and(some).then(add2)); + + m.subscribe(observer); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, never()).onCompleted(); + } + @Test + public void and2Error2() { + Observable error = Observable.error(new RuntimeException("Forced failure")); + + Observable some = Observable.just(1); + + Observable m = Observable.when(some.and(error).then(add2)); + + m.subscribe(observer); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, never()).onCompleted(); + } + @Test + public void and3() { + Observable some = Observable.just(1); + + Observable m = Observable.when(some.and(some).and(some).then(add3)); + + m.subscribe(observer); + + verify(observer, never()).onError(any(Throwable.class)); + verify(observer, times(1)).onNext(3); + verify(observer, times(1)).onCompleted(); + } + @Test + public void and3Error1() { + Observable error = Observable.error(new RuntimeException("Forced failure")); + + Observable some = Observable.just(1); + + Observable m = Observable.when(error.and(some).and(some).then(add3)); + + m.subscribe(observer); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, never()).onCompleted(); + } + @Test + public void and3Error2() { + Observable error = Observable.error(new RuntimeException("Forced failure")); + + Observable some = Observable.just(1); + + Observable m = Observable.when(some.and(error).and(some).then(add3)); + + m.subscribe(observer); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, never()).onCompleted(); + } + @Test + public void and3Error3() { + Observable error = Observable.error(new RuntimeException("Forced failure")); + + Observable some = Observable.just(1); + + Observable m = Observable.when(some.and(some).and(error).then(add3)); + + m.subscribe(observer); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, never()).onCompleted(); + } + @Test(expected = NullPointerException.class) + public void thenArgumentNull() { + Observable some = Observable.just(1); + + some.then(null); + } + @Test(expected = NullPointerException.class) + public void then2ArgumentNull() { + Observable some = Observable.just(1); + + some.and(some).then(null); + } + @Test(expected = NullPointerException.class) + public void then3ArgumentNull() { + Observable some = Observable.just(1); + + some.and(some).and(some).then(null); + } + @Test + public void then1() { + Observable some = Observable.just(1); + + Observable m = Observable.when(some.then(Functions.identity())); + m.subscribe(observer); + + verify(observer, never()).onError(any(Throwable.class)); + verify(observer, times(1)).onNext(1); + verify(observer, times(1)).onCompleted(); + } + @Test + public void then1Error() { + Observable some = Observable.error(new RuntimeException("Forced failure")); + + Observable m = Observable.when(some.then(Functions.identity())); + m.subscribe(observer); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, never()).onCompleted(); + } + @Test + public void then1Throws() { + Observable some = Observable.just(1); + + Observable m = Observable.when(some.then(func1Throw)); + m.subscribe(observer); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, never()).onCompleted(); + } + @Test + public void then2Throws() { + Observable some = Observable.just(1); + + Observable m = Observable.when(some.and(some).then(func2Throw)); + m.subscribe(observer); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, never()).onCompleted(); + } + @Test + public void then3Throws() { + Observable some = Observable.just(1); + + Observable m = Observable.when(some.and(some).and(some).then(func3Throw)); + m.subscribe(observer); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, never()).onCompleted(); + } + @Test(expected = NullPointerException.class) + public void whenArgumentNull1() { + Observable.when((Plan0[])null); + } + @Test(expected = NullPointerException.class) + public void whenArgumentNull2() { + Observable.when((Iterable>)null); + } + @Test + public void whenMultipleSymmetric() { + Observable source1 = Observable.from(1, 2, 3); + Observable source2 = Observable.from(4, 5, 6); + + Observable m = Observable.when(source1.and(source2).then(add2)); + m.subscribe(observer); + + verify(observer, never()).onError(any(Throwable.class)); + verify(observer, times(1)).onNext(1 + 4); + verify(observer, times(1)).onNext(2 + 5); + verify(observer, times(1)).onNext(3 + 6); + verify(observer, times(1)).onCompleted(); + } + + @Test + public void whenMultipleAsymSymmetric() { + Observable source1 = Observable.from(1, 2, 3); + Observable source2 = Observable.from(4, 5); + + Observable m = Observable.when(source1.and(source2).then(add2)); + m.subscribe(observer); + + verify(observer, never()).onError(any(Throwable.class)); + verify(observer, times(1)).onNext(1 + 4); + verify(observer, times(1)).onNext(2 + 5); + verify(observer, times(1)).onCompleted(); + } + @Test + public void whenEmptyEmpty() { + Observable source1 = Observable.empty(); + Observable source2 = Observable.empty(); + + Observable m = Observable.when(source1.and(source2).then(add2)); + m.subscribe(observer); + + verify(observer, never()).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, times(1)).onCompleted(); + } + + @Test + public void whenNeverNever() { + Observable source1 = Observable.never(); + Observable source2 = Observable.never(); + + Observable m = Observable.when(source1.and(source2).then(add2)); + m.subscribe(observer); + + verify(observer, never()).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, never()).onCompleted(); + } + @Test + public void whenThrowNonEmpty() { + Observable source1 = Observable.empty(); + Observable source2 = Observable.error(new RuntimeException("Forced failure")); + + Observable m = Observable.when(source1.and(source2).then(add2)); + m.subscribe(observer); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onNext(any()); + verify(observer, never()).onCompleted(); + } + @Test + public void whenComplicated() { + PublishSubject xs = PublishSubject.create(); + PublishSubject ys = PublishSubject.create(); + PublishSubject zs = PublishSubject.create(); + + Observable m = Observable.when( + xs.and(ys).then(add2), + xs.and(zs).then(mul2), + ys.and(zs).then(sub2) + ); + m.subscribe(observer); + + xs.onNext(1); // t == 210 + + xs.onNext(2); // t == 220 + zs.onNext(7); // t == 220 + + xs.onNext(3); // t == 230 + zs.onNext(8); // t == 230 + + ys.onNext(4); // t == 240 + zs.onNext(9); // t == 240 + xs.onCompleted(); // t == 240 + + ys.onNext(5); // t == 250 + + ys.onNext(6); // t == 260 + + ys.onCompleted(); // t == 270 + + zs.onCompleted(); // t == 300 + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(1 * 7); + inOrder.verify(observer, times(1)).onNext(2 * 8); + inOrder.verify(observer, times(1)).onNext(3 + 4); + inOrder.verify(observer, times(1)).onNext(5 - 9); + inOrder.verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } +} From e25e159cdd0f1dda8412960b7d5da030bfafb19b Mon Sep 17 00:00:00 2001 From: DavidMGross Date: Thu, 21 Nov 2013 12:06:49 -0800 Subject: [PATCH 284/333] Update Observable.java Correcting for overzealous search-and-replace in javadoc comments --- rxjava-core/src/main/java/rx/Observable.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 53327b371d..4b80d4e049 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -133,7 +133,7 @@ * *

    * For more information see the - * RxJava Wiki + * RxJava Wiki * * @param the type of the item emitted by the Observable */ @@ -195,7 +195,7 @@ protected Observable(OnSubscribeFunc onSubscribe) { * in which multiple Observers will receive their notifications. *

    * For more information see the - * RxJava Wiki + * RxJava Wiki * * @param observer the Observer * @return a {@link Subscription} reference with which the {@link Observer} @@ -276,7 +276,7 @@ public Subscription subscribe(Observer observer) { * Observers will receive their notifications. *

    * For more information see the - * RxJava Wiki + * RxJava Wiki * * @param observer the Observer * @param scheduler the {@link Scheduler} on which Observers subscribe to From 7fcf5a999beb953e9f95e328d413e3a20fd5952b Mon Sep 17 00:00:00 2001 From: akarnokd Date: Thu, 21 Nov 2013 22:31:52 +0100 Subject: [PATCH 285/333] Operation: Join Issue #56 --- rxjava-core/src/main/java/rx/Observable.java | 23 +- .../main/java/rx/operators/OperationJoin.java | 277 ++++++++++++++++ .../java/rx/operators/OperationJoinTest.java | 310 ++++++++++++++++++ 3 files changed, 609 insertions(+), 1 deletion(-) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationJoin.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationJoinTest.java diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 53327b371d..9f5f399ed0 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -51,6 +51,7 @@ import rx.operators.OperationFirstOrDefault; import rx.operators.OperationGroupBy; import rx.operators.OperationInterval; +import rx.operators.OperationJoin; import rx.operators.OperationLast; import rx.operators.OperationMap; import rx.operators.OperationMaterialize; @@ -5662,5 +5663,25 @@ private boolean isInternalImplementation(Object o) { return isInternal; } } - + /** + * Correlates the elements of two sequences based on overlapping durations. + * @param right The right observable sequence to join elements for. + * @param leftDurationSelector A function to select the duration of each + * element of this observable sequence, used to + * determine overlap. + * @param rightDurationSelector A function to select the duration of each + * element of the right observable sequence, + * used to determine overlap. + * @param resultSelector A function invoked to compute a result element + * for any two overlapping elements of the left and + * right observable sequences. + * @return An observable sequence that contains result elements computed + * from source elements that have an overlapping duration. + * @see MSDN: Observable.Join + */ + public Observable join(Observable right, Func1> leftDurationSelector, + Func1> rightDurationSelector, + Func2 resultSelector) { + return create(new OperationJoin(this, right, leftDurationSelector, rightDurationSelector, resultSelector)); + } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationJoin.java b/rxjava-core/src/main/java/rx/operators/OperationJoin.java new file mode 100644 index 0000000000..b75b8498b0 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationJoin.java @@ -0,0 +1,277 @@ +/** + * Copyright 2013 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.operators; + +import java.util.HashMap; +import java.util.Map; +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.SerialSubscription; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +/** + * Correlates the elements of two sequences based on overlapping durations. + */ +public class OperationJoin implements OnSubscribeFunc { + final Observable left; + final Observable right; + final Func1> leftDurationSelector; + final Func1> rightDurationSelector; + final Func2 resultSelector; + public OperationJoin( + Observable left, + Observable right, + Func1> leftDurationSelector, + Func1> rightDurationSelector, + Func2 resultSelector) { + this.left = left; + this.right = right; + this.leftDurationSelector = leftDurationSelector; + this.rightDurationSelector = rightDurationSelector; + this.resultSelector = resultSelector; + } + + @Override + public Subscription onSubscribe(Observer t1) { + SerialSubscription cancel = new SerialSubscription(); + ResultSink result = new ResultSink(t1, cancel); + cancel.setSubscription(result.run()); + return cancel; + } + /** Manage the left and right sources. */ + class ResultSink { + final Object gate = new Object(); + final CompositeSubscription group = new CompositeSubscription(); + boolean leftDone; + int leftId; + final Map leftMap = new HashMap(); + boolean rightDone; + int rightId; + final Map rightMap = new HashMap(); + final Observer observer; + final Subscription cancel; + public ResultSink(Observer observer, Subscription cancel) { + this.observer = observer; + this.cancel = cancel; + } + public Subscription run() { + SerialSubscription leftCancel = new SerialSubscription(); + SerialSubscription rightCancel = new SerialSubscription(); + + group.add(leftCancel); + group.add(rightCancel); + + leftCancel.setSubscription(left.subscribe(new LeftObserver(leftCancel))); + rightCancel.setSubscription(right.subscribe(new RightObserver(rightCancel))); + + return group; + } + /** Observes the left values. */ + class LeftObserver implements Observer { + final Subscription self; + public LeftObserver(Subscription self) { + this.self = self; + } + protected void expire(int id, Subscription resource) { + synchronized (gate) { + if (leftMap.remove(id) != null && leftMap.isEmpty() && leftDone) { + observer.onCompleted(); + cancel.unsubscribe(); + } + } + group.remove(resource); + } + @Override + public void onNext(TLeft args) { + int id; + synchronized (gate) { + id = leftId++; + leftMap.put(id, args); + } + SerialSubscription md = new SerialSubscription(); + group.add(md); + + Observable duration; + try { + duration = leftDurationSelector.call(args); + } catch (Throwable t) { + observer.onError(t); + cancel.unsubscribe(); + return; + } + + md.setSubscription(duration.subscribe(new LeftDurationObserver(id, md))); + + synchronized (gate) { + for (TRight r : rightMap.values()) { + R result; + try { + result = resultSelector.call(args, r); + } catch (Throwable t) { + observer.onError(t); + cancel.unsubscribe(); + return; + } + observer.onNext(result); + } + } + } + @Override + public void onError(Throwable e) { + synchronized (gate) { + observer.onError(e); + cancel.unsubscribe(); + } + } + @Override + public void onCompleted() { + synchronized (gate) { + leftDone = true; + if (rightDone || leftMap.isEmpty()) { + observer.onCompleted(); + cancel.unsubscribe(); + } else { + self.unsubscribe(); + } + } + } + /** Observes the left duration. */ + class LeftDurationObserver implements Observer { + final int id; + final Subscription handle; + public LeftDurationObserver(int id, Subscription handle) { + this.id = id; + this.handle = handle; + } + + @Override + public void onNext(TLeftDuration args) { + expire(id, handle); + } + + @Override + public void onError(Throwable e) { + LeftObserver.this.onError(e); + } + + @Override + public void onCompleted() { + expire(id, handle); + } + + } + } + /** Observes the right values. */ + class RightObserver implements Observer { + final Subscription self; + public RightObserver(Subscription self) { + this.self = self; + } + void expire(int id, Subscription resource) { + synchronized (gate) { + if (rightMap.remove(id) != null && rightMap.isEmpty() && rightDone) { + observer.onCompleted(); + cancel.unsubscribe(); + } + } + group.remove(resource); + } + @Override + public void onNext(TRight args) { + int id = 0; + synchronized (gate) { + id = rightId++; + rightMap.put(id, args); + } + SerialSubscription md = new SerialSubscription(); + group.add(md); + + Observable duration; + try { + duration = rightDurationSelector.call(args); + } catch (Throwable t) { + observer.onError(t); + cancel.unsubscribe(); + return; + } + + md.setSubscription(duration.subscribe(new RightDurationObserver(id, md))); + + synchronized (gate) { + for (TLeft lv : leftMap.values()) { + R result; + try { + result = resultSelector.call(lv, args); + } catch (Throwable t) { + observer.onError(t); + cancel.unsubscribe(); + return; + } + observer.onNext(result); + } + } + } + @Override + public void onError(Throwable e) { + synchronized (gate) { + observer.onError(e); + cancel.unsubscribe(); + } + } + @Override + public void onCompleted() { + synchronized (gate) { + rightDone = true; + if (leftDone || rightMap.isEmpty()) { + observer.onCompleted(); + cancel.unsubscribe(); + } else { + self.unsubscribe(); + } + } + } + /** Observe the right duration. */ + class RightDurationObserver implements Observer { + final int id; + final Subscription handle; + public RightDurationObserver(int id, Subscription handle) { + this.id = id; + this.handle = handle; + } + + @Override + public void onNext(TRightDuration args) { + expire(id, handle); + } + + @Override + public void onError(Throwable e) { + RightObserver.this.onError(e); + } + + @Override + public void onCompleted() { + expire(id, handle); + } + + } + } + } +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationJoinTest.java b/rxjava-core/src/test/java/rx/operators/OperationJoinTest.java new file mode 100644 index 0000000000..c96cbb1f93 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationJoinTest.java @@ -0,0 +1,310 @@ +/** + * Copyright 2013 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.operators; + +import java.util.Collection; +import org.junit.Before; +import org.junit.Test; +import static org.mockito.Matchers.any; +import org.mockito.Mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import org.mockito.MockitoAnnotations; +import rx.Observable; +import rx.Observer; +import rx.subjects.PublishSubject; +import rx.util.functions.Action1; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +public class OperationJoinTest { + @Mock + Observer observer; + + Func2 add = new Func2() { + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + }; + Func1> just(final Observable observable) { + return new Func1>() { + @Override + public Observable call(Integer t1) { + return observable; + } + }; + } + Action1 toList(final Collection out) { + return new Action1() { + @Override + public void call(Integer t1) { + out.add(t1); + } + }; + } + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + @Test + public void normal1() { + PublishSubject source1 = PublishSubject.create(); + PublishSubject source2 = PublishSubject.create(); + + Observable m = source1.join(source2, + just(Observable.never()), + just(Observable.never()), add); + + m.subscribe(observer); + + source1.onNext(1); + source1.onNext(2); + source1.onNext(4); + + source2.onNext(16); + source2.onNext(32); + source2.onNext(64); + + source1.onCompleted(); + source2.onCompleted(); + + verify(observer, times(1)).onNext(17); + verify(observer, times(1)).onNext(18); + verify(observer, times(1)).onNext(20); + verify(observer, times(1)).onNext(33); + verify(observer, times(1)).onNext(34); + verify(observer, times(1)).onNext(36); + verify(observer, times(1)).onNext(65); + verify(observer, times(1)).onNext(66); + verify(observer, times(1)).onNext(68); + + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + @Test + public void normal1WithDuration() { + PublishSubject source1 = PublishSubject.create(); + PublishSubject source2 = PublishSubject.create(); + + PublishSubject duration1 = PublishSubject.create(); + + Observable m = source1.join(source2, + just(duration1), + just(Observable.never()), add); + m.subscribe(observer); + + source1.onNext(1); + source1.onNext(2); + source2.onNext(16); + + duration1.onNext(1); + + source1.onNext(4); + source1.onNext(8); + + source1.onCompleted(); + source2.onCompleted(); + + verify(observer, times(1)).onNext(17); + verify(observer, times(1)).onNext(18); + verify(observer, times(1)).onNext(20); + verify(observer, times(1)).onNext(24); + + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + } + @Test + public void normal2() { + PublishSubject source1 = PublishSubject.create(); + PublishSubject source2 = PublishSubject.create(); + + Observable m = source1.join(source2, + just(Observable.never()), + just(Observable.never()), add); + + m.subscribe(observer); + + source1.onNext(1); + source1.onNext(2); + source1.onCompleted(); + + source2.onNext(16); + source2.onNext(32); + source2.onNext(64); + + source2.onCompleted(); + + verify(observer, times(1)).onNext(17); + verify(observer, times(1)).onNext(18); + verify(observer, times(1)).onNext(33); + verify(observer, times(1)).onNext(34); + verify(observer, times(1)).onNext(65); + verify(observer, times(1)).onNext(66); + + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + @Test + public void leftThrows() { + PublishSubject source1 = PublishSubject.create(); + PublishSubject source2 = PublishSubject.create(); + + Observable m = source1.join(source2, + just(Observable.never()), + just(Observable.never()), add); + + m.subscribe(observer); + + source2.onNext(1); + source1.onError(new RuntimeException("Forced failure")); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onCompleted(); + verify(observer, never()).onNext(any()); + } + @Test + public void rightThrows() { + PublishSubject source1 = PublishSubject.create(); + PublishSubject source2 = PublishSubject.create(); + + Observable m = source1.join(source2, + just(Observable.never()), + just(Observable.never()), add); + + m.subscribe(observer); + + source1.onNext(1); + source2.onError(new RuntimeException("Forced failure")); + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onCompleted(); + verify(observer, never()).onNext(any()); + } + @Test + public void leftDurationThrows() { + PublishSubject source1 = PublishSubject.create(); + PublishSubject source2 = PublishSubject.create(); + + Observable duration1 = Observable.error(new RuntimeException("Forced failure")); + + Observable m = source1.join(source2, + just(duration1), + just(Observable.never()), add); + m.subscribe(observer); + + source1.onNext(1); + + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onCompleted(); + verify(observer, never()).onNext(any()); + } + @Test + public void rightDurationThrows() { + PublishSubject source1 = PublishSubject.create(); + PublishSubject source2 = PublishSubject.create(); + + Observable duration1 = Observable.error(new RuntimeException("Forced failure")); + + Observable m = source1.join(source2, + just(Observable.never()), + just(duration1), add); + m.subscribe(observer); + + source2.onNext(1); + + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onCompleted(); + verify(observer, never()).onNext(any()); + } + @Test + public void leftDurationSelectorThrows() { + PublishSubject source1 = PublishSubject.create(); + PublishSubject source2 = PublishSubject.create(); + + Func1> fail = new Func1>() { + @Override + public Observable call(Integer t1) { + throw new RuntimeException("Forced failure"); + } + }; + + Observable m = source1.join(source2, + fail, + just(Observable.never()), add); + m.subscribe(observer); + + source1.onNext(1); + + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onCompleted(); + verify(observer, never()).onNext(any()); + } + @Test + public void rightDurationSelectorThrows() { + PublishSubject source1 = PublishSubject.create(); + PublishSubject source2 = PublishSubject.create(); + + Func1> fail = new Func1>() { + @Override + public Observable call(Integer t1) { + throw new RuntimeException("Forced failure"); + } + }; + + Observable m = source1.join(source2, + just(Observable.never()), + fail, add); + m.subscribe(observer); + + source2.onNext(1); + + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onCompleted(); + verify(observer, never()).onNext(any()); + } + @Test + public void resultSelectorThrows() { + PublishSubject source1 = PublishSubject.create(); + PublishSubject source2 = PublishSubject.create(); + + Func2 fail = new Func2() { + @Override + public Integer call(Integer t1, Integer t2) { + throw new RuntimeException("Forced failure"); + } + }; + + Observable m = source1.join(source2, + just(Observable.never()), + just(Observable.never()), fail); + m.subscribe(observer); + + source1.onNext(1); + source2.onNext(2); + + + verify(observer, times(1)).onError(any(Throwable.class)); + verify(observer, never()).onCompleted(); + verify(observer, never()).onNext(any()); + } +} From ff5656b361e92170bc4a8b208a0487e0c5c70a35 Mon Sep 17 00:00:00 2001 From: akarnokd Date: Thu, 21 Nov 2013 22:34:13 +0100 Subject: [PATCH 286/333] Removed unused test function --- .../src/test/java/rx/operators/OperationJoinTest.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/rxjava-core/src/test/java/rx/operators/OperationJoinTest.java b/rxjava-core/src/test/java/rx/operators/OperationJoinTest.java index c96cbb1f93..8f841ebb50 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationJoinTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationJoinTest.java @@ -49,14 +49,6 @@ public Observable call(Integer t1) { } }; } - Action1 toList(final Collection out) { - return new Action1() { - @Override - public void call(Integer t1) { - out.add(t1); - } - }; - } @Before public void before() { MockitoAnnotations.initMocks(this); From 9343e290b3bfff7567a93d060f5b9850592358af Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Thu, 21 Nov 2013 14:20:52 -0800 Subject: [PATCH 287/333] Empty subscribe --- rxjava-core/src/main/java/rx/Observable.java | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 4b80d4e049..56a4664654 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -303,6 +303,33 @@ private Subscription protectivelyWrapAndSubscribe(Observer o) { return subscription.wrap(subscribe(new SafeObserver(subscription, o))); } + /** + * Subscribe and ignore all events. + * + * @return + */ + public Subscription subscribe() { + return protectivelyWrapAndSubscribe(new Observer() { + + @Override + public void onCompleted() { + // do nothing + } + + @Override + public void onError(Throwable e) { + handleError(e); + throw new OnErrorNotImplementedException(e); + } + + @Override + public void onNext(T args) { + // do nothing + } + + }); + } + /** * An {@link Observer} must call an Observable's {@code subscribe} method * in order to receive items and notifications from the Observable. From 2cdbbca1326b2e6a0551d58b8e134d9b8bede9d6 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 17:51:27 +0100 Subject: [PATCH 288/333] rename README.md to Rationale.md --- language-adaptors/rxjava-scala/{README.md => Rationale.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename language-adaptors/rxjava-scala/{README.md => Rationale.md} (100%) diff --git a/language-adaptors/rxjava-scala/README.md b/language-adaptors/rxjava-scala/Rationale.md similarity index 100% rename from language-adaptors/rxjava-scala/README.md rename to language-adaptors/rxjava-scala/Rationale.md From 402fbec638c7133e2f5fda29f5cfd8c47208cc92 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 17:53:16 +0100 Subject: [PATCH 289/333] restore original README.md --- language-adaptors/rxjava-scala/README.md | 101 +++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 language-adaptors/rxjava-scala/README.md diff --git a/language-adaptors/rxjava-scala/README.md b/language-adaptors/rxjava-scala/README.md new file mode 100644 index 0000000000..d198abcdb8 --- /dev/null +++ b/language-adaptors/rxjava-scala/README.md @@ -0,0 +1,101 @@ +# Scala Adaptor for RxJava + +This adaptor allows to use RxJava in Scala with anonymous functions, e.g. + +```scala +val o = Observable.interval(200 millis).take(5) +o.subscribe(n => println("n = " + n)) +Observable(1, 2, 3, 4).reduce(_ + _) +``` + +For-comprehensions are also supported: + +```scala +val first = Observable(10, 11, 12) +val second = Observable(10, 11, 12) +val booleans = for ((n1, n2) <- (first zip second)) yield (n1 == n2) +``` + +Further, this adaptor attempts to expose an API which is as Scala-idiomatic as possible. This means that certain methods have been renamed, their signature was changed, or static methods were changed to instance methods. Some examples: + +```scala + // instead of concat: +def ++[U >: T](that: Observable[U]): Observable[U] + +// instance method instead of static: +def zip[U](that: Observable[U]): Observable[(T, U)] + +// the implicit evidence argument ensures that dematerialize can only be called on Observables of Notifications: +def dematerialize[U](implicit evidence: T <:< Notification[U]): Observable[U] + +// additional type parameter U with lower bound to get covariance right: +def onErrorResumeNext[U >: T](resumeFunction: Throwable => Observable[U]): Observable[U] + +// curried in Scala collections, so curry fold also here: +def fold[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] + +// using Duration instead of (long timepan, TimeUnit duration): +def sample(duration: Duration): Observable[T] + +// called skip in Java, but drop in Scala +def drop(n: Int): Observable[T] + +// there's only mapWithIndex in Java, because Java doesn't have tuples: +def zipWithIndex: Observable[(T, Int)] + +// corresponds to Java's toList: +def toSeq: Observable[Seq[T]] + +// the implicit evidence argument ensures that switch can only be called on Observables of Observables: +def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] + +// Java's from becomes apply, and we use Scala Range +def apply(range: Range): Observable[Int] + +// use Bottom type: +def never: Observable[Nothing] +``` + +Also, the Scala Observable is fully covariant in its type parameter, whereas the Java Observable only achieves partial covariance due to limitations of Java's type system (or if you can fix this, your suggestions are very welcome). + +For more examples, see [RxScalaDemo.scala](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala). + +Scala code using Rx should only import members from `rx.lang.scala` and below. + + +## Documentation + +The API documentation can be found [here](http://rxscala.github.io/scaladoc/index.html#rx.lang.scala.Observable). + +You can build the API documentation yourself by running `./gradlew scaladoc` in the RxJava root directory. + +Then navigate to `RxJava/language-adaptors/rxjava-scala/build/docs/scaladoc/index.html` to display it. + + +## Binaries + +Binaries and dependency information for Maven, Ivy, Gradle and others can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22rxjava-scala%22). + +Example for Maven: + +```xml + + com.netflix.rxjava + rxjava-scala + x.y.z + +``` + +and for Ivy: + +```xml + +``` + +and for sbt: + +```scala +libraryDependencies ++= Seq( + "com.netflix.rxjava" % "rxjava-scala" % "x.y.z" +) +``` From 4fa20e4a26ceabeea8a47994601297c16150b96d Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 18:01:27 +0100 Subject: [PATCH 290/333] mention Rationale.md in README.md --- language-adaptors/rxjava-scala/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/language-adaptors/rxjava-scala/README.md b/language-adaptors/rxjava-scala/README.md index d198abcdb8..30b8d75b63 100644 --- a/language-adaptors/rxjava-scala/README.md +++ b/language-adaptors/rxjava-scala/README.md @@ -67,6 +67,8 @@ Scala code using Rx should only import members from `rx.lang.scala` and below. The API documentation can be found [here](http://rxscala.github.io/scaladoc/index.html#rx.lang.scala.Observable). +Note that starting from version 0.15, `rx.lang.scala.Observable` is not a value class any more. [./Rationale.md](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/Rationale.md) explains why. + You can build the API documentation yourself by running `./gradlew scaladoc` in the RxJava root directory. Then navigate to `RxJava/language-adaptors/rxjava-scala/build/docs/scaladoc/index.html` to display it. From 765d27c66539eeb0601cf2f0b99c93a054ac2cfc Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 18:15:16 +0100 Subject: [PATCH 291/333] move commented-out example from TestScheduler to separate file TestSchedulerExample --- .../scala/examples/TestSchedulerExample.scala | 45 +++++++++++++++++++ .../scala/concurrency/TestScheduler.scala | 38 ---------------- 2 files changed, 45 insertions(+), 38 deletions(-) create mode 100644 language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/TestSchedulerExample.scala diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/TestSchedulerExample.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/TestSchedulerExample.scala new file mode 100644 index 0000000000..df588c9689 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/TestSchedulerExample.scala @@ -0,0 +1,45 @@ +package rx.lang.scala.examples + +import org.junit.Test +import org.scalatest.junit.JUnitSuite +import scala.concurrent.duration._ +import scala.language.postfixOps +import rx.lang.scala.{ Observable, Observer } +import rx.lang.scala.concurrency.TestScheduler + +class TestSchedulerExample extends JUnitSuite { + + @Test def testInterval() { + import org.mockito.Matchers._ + import org.mockito.Mockito._ + + val scheduler = TestScheduler() + // Use a Java Observer for Mockito + val observer = mock(classOf[rx.Observer[Long]]) + + val o = Observable.interval(1 second, scheduler) + + // Wrap Java Observer in Scala Observer, then subscribe + val sub = o.subscribe(Observer(observer)) + + verify(observer, never).onNext(0L) + verify(observer, never).onCompleted() + verify(observer, never).onError(any(classOf[Throwable])) + + scheduler.advanceTimeTo(2 seconds) + + val inOrdr = inOrder(observer); + inOrdr.verify(observer, times(1)).onNext(0L) + inOrdr.verify(observer, times(1)).onNext(1L) + inOrdr.verify(observer, never).onNext(2L) + verify(observer, never).onCompleted() + verify(observer, never).onError(any(classOf[Throwable])) + + sub.unsubscribe(); + scheduler.advanceTimeTo(4 seconds) + verify(observer, never).onNext(2L) + verify(observer, times(1)).onCompleted() + verify(observer, never).onError(any(classOf[Throwable])) + } + +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala index a76d95f096..e2873f9a29 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala @@ -2,7 +2,6 @@ package rx.lang.scala.concurrency import scala.concurrent.duration.Duration import rx.lang.scala.Scheduler -//import org.scalatest.junit.JUnitSuite /** * Scheduler with artificial time, useful for testing. @@ -66,40 +65,3 @@ object TestScheduler { } } -//private class UnitTest extends JUnitSuite { -// import org.junit.Test -// import scala.concurrent.duration._ -// import scala.language.postfixOps -// import rx.lang.scala.{Observable, Observer} -// -// @Test def testInterval() { -// import org.mockito.Matchers._ -// import org.mockito.Mockito._ -// -// val scheduler = TestScheduler() -// val observer = mock(classOf[rx.Observer[Long]]) -// -// val o = Observable.interval(1 second, scheduler) -// val sub = o.subscribe(observer) -// -// verify(observer, never).onNext(0L) -// verify(observer, never).onCompleted() -// verify(observer, never).onError(any(classOf[Throwable])) -// -// scheduler.advanceTimeTo(2 seconds) -// -// val inOrdr = inOrder(observer); -// inOrdr.verify(observer, times(1)).onNext(0L) -// inOrdr.verify(observer, times(1)).onNext(1L) -// inOrdr.verify(observer, never).onNext(2L) -// verify(observer, never).onCompleted() -// verify(observer, never).onError(any(classOf[Throwable])) -// -// sub.unsubscribe(); -// scheduler.advanceTimeTo(4 seconds) -// verify(observer, never).onNext(2L) -// verify(observer, times(1)).onCompleted() -// verify(observer, never).onError(any(classOf[Throwable])) -// } -//} - From d80dfae33ca3842143ebc19af6442ccf2d3d7d63 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 18:23:34 +0100 Subject: [PATCH 292/333] restore package.scala files (mainly for scaladoc) --- .../rx/lang/scala/concurrency/package.scala | 31 ++++ .../rx/lang/scala/observables/package.scala | 12 ++ .../main/scala/rx/lang/scala/package.scala | 158 ++++++++++++++++++ .../rx/lang/scala/subjects/package.scala | 31 ++++ .../scala/rx/lang/scala/util/package.scala | 49 ++++++ 5 files changed, 281 insertions(+) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala new file mode 100644 index 0000000000..a3e61c0021 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala @@ -0,0 +1,31 @@ +/** + * Copyright 2013 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.lang.scala + +import rx.concurrency.CurrentThreadScheduler + +/** + * Provides schedulers. + */ +package object concurrency { + + // These classes are not exposed to Scala users, but are accessible through rx.lang.scala.concurrency.Schedulers: + + // rx.concurrency.CurrentThreadScheduler + // rx.concurrency.ExecutorScheduler + // rx.concurrency.ImmediateScheduler + // rx.concurrency.NewThreadScheduler +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala new file mode 100644 index 0000000000..8507f0a54c --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala @@ -0,0 +1,12 @@ +package rx.lang.scala + +/** + * Contains special Observables. + * + * In Scala, this package only contains [[BlockingObservable]]. + * In the corresponding Java package `rx.observables`, there is also a + * `GroupedObservable` and a `ConnectableObservable`, but these are not needed + * in Scala, because we use a pair `(key, observable)` instead of `GroupedObservable` + * and a pair `(startFunction, observable)` instead of `ConnectableObservable`. + */ +package object observables {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala new file mode 100644 index 0000000000..8aa0e63760 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala @@ -0,0 +1,158 @@ +/** + * Copyright 2013 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.lang + +import java.util.concurrent.TimeUnit +import java.util.Date + +/* + * Note that: + * - Scala users cannot use Java's types with variance without always using writing + * e.g. rx.Notification[_ <: T], so we create aliases fixing the variance + * - For consistency, we create aliases for all types which Scala users need + */ + +/** + * This package contains all classes that RxScala users need. + * + * It mirrors the structure of package `rx`, but implementation classes that RxScala users + * will not need are left out. + */ +package object scala { + + /* + * Here we're imitating C's preprocessor using Search & Replace. + * + * To activate the code needed to get nice Scaladoc, do the following replacements: + * /*//#ifdef SCALADOC --> //#ifdef SCALADOC + * *///#else --> /*//#else + * //#endif --> *///#endif + * + * To get back to the actual code, undo the above replacements. + * + */ + + /*//#ifdef SCALADOC + + /** + * Provides a mechanism for receiving push-based notifications. + * + * After an Observer calls an [[Observable]]'s `subscribe` method, the Observable + * calls the Observer's `onNext` method to provide notifications. A well-behaved Observable will + * call an Observer's `onCompleted` method exactly once or the Observer's `onError` method exactly once. + */ + trait Observer[-T] { + + /** + * Notifies the Observer that the [[Observable]] has finished sending push-based notifications. + * + * The [[Observable]] will not call this method if it calls `onError`. + */ + def onCompleted(): Unit + + /** + * Notifies the Observer that the [[Observable]] has experienced an error condition. + * + * If the [[Observable]] calls this method, it will not thereafter call `onNext` or `onCompleted`. + */ + def onError(e: Throwable): Unit + + /** + * Provides the Observer with new data. + * + * The [[Observable]] calls this closure 0 or more times. + * + * The [[Observable]] will not call this method again after it calls either `onCompleted` or `onError`. + */ + def onNext(arg: T): Unit + } + + /** + * Subscriptions are returned from all `Observable.subscribe` methods to allow unsubscribing. + * + * This interface is the equivalent of `IDisposable` in the .NET Rx implementation. + */ + trait Subscription { + /** + * Call this method to stop receiving notifications on the Observer that was registered when + * this Subscription was received. + */ + def unsubscribe(): Unit + } + + import language.implicitConversions + + private[scala] implicit def fakeSubscription2RxSubscription(s: Subscription): rx.Subscription = + new rx.Subscription { + def unsubscribe() = s.unsubscribe() + } + private[scala] implicit def rxSubscription2FakeSubscription(s: rx.Subscription): Subscription = + new Subscription { + def unsubscribe() = s.unsubscribe() + } + + private[scala] implicit def schedulerActionToFunc2[T](action: (Scheduler, T) => Subscription) = + new rx.util.functions.Func2[rx.Scheduler, T, rx.Subscription] { + def call(s: rx.Scheduler, t: T): rx.Subscription = { + action(ImplicitFunctionConversions.javaSchedulerToScalaScheduler(s), t) + } + } + + private[scala] implicit def fakeObserver2RxObserver[T](o: Observer[T]): rx.Observer[_ >: T] = ??? + private[scala] implicit def rxObserver2fakeObserver[T](o: rx.Observer[_ >: T]): Observer[T] = ??? + + *///#else + + type Observer[-T] = rx.Observer[_ >: T] + + type Subscription = rx.Subscription + + //#endif + + /** + * Allows to construct observables in a similar way as futures. + * + * Example: + * + * {{{ + * implicit val scheduler = Schedulers.threadPoolForIO + * val o: Observable[List[Friend]] = observable { + * session.getFriends + * } + * o.subscribe( + * friendList => println(friendList), + * err => println(err.getMessage) + * ) + * }}} + */ + def observable[T](body: => T)(implicit scheduler: Scheduler): Observable[T] = { + Observable(1).observeOn(scheduler).map(_ => body) + } +} + +/* + +These classes are considered unnecessary for Scala users, so we don't create aliases for them: + +rx.plugins.RxJavaErrorHandler +rx.plugins.RxJavaObservableExecutionHook +rx.plugins.RxJavaPlugins + +rx.subscriptions.BooleanSubscription +rx.subscriptions.CompositeSubscription +rx.subscriptions.Subscriptions + +*/ diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala new file mode 100644 index 0000000000..ec096e92eb --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala @@ -0,0 +1,31 @@ +package rx.lang.scala + +/** + * Provides the type `Subject`. + */ +package object subjects { + + /** + * A Subject is an Observable and an Observer at the same time. + * + * The Java Subject looks like this: + * {{{ + * public abstract class Subject extends Observable implements Observer + * }}} + */ + type Subject[-T, +R] = rx.subjects.Subject[_ >: T, _ <: R] + + // For nicer scaladoc, we would like to present something like this: + /* + trait Observable[+R] {} + trait Observer[-T] {} + trait Subject[-T, +R] extends Observable[R] with Observer[T] { } + */ + + // We don't make aliases to these types, because they are considered internal/not needed by users: + // rx.subjects.AsyncSubject + // rx.subjects.BehaviorSubject + // rx.subjects.PublishSubject + // rx.subjects.ReplaySubject + +} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala new file mode 100644 index 0000000000..ed19d849ab --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala @@ -0,0 +1,49 @@ +/** + * Copyright 2013 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.lang.scala + +/** + * Provides [[Opening]]s, [[Closing]]s, and [[Timestamped]]. + */ +package object util { + + /** + * Tagging interface for objects which can open buffers. + * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + */ + type Opening = rx.util.Opening + + /** + * Creates an object which can open buffers. + * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + */ + def Opening() = rx.util.Openings.create() + + /** + * Tagging interface for objects which can close buffers. + * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + */ + type Closing = rx.util.Closing + + /** + * Creates an object which can close buffers. + * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + */ + def Closing() = rx.util.Closings.create() + + // rx.util.Range not needed because there's a standard Scala Range + +} From 5796e71d5c259bbbb8a0fd1cf09013a18211e33c Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 20:00:51 +0100 Subject: [PATCH 293/333] update package objects --- .../main/scala/rx/lang/scala/package.scala | 110 ------------------ .../rx/lang/scala/subjects/package.scala | 29 +---- .../rx/lang/scala/subscriptions/package.scala | 6 + .../rx/lang/scala/subscriptions/scala.scala | 25 ---- .../main/scala/rx/lang/scala/util/util.scala | 49 -------- 5 files changed, 8 insertions(+), 211 deletions(-) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/package.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/scala.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/util.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala index 8aa0e63760..0809e1fb2b 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala @@ -18,13 +18,6 @@ package rx.lang import java.util.concurrent.TimeUnit import java.util.Date -/* - * Note that: - * - Scala users cannot use Java's types with variance without always using writing - * e.g. rx.Notification[_ <: T], so we create aliases fixing the variance - * - For consistency, we create aliases for all types which Scala users need - */ - /** * This package contains all classes that RxScala users need. * @@ -33,95 +26,6 @@ import java.util.Date */ package object scala { - /* - * Here we're imitating C's preprocessor using Search & Replace. - * - * To activate the code needed to get nice Scaladoc, do the following replacements: - * /*//#ifdef SCALADOC --> //#ifdef SCALADOC - * *///#else --> /*//#else - * //#endif --> *///#endif - * - * To get back to the actual code, undo the above replacements. - * - */ - - /*//#ifdef SCALADOC - - /** - * Provides a mechanism for receiving push-based notifications. - * - * After an Observer calls an [[Observable]]'s `subscribe` method, the Observable - * calls the Observer's `onNext` method to provide notifications. A well-behaved Observable will - * call an Observer's `onCompleted` method exactly once or the Observer's `onError` method exactly once. - */ - trait Observer[-T] { - - /** - * Notifies the Observer that the [[Observable]] has finished sending push-based notifications. - * - * The [[Observable]] will not call this method if it calls `onError`. - */ - def onCompleted(): Unit - - /** - * Notifies the Observer that the [[Observable]] has experienced an error condition. - * - * If the [[Observable]] calls this method, it will not thereafter call `onNext` or `onCompleted`. - */ - def onError(e: Throwable): Unit - - /** - * Provides the Observer with new data. - * - * The [[Observable]] calls this closure 0 or more times. - * - * The [[Observable]] will not call this method again after it calls either `onCompleted` or `onError`. - */ - def onNext(arg: T): Unit - } - - /** - * Subscriptions are returned from all `Observable.subscribe` methods to allow unsubscribing. - * - * This interface is the equivalent of `IDisposable` in the .NET Rx implementation. - */ - trait Subscription { - /** - * Call this method to stop receiving notifications on the Observer that was registered when - * this Subscription was received. - */ - def unsubscribe(): Unit - } - - import language.implicitConversions - - private[scala] implicit def fakeSubscription2RxSubscription(s: Subscription): rx.Subscription = - new rx.Subscription { - def unsubscribe() = s.unsubscribe() - } - private[scala] implicit def rxSubscription2FakeSubscription(s: rx.Subscription): Subscription = - new Subscription { - def unsubscribe() = s.unsubscribe() - } - - private[scala] implicit def schedulerActionToFunc2[T](action: (Scheduler, T) => Subscription) = - new rx.util.functions.Func2[rx.Scheduler, T, rx.Subscription] { - def call(s: rx.Scheduler, t: T): rx.Subscription = { - action(ImplicitFunctionConversions.javaSchedulerToScalaScheduler(s), t) - } - } - - private[scala] implicit def fakeObserver2RxObserver[T](o: Observer[T]): rx.Observer[_ >: T] = ??? - private[scala] implicit def rxObserver2fakeObserver[T](o: rx.Observer[_ >: T]): Observer[T] = ??? - - *///#else - - type Observer[-T] = rx.Observer[_ >: T] - - type Subscription = rx.Subscription - - //#endif - /** * Allows to construct observables in a similar way as futures. * @@ -142,17 +46,3 @@ package object scala { Observable(1).observeOn(scheduler).map(_ => body) } } - -/* - -These classes are considered unnecessary for Scala users, so we don't create aliases for them: - -rx.plugins.RxJavaErrorHandler -rx.plugins.RxJavaObservableExecutionHook -rx.plugins.RxJavaPlugins - -rx.subscriptions.BooleanSubscription -rx.subscriptions.CompositeSubscription -rx.subscriptions.Subscriptions - -*/ diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala index ec096e92eb..cf7db56f11 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala @@ -1,31 +1,6 @@ package rx.lang.scala /** - * Provides the type `Subject`. + * Subjects are Observers and Observables at the same time. */ -package object subjects { - - /** - * A Subject is an Observable and an Observer at the same time. - * - * The Java Subject looks like this: - * {{{ - * public abstract class Subject extends Observable implements Observer - * }}} - */ - type Subject[-T, +R] = rx.subjects.Subject[_ >: T, _ <: R] - - // For nicer scaladoc, we would like to present something like this: - /* - trait Observable[+R] {} - trait Observer[-T] {} - trait Subject[-T, +R] extends Observable[R] with Observer[T] { } - */ - - // We don't make aliases to these types, because they are considered internal/not needed by users: - // rx.subjects.AsyncSubject - // rx.subjects.BehaviorSubject - // rx.subjects.PublishSubject - // rx.subjects.ReplaySubject - -} \ No newline at end of file +package object subjects {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/package.scala new file mode 100644 index 0000000000..4662cb9ccb --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/package.scala @@ -0,0 +1,6 @@ +package rx.lang.scala + +/** + * Provides `Subscription`, and specialized versions of it. + */ +package object subscriptions {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/scala.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/scala.scala deleted file mode 100644 index d0c7fa1761..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/scala.scala +++ /dev/null @@ -1,25 +0,0 @@ -package rx.lang - -import rx.lang.scala.Scheduler -package object scala { - - /** - * Allows to construct observables in a similar way as futures. - * - * Example: - * - * {{{ - * implicit val scheduler = Schedulers.threadPoolForIO - * val o: Observable[List[Friend]] = observable { - * session.getFriends - * } - * o.subscribe( - * friendList => println(friendList), - * err => println(err.getMessage) - * ) - * }}} - */ - def observable[T](body: => T)(implicit scheduler: Scheduler): Observable[T] = { - Observable(1).observeOn(scheduler).map(_ => body) - } -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/util.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/util.scala deleted file mode 100644 index fe58b2eeb4..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/util.scala +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2013 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.lang.scala - -/** - * Provides [[rx.lang.scala.util.Opening]]s, [[rx.lang.scala.util.Closing]]s, and [[rx.util.Timestamped]]. - */ -package object util { - - /** - * Tagging interface for objects which can open buffers. - * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] - */ - type Opening = rx.util.Opening - - /** - * Creates an object which can open buffers. - * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] - */ - def Opening() = rx.util.Openings.create() - - /** - * Tagging interface for objects which can close buffers. - * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] - */ - type Closing = rx.util.Closing - - /** - * Creates an object which can close buffers. - * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] - */ - def Closing() = rx.util.Closings.create() - - // rx.util.Range not needed because there's a standard Scala Range - -} From 07ab12fc7678de090f8a7aeda660b3ea84be7275 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 20:21:37 +0100 Subject: [PATCH 294/333] PublishSubject.apply() does not take a parameter --- .../src/main/scala/rx/lang/scala/subjects/PublishSubject.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala index 93989bfe32..a5fd50af29 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala @@ -3,7 +3,7 @@ package rx.lang.scala.subjects import rx.lang.scala.Subject object PublishSubject { - def apply[T](value: T): PublishSubject[T] = { + def apply[T](): PublishSubject[T] = { new PublishSubject[T](rx.subjects.PublishSubject.create()) } } From 15bae11177c357bf6eeaec94465d15e4c95580c0 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 20:23:21 +0100 Subject: [PATCH 295/333] @Ignore RxScalaDemo --- .../src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala index 130c9f2aa8..bcb51fe94b 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala @@ -26,8 +26,9 @@ import rx.lang.scala.examples.Olympics import rx.lang.scala.Notification.OnCompleted import rx.lang.scala.Notification.OnError import rx.lang.scala.Notification.OnNext +import org.scalatest.Ignore -//@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily +@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @Test def intervalExample() { From 32f3fd71d1bc57920d60a763a5e0bfb1ac71d56a Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 20:28:15 +0100 Subject: [PATCH 296/333] private vars in Scala do not start with underscore --- .../rx/lang/scala/subscriptions/SerialSubscription.scala | 6 +++--- .../scala/rx/lang/scala/subscriptions/Subscription.scala | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala index 8ca4083c8d..c54a39b58d 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala @@ -29,17 +29,17 @@ object SerialSubscription { class SerialSubscription private[scala] (val asJavaSubscription: rx.subscriptions.SerialSubscription) extends Subscription { - private val _isUnsubscribed = new AtomicBoolean(false) + private val unsubscribed = new AtomicBoolean(false) /** * Checks whether the subscription has been unsubscribed. */ - def isUnsubscribed: Boolean = _isUnsubscribed.get() + def isUnsubscribed: Boolean = unsubscribed.get() /** * Unsubscribes this subscription, setting isUnsubscribed to true. */ - override def unsubscribe(): Unit = { super.unsubscribe(); _isUnsubscribed.set(true) } + override def unsubscribe(): Unit = { super.unsubscribe(); unsubscribed.set(true) } def subscription_=(value: Subscription): Unit = asJavaSubscription.setSubscription(value.asJavaSubscription) def subscription: Subscription = Subscription(asJavaSubscription.getSubscription) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/Subscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/Subscription.scala index ec4e7b5704..8ccc29742c 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/Subscription.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/Subscription.scala @@ -65,11 +65,11 @@ object Subscription { def apply(u: => Unit): Subscription = { new Subscription () { - private val _isUnsubscribed = new AtomicBoolean(false) - def isUnsubscribed = _isUnsubscribed.get() + private val unsubscribed = new AtomicBoolean(false) + def isUnsubscribed = unsubscribed.get() val asJavaSubscription = new rx.Subscription { - def unsubscribe() { u; _isUnsubscribed.set(true) } + def unsubscribe() { u; unsubscribed.set(true) } } } } From 2130684c30d418a75c9d76fd14d3150fdd136b63 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 20:31:09 +0100 Subject: [PATCH 297/333] restore CompletenessTest --- .../rx/lang/scala/CompletenessTest.scala | 354 ++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala new file mode 100644 index 0000000000..f38ac0d521 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala @@ -0,0 +1,354 @@ +package rx.lang.scala + +import java.util.Calendar + +import scala.collection.SortedMap +import scala.reflect.runtime.universe +import scala.reflect.runtime.universe.Symbol +import scala.reflect.runtime.universe.Type +import scala.reflect.runtime.universe.typeOf + +import org.junit.Ignore +import org.junit.Test +import org.scalatest.junit.JUnitSuite + +/** + * These tests can be used to check if all methods of the Java Observable have a corresponding + * method in the Scala Observable. + * + * These tests don't contain any assertions, so they will always succeed, but they print their + * results to stdout. + */ +class CompletenessTest extends JUnitSuite { + + // some frequently used comments: + val unnecessary = "[considered unnecessary in Scala land]" + val deprecated = "[deprecated in RxJava]" + val averageProblem = "[We can't have a general average method because Scala's `Numeric` does not have " + + "scalar multiplication (we would need to calculate `(1.0/numberOfElements)*sum`). " + + "You can use `fold` instead to accumulate `sum` and `numberOfElements` and divide at the end.]" + val commentForFirstWithPredicate = "[use `.filter(condition).first`]" + val fromFuture = "[TODO: Decide how Scala Futures should relate to Observables. Should there be a " + + "common base interface for Future and Observable? And should Futures also have an unsubscribe method?]" + + /** + * Maps each method from the Java Observable to its corresponding method in the Scala Observable + */ + val correspondence = defaultMethodCorrespondence ++ correspondenceChanges // ++ overrides LHS with RHS + + /** + * Creates default method correspondence mappings, assuming that Scala methods have the same + * name and the same argument types as in Java + */ + def defaultMethodCorrespondence: Map[String, String] = { + val allMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.Observable[_]]) + val tuples = for (javaM <- allMethods) yield (javaM, javaMethodSignatureToScala(javaM)) + tuples.toMap + } + + /** + * Manually added mappings from Java Observable methods to Scala Observable methods + */ + def correspondenceChanges = Map( + // manually added entries for Java instance methods + "aggregate(Func2[T, T, T])" -> "reduce((U, U) => U)", + "aggregate(R, Func2[R, _ >: T, R])" -> "foldLeft(R)((R, T) => R)", + "all(Func1[_ >: T, Boolean])" -> "forall(T => Boolean)", + "buffer(Long, Long, TimeUnit)" -> "buffer(Duration, Duration)", + "buffer(Long, Long, TimeUnit, Scheduler)" -> "buffer(Duration, Duration, Scheduler)", + "count()" -> "length", + "dematerialize()" -> "dematerialize(<:<[Observable[T], Observable[Notification[U]]])", + "elementAt(Int)" -> "[use `.drop(index).first`]", + "elementAtOrDefault(Int, T)" -> "[use `.drop(index).firstOrElse(default)`]", + "first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, + "firstOrDefault(T)" -> "firstOrElse(=> U)", + "firstOrDefault(Func1[_ >: T, Boolean], T)" -> "[use `.filter(condition).firstOrElse(default)`]", + "groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> "[use `groupBy` and `map`]", + "mapMany(Func1[_ >: T, _ <: Observable[_ <: R]])" -> "flatMap(T => Observable[R])", + "mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> "[combine `zipWithIndex` with `map` or with a for comprehension]", + "onErrorResumeNext(Func1[Throwable, _ <: Observable[_ <: T]])" -> "onErrorResumeNext(Throwable => Observable[U])", + "onErrorResumeNext(Observable[_ <: T])" -> "onErrorResumeNext(Observable[U])", + "onErrorReturn(Func1[Throwable, _ <: T])" -> "onErrorReturn(Throwable => U)", + "onExceptionResumeNext(Observable[_ <: T])" -> "onExceptionResumeNext(Observable[U])", + "parallel(Func1[Observable[T], Observable[R]])" -> "parallel(Observable[T] => Observable[R])", + "parallel(Func1[Observable[T], Observable[R]], Scheduler)" -> "parallel(Observable[T] => Observable[R], Scheduler)", + "reduce(Func2[T, T, T])" -> "reduce((U, U) => U)", + "reduce(R, Func2[R, _ >: T, R])" -> "foldLeft(R)((R, T) => R)", + "scan(Func2[T, T, T])" -> unnecessary, + "scan(R, Func2[R, _ >: T, R])" -> "scan(R)((R, T) => R)", + "skip(Int)" -> "drop(Int)", + "skipWhile(Func1[_ >: T, Boolean])" -> "dropWhile(T => Boolean)", + "skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary, + "startWith(Iterable[T])" -> "[unnecessary because we can just use `++` instead]", + "takeFirst()" -> "first", + "takeFirst(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, + "takeLast(Int)" -> "takeRight(Int)", + "takeWhileWithIndex(Func2[_ >: T, _ >: Integer, Boolean])" -> "[use `.zipWithIndex.takeWhile{case (elem, index) => condition}.map(_._1)`]", + "toList()" -> "toSeq", + "toSortedList()" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sorted)`]", + "toSortedList(Func2[_ >: T, _ >: T, Integer])" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sortWith(f))`]", + "where(Func1[_ >: T, Boolean])" -> "filter(T => Boolean)", + "window(Long, Long, TimeUnit)" -> "window(Duration, Duration)", + "window(Long, Long, TimeUnit, Scheduler)" -> "window(Duration, Duration, Scheduler)", + + // manually added entries for Java static methods + "average(Observable[Integer])" -> averageProblem, + "averageDoubles(Observable[Double])" -> averageProblem, + "averageFloats(Observable[Float])" -> averageProblem, + "averageLongs(Observable[Long])" -> averageProblem, + "create(OnSubscribeFunc[T])" -> "apply(Observer[T] => Subscription)", + "combineLatest(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "combineLatest(Observable[U])", + "concat(Observable[_ <: Observable[_ <: T]])" -> "concat(<:<[Observable[T], Observable[Observable[U]]])", + "defer(Func0[_ <: Observable[_ <: T]])" -> "defer(=> Observable[T])", + "empty()" -> "apply(T*)", + "error(Throwable)" -> "apply(Throwable)", + "from(Array[T])" -> "apply(T*)", + "from(Iterable[_ <: T])" -> "apply(T*)", + "from(Future[_ <: T])" -> fromFuture, + "from(Future[_ <: T], Long, TimeUnit)" -> fromFuture, + "from(Future[_ <: T], Scheduler)" -> fromFuture, + "just(T)" -> "apply(T*)", + "merge(Observable[_ <: T], Observable[_ <: T])" -> "merge(Observable[U])", + "merge(Observable[_ <: Observable[_ <: T]])" -> "flatten(<:<[Observable[T], Observable[Observable[U]]])", + "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[U])", + "mergeDelayError(Observable[_ <: Observable[_ <: T]])" -> "flattenDelayError(<:<[Observable[T], Observable[Observable[U]]])", + "range(Int, Int)" -> "apply(Range)", + "sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use `(first zip second) map (p => p._1 == p._2)`]", + "sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> "[use `(first zip second) map (p => equality(p._1, p._2))`]", + "sum(Observable[Integer])" -> "sum(Numeric[U])", + "sumDoubles(Observable[Double])" -> "sum(Numeric[U])", + "sumFloats(Observable[Float])" -> "sum(Numeric[U])", + "sumLongs(Observable[Long])" -> "sum(Numeric[U])", + "synchronize(Observable[T])" -> "synchronize", + "switchDo(Observable[_ <: Observable[_ <: T]])" -> deprecated, + "switchOnNext(Observable[_ <: Observable[_ <: T]])" -> "switch(<:<[Observable[T], Observable[Observable[U]]])", + "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method `zip` and `map`]", + "zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]", + "zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]" + ) ++ List.iterate("T", 9)(s => s + ", T").map( + // all 9 overloads of startWith: + "startWith(" + _ + ")" -> "[unnecessary because we can just use `++` instead]" + ).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( + // concat 2-9 + "concat(" + _ + ")" -> "[unnecessary because we can use `++` instead or `Observable(o1, o2, ...).concat`]" + ).drop(1).toMap ++ List.iterate("T", 10)(s => s + ", T").map( + // all 10 overloads of from: + "from(" + _ + ")" -> "apply(T*)" + ).toMap ++ (3 to 9).map(i => { + // zip3-9: + val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") + val funcParams = (1 to i).map(j => s"_ >: T$j, ").mkString("") + ("zip(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", unnecessary) + }).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( + // merge 3-9: + "merge(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flatten` instead]" + ).drop(2).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( + // mergeDelayError 3-9: + "mergeDelayError(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flattenDelayError` instead]" + ).drop(2).toMap ++ (3 to 9).map(i => { + // combineLatest 3-9: + val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") + val funcParams = (1 to i).map(j => s"_ >: T$j, ").mkString("") + ("combineLatest(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", "[If C# doesn't need it, Scala doesn't need it either ;-)]") + }).toMap + + def removePackage(s: String) = s.replaceAll("(\\w+\\.)+(\\w+)", "$2") + + def methodMembersToMethodStrings(members: Iterable[Symbol]): Iterable[String] = { + for (member <- members; alt <- member.asTerm.alternatives) yield { + val m = alt.asMethod + // multiple parameter lists in case of curried functions + val paramListStrs = for (paramList <- m.paramss) yield { + paramList.map( + symb => removePackage(symb.typeSignature.toString.replaceAll(",(\\S)", ", $1")) + ).mkString("(", ", ", ")") + } + val name = alt.asMethod.name.decoded + name + paramListStrs.mkString("") + } + } + + def getPublicInstanceMethods(tp: Type): Iterable[String] = { + // declarations: => only those declared in Observable + // members => also those of superclasses + methodMembersToMethodStrings(tp.declarations.filter(m => m.isMethod && m.isPublic)) + // TODO how can we filter out instance methods which were put into companion because + // of extends AnyVal in a way which does not depend on implementation-chosen name '$extension'? + .filter(! _.contains("$extension")) + } + + // also applicable for Java types + def getPublicInstanceAndCompanionMethods(tp: Type): Iterable[String] = + getPublicInstanceMethods(tp) ++ + getPublicInstanceMethods(tp.typeSymbol.companionSymbol.typeSignature) + + def printMethodSet(title: String, tp: Type) { + println("\n" + title) + println(title.map(_ => '-') + "\n") + getPublicInstanceMethods(tp).toList.sorted.foreach(println(_)) + } + + @Ignore // because spams output + @Test def printJavaInstanceMethods: Unit = { + printMethodSet("Instance methods of rx.Observable", + typeOf[rx.Observable[_]]) + } + + @Ignore // because spams output + @Test def printScalaInstanceMethods: Unit = { + printMethodSet("Instance methods of rx.lang.scala.Observable", + typeOf[rx.lang.scala.Observable[_]]) + } + + @Ignore // because spams output + @Test def printJavaStaticMethods: Unit = { + printMethodSet("Static methods of rx.Observable", + typeOf[rx.Observable[_]].typeSymbol.companionSymbol.typeSignature) + } + + @Ignore // because spams output + @Test def printScalaCompanionMethods: Unit = { + printMethodSet("Companion methods of rx.lang.scala.Observable", + typeOf[rx.lang.scala.Observable.type]) + } + + def javaMethodSignatureToScala(s: String): String = { + s.replaceAllLiterally("Long, TimeUnit", "Duration") + .replaceAll("Action0", "() => Unit") + // nested [] can't be parsed with regex, so these will have to be added manually + .replaceAll("Action1\\[([^]]*)\\]", "$1 => Unit") + .replaceAll("Action2\\[([^]]*), ([^]]*)\\]", "($1, $2) => Unit") + .replaceAll("Func0\\[([^]]*)\\]", "() => $1") + .replaceAll("Func1\\[([^]]*), ([^]]*)\\]", "$1 => $2") + .replaceAll("Func2\\[([^]]*), ([^]]*), ([^]]*)\\]", "($1, $2) => $3") + .replaceAllLiterally("_ <: ", "") + .replaceAllLiterally("_ >: ", "") + .replaceAll("(\\w+)\\(\\)", "$1") + } + + @Ignore // because spams output + @Test def printDefaultMethodCorrespondence: Unit = { + println("\nDefault Method Correspondence") + println( "-----------------------------\n") + val c = SortedMap(defaultMethodCorrespondence.toSeq : _*) + val len = c.keys.map(_.length).max + 2 + for ((javaM, scalaM) <- c) { + println(s""" %-${len}s -> %s,""".format("\"" + javaM + "\"", "\"" + scalaM + "\"")) + } + } + + @Ignore // because spams output + @Test def printCorrectedMethodCorrespondence: Unit = { + println("\nCorrected Method Correspondence") + println( "-------------------------------\n") + val c = SortedMap(correspondence.toSeq : _*) + for ((javaM, scalaM) <- c) { + println("%s -> %s,".format("\"" + javaM + "\"", "\"" + scalaM + "\"")) + } + } + + def checkMethodPresence(expectedMethods: Iterable[String], tp: Type): Unit = { + val actualMethods = getPublicInstanceAndCompanionMethods(tp).toSet + val expMethodsSorted = expectedMethods.toList.sorted + var good = 0 + var bad = 0 + for (m <- expMethodsSorted) if (actualMethods.contains(m) || m.charAt(0) == '[') { + good += 1 + } else { + bad += 1 + println(s"Warning: $m is NOT present in $tp") + } + val status = if (bad == 0) "SUCCESS" else "BAD" + println(s"$status: $bad out of ${bad+good} methods were not found in $tp") + } + + @Test def checkScalaMethodPresenceVerbose: Unit = { + println("\nTesting that all mentioned Scala methods exist") + println( "----------------------------------------------\n") + + val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable[_]]).toSet + var good = 0 + var bad = 0 + for ((javaM, scalaM) <- SortedMap(correspondence.toSeq :_*)) { + if (actualMethods.contains(scalaM) || scalaM.charAt(0) == '[') { + good += 1 + } else { + bad += 1 + println(s"Warning:") + println(s"$scalaM is NOT present in Scala Observable") + println(s"$javaM is the method in Java Observable generating this warning") + } + } + val status = if (bad == 0) "SUCCESS" else "BAD" + println(s"\n$status: $bad out of ${bad+good} methods were not found in Scala Observable") + } + + def setTodoForMissingMethods(corresp: Map[String, String]): Map[String, String] = { + val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable[_]]).toSet + for ((javaM, scalaM) <- corresp) yield + (javaM, if (actualMethods.contains(scalaM) || scalaM.charAt(0) == '[') scalaM else "[**TODO: missing**]") + } + + @Test def checkJavaMethodPresence: Unit = { + println("\nTesting that all mentioned Java methods exist") + println( "---------------------------------------------\n") + checkMethodPresence(correspondence.keys, typeOf[rx.Observable[_]]) + } + + @Ignore // because we prefer the verbose version + @Test def checkScalaMethodPresence: Unit = { + checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable[_]]) + } + + def scalaToJavaSignature(s: String) = + s.replaceAllLiterally("_ <:", "? extends") + .replaceAllLiterally("_ >:", "? super") + .replaceAllLiterally("[", "<") + .replaceAllLiterally("]", ">") + .replaceAllLiterally("Array", "T[]") + + def escapeJava(s: String) = + s.replaceAllLiterally("<", "<") + .replaceAllLiterally(">", ">") + + @Ignore // because spams output + @Test def printMarkdownCorrespondenceTable() { + def isInteresting(p: (String, String)): Boolean = + p._1.replaceAllLiterally("()", "") != p._2 + def groupingKey(p: (String, String)): (String, String) = + (if (p._1.startsWith("average")) "average" else p._1.takeWhile(_ != '('), p._2) + def formatJavaCol(name: String, alternatives: Iterable[String]): String = { + alternatives.toList.sorted.map(scalaToJavaSignature(_)).map(s => { + if (s.length > 64) { + val toolTip = escapeJava(s) + "" + name + "(...)" + } else { + "`" + s + "`" + } + }).mkString("
    ") + } + def formatScalaCol(s: String): String = + if (s.startsWith("[") && s.endsWith("]")) s.drop(1).dropRight(1) else "`" + s + "`" + def escape(s: String) = s.replaceAllLiterally("[", "<").replaceAllLiterally("]", ">") + + println(""" +## Comparison of Scala Observable and Java Observable + +Note: +* This table contains both static methods and instance methods. +* If a signature is too long, move your mouse over it to get the full signature. + + +| Java Method | Scala Method | +|-------------|--------------|""") + + val ps = setTodoForMissingMethods(correspondence) + + (for (((javaName, scalaCol), pairs) <- ps.groupBy(groupingKey(_)).toList.sortBy(_._1._1)) yield { + "| " + formatJavaCol(javaName, pairs.map(_._1)) + " | " + formatScalaCol(scalaCol) + " |" + }).foreach(println(_)) + println(s"\nThis table was generated on ${Calendar.getInstance().getTime()}.") + println(s"**Do not edit**. Instead, edit `${getClass().getCanonicalName()}`.") + } + +} From 314387436a1bcf4de2a68904c9efb735bb16dda8 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 20:54:25 +0100 Subject: [PATCH 298/333] examples to examples, tests to tests --- .../rx/lang/scala/examples/RxScalaDemo.scala} | 25 +++--- .../scala/rx/lang/scala/ObservableTest.scala | 87 ++++++++++++++++++ .../lang/scala/examples/UnitTestSuite.scala | 88 ------------------- .../SubscriptionTests.scala | 3 +- 4 files changed, 103 insertions(+), 100 deletions(-) rename language-adaptors/rxjava-scala/src/{test/scala/rx/lang/scala/examples/RxJavaDemos.scala => examples/scala/rx/lang/scala/examples/RxScalaDemo.scala} (97%) create mode 100644 language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala delete mode 100644 language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/UnitTestSuite.scala rename language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/{examples => subscriptions}/SubscriptionTests.scala (94%) diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala similarity index 97% rename from language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala rename to language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala index bcb51fe94b..4a8ee56345 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala @@ -15,18 +15,23 @@ */ package rx.lang.scala.examples -import org.scalatest.junit.JUnitSuite -import rx.lang.scala._ -import scala.concurrent.duration._ +import java.io.IOException + +import scala.concurrent.duration.Duration +import scala.concurrent.duration.DurationInt +import scala.concurrent.duration.DurationLong +import scala.language.postfixOps + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Ignore import org.junit.Test -import org.junit.Assert._ +import org.scalatest.junit.JUnitSuite + +import rx.lang.scala.Notification +import rx.lang.scala.Observable +import rx.lang.scala.observable import rx.lang.scala.concurrency.Schedulers -import java.io.IOException -import rx.lang.scala.examples.Olympics -import rx.lang.scala.Notification.OnCompleted -import rx.lang.scala.Notification.OnError -import rx.lang.scala.Notification.OnNext -import org.scalatest.Ignore @Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala new file mode 100644 index 0000000000..ef9fbd90d9 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala @@ -0,0 +1,87 @@ +package rx.lang.scala + +import org.junit.{ Ignore, Assert, Test } +import org.scalatest.junit.JUnitSuite + +class ObservableTests extends JUnitSuite { + + // Tests which needn't be run: + + @Ignore + def testCovariance = { + //println("hey, you shouldn't run this test") + + val o1: Observable[Nothing] = Observable() + val o2: Observable[Int] = o1 + val o3: Observable[App] = o1 + val o4: Observable[Any] = o2 + val o5: Observable[Any] = o3 + } + + // Tests which have to be run: + + @Test + def testDematerialize() { + val o = Observable(1, 2, 3) + val mat = o.materialize + val demat = mat.dematerialize + + // correctly rejected: + //val wrongDemat = Observable("hello").dematerialize + + Assert.assertEquals(demat.toBlockingObservable.toIterable.toList, List(1, 2, 3)) + } + + // Test that Java's firstOrDefault propagates errors. + // If this changes (i.e. it suppresses errors and returns default) then Scala's firstOrElse + // should be changed accordingly. + @Test def testJavaFirstOrDefault() { + Assert.assertEquals(1, rx.Observable.from(1, 2).firstOrDefault(10).toBlockingObservable().single) + Assert.assertEquals(10, rx.Observable.empty().firstOrDefault(10).toBlockingObservable().single) + val msg = "msg6251" + var receivedMsg = "none" + try { + rx.Observable.error(new Exception(msg)).firstOrDefault(10).toBlockingObservable().single + } catch { + case e: Exception => receivedMsg = e.getCause().getMessage() + } + Assert.assertEquals(receivedMsg, msg) + } + + @Test def testFirstOrElse() { + def mustNotBeCalled: String = sys.error("this method should not be called") + def mustBeCalled: String = "this is the default value" + Assert.assertEquals("hello", Observable("hello").firstOrElse(mustNotBeCalled).toBlockingObservable.single) + Assert.assertEquals("this is the default value", Observable().firstOrElse(mustBeCalled).toBlockingObservable.single) + } + + @Test def testFirstOrElseWithError() { + val msg = "msg6251" + var receivedMsg = "none" + try { + Observable[Int](new Exception(msg)).firstOrElse(10).toBlockingObservable.single + } catch { + case e: Exception => receivedMsg = e.getCause().getMessage() + } + Assert.assertEquals(receivedMsg, msg) + } + + /* + @Test def testHead() { + val observer = mock(classOf[Observer[Int]]) + val o = Observable().head + val sub = o.subscribe(observer) + + verify(observer, never).onNext(any(classOf[Int])) + verify(observer, never).onCompleted() + verify(observer, times(1)).onError(any(classOf[NoSuchElementException])) + } + */ + + @Test def testTest() = { + val a: Observable[Int] = Observable() + Assert.assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) + //println("This UnitTestSuite.testTest() for rx.lang.scala.Observable") + } + +} diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/UnitTestSuite.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/UnitTestSuite.scala deleted file mode 100644 index 0e93aeb69e..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/UnitTestSuite.scala +++ /dev/null @@ -1,88 +0,0 @@ -package rx.lang.scala.examples - -import org.junit.{Ignore, Assert, Test} -import org.scalatest.junit.JUnitSuite -import rx.lang.scala.Observable - -class UnitTestSuite extends JUnitSuite { - - // Tests which needn't be run: - -@Ignore -def testCovariance = { - //println("hey, you shouldn't run this test") - - val o1: Observable[Nothing] = Observable() - val o2: Observable[Int] = o1 - val o3: Observable[App] = o1 - val o4: Observable[Any] = o2 - val o5: Observable[Any] = o3 -} - -// Tests which have to be run: - -@Test - def testDematerialize() { - val o = Observable(1, 2, 3) - val mat = o.materialize - val demat = mat.dematerialize - - // correctly rejected: - //val wrongDemat = Observable("hello").dematerialize - - Assert.assertEquals(demat.toBlockingObservable.toIterable.toList, List(1, 2, 3)) -} - -// Test that Java's firstOrDefault propagates errors. -// If this changes (i.e. it suppresses errors and returns default) then Scala's firstOrElse -// should be changed accordingly. -@Test def testJavaFirstOrDefault() { - Assert.assertEquals(1, rx.Observable.from(1, 2).firstOrDefault(10).toBlockingObservable().single) - Assert.assertEquals(10, rx.Observable.empty().firstOrDefault(10).toBlockingObservable().single) - val msg = "msg6251" - var receivedMsg = "none" - try { - rx.Observable.error(new Exception(msg)).firstOrDefault(10).toBlockingObservable().single - } catch { - case e: Exception => receivedMsg = e.getCause().getMessage() - } - Assert.assertEquals(receivedMsg, msg) -} - -@Test def testFirstOrElse() { - def mustNotBeCalled: String = sys.error("this method should not be called") - def mustBeCalled: String = "this is the default value" - Assert.assertEquals("hello", Observable("hello").firstOrElse(mustNotBeCalled).toBlockingObservable.single) - Assert.assertEquals("this is the default value", Observable().firstOrElse(mustBeCalled).toBlockingObservable.single) -} - -@Test def testFirstOrElseWithError() { - val msg = "msg6251" - var receivedMsg = "none" - try { - Observable[Int](new Exception(msg)).firstOrElse(10).toBlockingObservable.single - } catch { - case e: Exception => receivedMsg = e.getCause().getMessage() - } - Assert.assertEquals(receivedMsg, msg) -} - - /* - @Test def testHead() { - val observer = mock(classOf[Observer[Int]]) - val o = Observable().head - val sub = o.subscribe(observer) - - verify(observer, never).onNext(any(classOf[Int])) - verify(observer, never).onCompleted() - verify(observer, times(1)).onError(any(classOf[NoSuchElementException])) - } - */ - - @Test def testTest() = { - val a: Observable[Int] = Observable() - Assert.assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) - //println("This UnitTestSuite.testTest() for rx.lang.scala.Observable") - } - -} diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/SubscriptionTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala similarity index 94% rename from language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/SubscriptionTests.scala rename to language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala index aad6f22f95..04a0414645 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/SubscriptionTests.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala @@ -1,8 +1,7 @@ -package rx.lang.scala.examples +package rx.lang.scala.subscriptions import org.junit.{Assert, Test} import org.scalatest.junit.JUnitSuite -import rx.lang.scala.subscriptions.{MultipleAssignmentSubscription, CompositeSubscription, BooleanSubscription, Subscription} class SubscriptionTests extends JUnitSuite { From 01b677ec7d005cb7d04e008d201f5af19a911b3a Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 21:01:15 +0100 Subject: [PATCH 299/333] replace Assert.assertXxx by assertXxx --- .../scala/rx/lang/scala/ObservableTest.scala | 19 ++++--- .../subscriptions/SubscriptionTests.scala | 55 ++++++++++--------- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala index ef9fbd90d9..d96b23fe43 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala @@ -1,6 +1,7 @@ package rx.lang.scala -import org.junit.{ Ignore, Assert, Test } +import org.junit.Assert._ +import org.junit.{ Ignore, Test } import org.scalatest.junit.JUnitSuite class ObservableTests extends JUnitSuite { @@ -29,15 +30,15 @@ class ObservableTests extends JUnitSuite { // correctly rejected: //val wrongDemat = Observable("hello").dematerialize - Assert.assertEquals(demat.toBlockingObservable.toIterable.toList, List(1, 2, 3)) + assertEquals(demat.toBlockingObservable.toIterable.toList, List(1, 2, 3)) } // Test that Java's firstOrDefault propagates errors. // If this changes (i.e. it suppresses errors and returns default) then Scala's firstOrElse // should be changed accordingly. @Test def testJavaFirstOrDefault() { - Assert.assertEquals(1, rx.Observable.from(1, 2).firstOrDefault(10).toBlockingObservable().single) - Assert.assertEquals(10, rx.Observable.empty().firstOrDefault(10).toBlockingObservable().single) + assertEquals(1, rx.Observable.from(1, 2).firstOrDefault(10).toBlockingObservable().single) + assertEquals(10, rx.Observable.empty().firstOrDefault(10).toBlockingObservable().single) val msg = "msg6251" var receivedMsg = "none" try { @@ -45,14 +46,14 @@ class ObservableTests extends JUnitSuite { } catch { case e: Exception => receivedMsg = e.getCause().getMessage() } - Assert.assertEquals(receivedMsg, msg) + assertEquals(receivedMsg, msg) } @Test def testFirstOrElse() { def mustNotBeCalled: String = sys.error("this method should not be called") def mustBeCalled: String = "this is the default value" - Assert.assertEquals("hello", Observable("hello").firstOrElse(mustNotBeCalled).toBlockingObservable.single) - Assert.assertEquals("this is the default value", Observable().firstOrElse(mustBeCalled).toBlockingObservable.single) + assertEquals("hello", Observable("hello").firstOrElse(mustNotBeCalled).toBlockingObservable.single) + assertEquals("this is the default value", Observable().firstOrElse(mustBeCalled).toBlockingObservable.single) } @Test def testFirstOrElseWithError() { @@ -63,7 +64,7 @@ class ObservableTests extends JUnitSuite { } catch { case e: Exception => receivedMsg = e.getCause().getMessage() } - Assert.assertEquals(receivedMsg, msg) + assertEquals(receivedMsg, msg) } /* @@ -80,7 +81,7 @@ class ObservableTests extends JUnitSuite { @Test def testTest() = { val a: Observable[Int] = Observable() - Assert.assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) + assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) //println("This UnitTestSuite.testTest() for rx.lang.scala.Observable") } diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala index 04a0414645..42860f5791 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala @@ -1,6 +1,7 @@ package rx.lang.scala.subscriptions -import org.junit.{Assert, Test} +import org.junit.Assert._ +import org.junit.Test import org.scalatest.junit.JUnitSuite class SubscriptionTests extends JUnitSuite { @@ -8,16 +9,16 @@ class SubscriptionTests extends JUnitSuite { @Test def anonymousSubscriptionCreate() { val subscription = Subscription{} - Assert.assertNotNull(subscription) + assertNotNull(subscription) } @Test def anonymousSubscriptionDispose() { var unsubscribed = false val subscription = Subscription{ unsubscribed = true } - Assert.assertFalse(unsubscribed) + assertFalse(unsubscribed) subscription.unsubscribe() - Assert.assertTrue(unsubscribed) + assertTrue(unsubscribed) } @Test @@ -29,11 +30,11 @@ class SubscriptionTests extends JUnitSuite { @Test def booleanSubscription() { val subscription = BooleanSubscription() - Assert.assertFalse(subscription.isUnsubscribed) + assertFalse(subscription.isUnsubscribed) subscription.unsubscribe() - Assert.assertTrue(subscription.isUnsubscribed) + assertTrue(subscription.isUnsubscribed) subscription.unsubscribe() - Assert.assertTrue(subscription.isUnsubscribed) + assertTrue(subscription.isUnsubscribed) } @Test @@ -47,22 +48,22 @@ class SubscriptionTests extends JUnitSuite { val composite = CompositeSubscription() - Assert.assertFalse(composite.isUnsubscribed) + assertFalse(composite.isUnsubscribed) composite += s0 composite += s1 composite.unsubscribe() - Assert.assertTrue(composite.isUnsubscribed) - Assert.assertTrue(s0.isUnsubscribed) - Assert.assertTrue(u0) - Assert.assertTrue(u1) + assertTrue(composite.isUnsubscribed) + assertTrue(s0.isUnsubscribed) + assertTrue(u0) + assertTrue(u1) val s2 = BooleanSubscription() - Assert.assertFalse(s2.isUnsubscribed) + assertFalse(s2.isUnsubscribed) composite += s2 - Assert.assertTrue(s2.isUnsubscribed) + assertTrue(s2.isUnsubscribed) } @@ -73,13 +74,13 @@ class SubscriptionTests extends JUnitSuite { val composite = CompositeSubscription() composite += s0 - Assert.assertFalse(s0.isUnsubscribed) + assertFalse(s0.isUnsubscribed) composite -= s0 - Assert.assertTrue(s0.isUnsubscribed) + assertTrue(s0.isUnsubscribed) composite.unsubscribe() - Assert.assertTrue(composite.isUnsubscribed) + assertTrue(composite.isUnsubscribed) } @Test @@ -89,27 +90,27 @@ class SubscriptionTests extends JUnitSuite { val s1 = BooleanSubscription() val multiple = MultipleAssignmentSubscription() - Assert.assertFalse(multiple.isUnsubscribed) + assertFalse(multiple.isUnsubscribed) multiple.subscription = s0 - Assert.assertEquals(s0.asJavaSubscription, multiple.subscription.asJavaSubscription) + assertEquals(s0.asJavaSubscription, multiple.subscription.asJavaSubscription) multiple.subscription = s1 - Assert.assertEquals(s1.asJavaSubscription, multiple.subscription.asJavaSubscription) + assertEquals(s1.asJavaSubscription, multiple.subscription.asJavaSubscription) - Assert.assertFalse(s0.isUnsubscribed) - Assert.assertFalse(s1.isUnsubscribed) + assertFalse(s0.isUnsubscribed) + assertFalse(s1.isUnsubscribed) multiple.unsubscribe() - Assert.assertTrue(multiple.isUnsubscribed) - Assert.assertFalse(s0.isUnsubscribed) - Assert.assertTrue(s1.isUnsubscribed) + assertTrue(multiple.isUnsubscribed) + assertFalse(s0.isUnsubscribed) + assertTrue(s1.isUnsubscribed) val s2 = BooleanSubscription() - Assert.assertFalse(s2.isUnsubscribed) + assertFalse(s2.isUnsubscribed) multiple.subscription = s2 - Assert.assertTrue(s2.isUnsubscribed) + assertTrue(s2.isUnsubscribed) } } From 7d268b279814638ca0c262b2b50612c6fa55ecd9 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 21:15:55 +0100 Subject: [PATCH 300/333] move rx.lang.scala.subscriptions.Subscription to rx.lang.scala.Subscription (mirror Java package structure) --- .../scala/ImplicitFunctionConversions.scala | 3 - .../main/scala/rx/lang/scala/Scheduler.scala | 2 +- .../{subscriptions => }/Subscription.scala | 65 +++++++++---------- .../subscriptions/SubscriptionTests.scala | 2 + 4 files changed, 33 insertions(+), 39 deletions(-) rename language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/{subscriptions => }/Subscription.scala (50%) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index 458f5fedbc..6a64c858e8 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -20,11 +20,8 @@ import java.{ lang => jlang } import rx.lang.scala._ import rx.util.functions._ import scala.collection.Seq -import rx.lang.scala.subscriptions.Subscription import java.{lang => jlang} import scala.language.implicitConversions -import rx.lang.scala.Observer -import rx.lang.scala.Scheduler /** * These function conversions convert between Scala functions and Rx `Func`s and `Action`s. diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala index 8060dbaa36..73e3e95575 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala @@ -5,7 +5,7 @@ import scala.concurrent.duration.Duration import ImplicitFunctionConversions.scalaFunction0ProducingUnitToAction0 import ImplicitFunctionConversions.schedulerActionToFunc2 import rx.util.functions.{Action0, Action1, Func2} -import rx.lang.scala.subscriptions.Subscription +import rx.lang.scala.Subscription /** * Represents an object thatimport rx.lang.scala.ImplicitFunctionConversions diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/Subscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala similarity index 50% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/Subscription.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala index 8ccc29742c..3de9b8262b 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/Subscription.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala @@ -1,12 +1,12 @@ /** * Copyright 2013 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. @@ -14,56 +14,51 @@ * limitations under the License. */ -package rx.lang.scala { +package rx.lang.scala - /** +/** * Subscriptions are returned from all `Observable.subscribe` methods to allow unsubscribing. * * This interface is the equivalent of `IDisposable` in the .NET Rx implementation. */ - trait Subscription { +trait Subscription { - val asJavaSubscription: rx.Subscription + val asJavaSubscription: rx.Subscription - /** - * Call this method to stop receiving notifications on the Observer that was registered when - * this Subscription was received. - */ - def unsubscribe(): Unit = asJavaSubscription.unsubscribe() + /** + * Call this method to stop receiving notifications on the Observer that was registered when + * this Subscription was received. + */ + def unsubscribe(): Unit = asJavaSubscription.unsubscribe() - /** - * Checks if the subscription is unsubscribed. - */ - def isUnsubscribed: Boolean - } + /** + * Checks if the subscription is unsubscribed. + */ + def isUnsubscribed: Boolean } -package rx.lang.scala.subscriptions { - -import rx.lang.scala.Subscription -import java.util.concurrent.atomic.AtomicBoolean - - object Subscription { - + import java.util.concurrent.atomic.AtomicBoolean + import rx.lang.scala.subscriptions._ + /** - * Creates an [[rx.lang.scala.Subscription]] from an[[rx.Subscription]]. + * Creates an [[rx.lang.scala.Subscription]] from an [[rx.Subscription]]. */ def apply(subscription: rx.Subscription): Subscription = { - subscription match { - case x: rx.subscriptions.BooleanSubscription => new BooleanSubscription(x) - case x: rx.subscriptions.CompositeSubscription => new CompositeSubscription(x) - case x: rx.subscriptions.MultipleAssignmentSubscription => new MultipleAssignmentSubscription(x) - case x: rx.subscriptions.SerialSubscription => new SerialSubscription(x) - case x: rx.Subscription => Subscription { x.unsubscribe() } - } + subscription match { + case x: rx.subscriptions.BooleanSubscription => new BooleanSubscription(x) + case x: rx.subscriptions.CompositeSubscription => new CompositeSubscription(x) + case x: rx.subscriptions.MultipleAssignmentSubscription => new MultipleAssignmentSubscription(x) + case x: rx.subscriptions.SerialSubscription => new SerialSubscription(x) + case x: rx.Subscription => Subscription { x.unsubscribe() } + } } /** * Creates an [[rx.lang.scala.Subscription]] that invokes the specified action when unsubscribed. */ - def apply(u: => Unit): Subscription = { - new Subscription () { + def apply(u: => Unit): Subscription = { + new Subscription() { private val unsubscribed = new AtomicBoolean(false) def isUnsubscribed = unsubscribed.get() @@ -73,7 +68,7 @@ object Subscription { } } } - } + } diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala index 42860f5791..4309967c0a 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala @@ -4,6 +4,8 @@ import org.junit.Assert._ import org.junit.Test import org.scalatest.junit.JUnitSuite +import rx.lang.scala.Subscription + class SubscriptionTests extends JUnitSuite { @Test From 1b414617265d8cf0d0b75212df5a33260971b3ab Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Fri, 22 Nov 2013 12:23:20 -0800 Subject: [PATCH 301/333] Unit Tests and SuppressWarnings Added tests while validating pull request. This fixes issue https://github.com/Netflix/RxJava/issues/387 --- .../main/java/rx/operators/OperationZip.java | 9 ++ .../java/rx/operators/OperationZipTest.java | 107 ++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/rxjava-core/src/main/java/rx/operators/OperationZip.java b/rxjava-core/src/main/java/rx/operators/OperationZip.java index 64d4c28c80..4c7c70851f 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationZip.java +++ b/rxjava-core/src/main/java/rx/operators/OperationZip.java @@ -59,37 +59,45 @@ */ public final class OperationZip { + @SuppressWarnings("unchecked") public static OnSubscribeFunc zip(Observable o1, Observable o2, final Func2 zipFunction) { return zip(Arrays.asList(o1, o2), Functions.fromFunc(zipFunction)); } + @SuppressWarnings("unchecked") public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, final Func3 zipFunction) { return zip(Arrays.asList(o1, o2, o3), Functions.fromFunc(zipFunction)); } + @SuppressWarnings("unchecked") public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, final Func4 zipFunction) { return zip(Arrays.asList(o1, o2, o3, o4), Functions.fromFunc(zipFunction)); } + @SuppressWarnings("unchecked") public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, final Func5 zipFunction) { return zip(Arrays.asList(o1, o2, o3, o4, o5), Functions.fromFunc(zipFunction)); } + @SuppressWarnings("unchecked") public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, final Func6 zipFunction) { return zip(Arrays.asList(o1, o2, o3, o4, o5, o6), Functions.fromFunc(zipFunction)); } + @SuppressWarnings("unchecked") public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, final Func7 zipFunction) { return zip(Arrays.asList(o1, o2, o3, o4, o5, o6, o7), Functions.fromFunc(zipFunction)); } + @SuppressWarnings("unchecked") public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, final Func8 zipFunction) { return zip(Arrays.asList(o1, o2, o3, o4, o5, o6, o7, o8), Functions.fromFunc(zipFunction)); } + @SuppressWarnings("unchecked") public static OnSubscribeFunc zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9, final Func9 zipFunction) { return zip(Arrays.asList(o1, o2, o3, o4, o5, o6, o7, o8, o9), Functions.fromFunc(zipFunction)); @@ -415,6 +423,7 @@ public ItemObserver( this.observer = observer; this.cancel = cancel; } + @SuppressWarnings("unchecked") @Override public void onNext(T value) { rwLock.readLock().lock(); diff --git a/rxjava-core/src/test/java/rx/operators/OperationZipTest.java b/rxjava-core/src/test/java/rx/operators/OperationZipTest.java index 43cca02327..f3549a6fbd 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationZipTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationZipTest.java @@ -30,6 +30,7 @@ import rx.Subscription; import rx.operators.OperationZip.Aggregator; import rx.operators.OperationZip.ZipObserver; +import rx.subjects.PublishSubject; import rx.subscriptions.Subscriptions; import rx.util.functions.Func2; import rx.util.functions.Func3; @@ -494,6 +495,112 @@ public void testOnNextExceptionInvokesOnError() { verify(aObserver, times(1)).onError(any(Throwable.class)); } + + @Test + public void testOnFirstCompletion() { + PublishSubject oA = PublishSubject.create(); + PublishSubject oB = PublishSubject.create(); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + + Observable o = Observable.create(zip(oA, oB, getConcat2Strings())); + o.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + oA.onNext("a1"); + inOrder.verify(observer, never()).onNext(anyString()); + oB.onNext("b1"); + inOrder.verify(observer, times(1)).onNext("a1-b1"); + oB.onNext("b2"); + inOrder.verify(observer, never()).onNext(anyString()); + oA.onNext("a2"); + inOrder.verify(observer, times(1)).onNext("a2-b2"); + + oA.onNext("a3"); + oA.onNext("a4"); + oA.onNext("a5"); + oA.onCompleted(); + + // SHOULD ONCOMPLETE BE EMITTED HERE INSTEAD OF WAITING + // FOR B3, B4, B5 TO BE EMITTED? + + oB.onNext("b3"); + oB.onNext("b4"); + oB.onNext("b5"); + + inOrder.verify(observer, times(1)).onNext("a3-b3"); + inOrder.verify(observer, times(1)).onNext("a4-b4"); + inOrder.verify(observer, times(1)).onNext("a5-b5"); + + // WE RECEIVE THE ONCOMPLETE HERE + inOrder.verify(observer, times(1)).onCompleted(); + + oB.onNext("b6"); + oB.onNext("b7"); + oB.onNext("b8"); + oB.onNext("b9"); + // never completes (infinite stream for example) + + // we should receive nothing else despite oB continuing after oA completed + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testOnErrorTermination() { + PublishSubject oA = PublishSubject.create(); + PublishSubject oB = PublishSubject.create(); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + + Observable o = Observable.create(zip(oA, oB, getConcat2Strings())); + o.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + oA.onNext("a1"); + inOrder.verify(observer, never()).onNext(anyString()); + oB.onNext("b1"); + inOrder.verify(observer, times(1)).onNext("a1-b1"); + oB.onNext("b2"); + inOrder.verify(observer, never()).onNext(anyString()); + oA.onNext("a2"); + inOrder.verify(observer, times(1)).onNext("a2-b2"); + + oA.onNext("a3"); + oA.onNext("a4"); + oA.onNext("a5"); + oA.onError(new RuntimeException("forced failure")); + + // it should emit failure immediately + inOrder.verify(observer, times(1)).onError(any(RuntimeException.class)); + + oB.onNext("b3"); + oB.onNext("b4"); + oB.onNext("b5"); + oB.onNext("b6"); + oB.onNext("b7"); + oB.onNext("b8"); + oB.onNext("b9"); + // never completes (infinite stream for example) + + // we should receive nothing else despite oB continuing after oA completed + inOrder.verifyNoMoreInteractions(); + } + + private Func2 getConcat2Strings() { + return new Func2() { + + @Override + public String call(String t1, String t2) { + return t1 + "-" + t2; + } + }; + } + + private Func2 getDivideZipr() { Func2 zipr = new Func2() { From 00198acc1bc2f6ed2fae6451d6df6e9a4f6b3bc8 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 21:30:58 +0100 Subject: [PATCH 302/333] reactivate Observable.withFilter --- .../src/main/scala/rx/lang/scala/Observable.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 0d8cbb7166..6f55a7d7a9 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -1820,9 +1820,9 @@ trait Observable[+T] Observable[java.lang.Boolean](asJavaObservable.isEmpty).map(_.booleanValue()) } - //def withFilter(p: T => Boolean): WithFilter[T] = { - // new WithFilter[T](p, asJava) - //} + def withFilter(p: T => Boolean): WithFilter[T] = { + new WithFilter[T](p, asJavaObservable) + } } From 8567fcbaff20859c68ce4c30c7f347be34581fa4 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 22:04:48 +0100 Subject: [PATCH 303/333] replace `work{ t1 }` by `work{ t1.call() }` --- .../rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala index 73e3e95575..f535d7a4d0 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala @@ -163,7 +163,7 @@ trait Scheduler { def scheduleRec(work: (=>Unit)=>Unit): Subscription = { Subscription(asJavaScheduler.schedule(new Action1[Action0] { def call(t1: Action0){ - work{ t1 } + work{ t1.call() } } })) //action1[action0] From 11903af341845b4bd8725c56059c3df2c6b1d85c Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 22:14:31 +0100 Subject: [PATCH 304/333] do not import things from the same package --- .../src/main/scala/rx/lang/scala/Observable.scala | 6 ------ .../src/main/scala/rx/lang/scala/Scheduler.scala | 1 - .../src/main/scala/rx/lang/scala/WithFilter.scala | 2 -- .../src/main/scala/rx/lang/scala/subjects/Subject.scala | 2 -- 4 files changed, 11 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index 6f55a7d7a9..68f7530dec 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -16,14 +16,8 @@ package rx.lang.scala - import rx.util.functions.FuncN import rx.Observable.OnSubscribeFunc -import rx.lang.scala.Notification -import rx.lang.scala.ImplicitFunctionConversions -import rx.lang.scala.Observer -import rx.lang.scala.Scheduler - /** * The Observable interface that implements the Reactive Pattern. diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala index f535d7a4d0..2142b3d402 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala @@ -5,7 +5,6 @@ import scala.concurrent.duration.Duration import ImplicitFunctionConversions.scalaFunction0ProducingUnitToAction0 import ImplicitFunctionConversions.schedulerActionToFunc2 import rx.util.functions.{Action0, Action1, Func2} -import rx.lang.scala.Subscription /** * Represents an object thatimport rx.lang.scala.ImplicitFunctionConversions diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala index 61b25b8784..d8524825ba 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala @@ -1,7 +1,5 @@ package rx.lang.scala -import rx.lang.scala.ImplicitFunctionConversions - import ImplicitFunctionConversions.scalaBooleanFunction1ToRxBooleanFunc1 import ImplicitFunctionConversions.scalaFunction1ToRxFunc1 diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala index 5631b1bdea..cb92df90d9 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala @@ -1,7 +1,5 @@ package rx.lang.scala -import rx.lang.scala.Observer - /** * A Subject is an Observable and an Observer at the same time. */ From 46d4855a34eb2b935de12e3837b9beef33fb1a66 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 22:21:14 +0100 Subject: [PATCH 305/333] update TODO --- language-adaptors/rxjava-scala/TODO.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/language-adaptors/rxjava-scala/TODO.md b/language-adaptors/rxjava-scala/TODO.md index 60169c9a81..f8965cc498 100644 --- a/language-adaptors/rxjava-scala/TODO.md +++ b/language-adaptors/rxjava-scala/TODO.md @@ -8,9 +8,7 @@ TODOs which came up at the meeting with Erik Meijer on 2013-10-11: * Rename the factory methods in `object Observable`, considering that the most important is the one taking an `Observer => Subscription` function (the "king" according to Erik). Thunk to Subscription conversion (?), also consider Jason's [comments](https://github.com/Netflix/RxJava/commit/c1596253fc5567b7cc37d20128374d189471ff79). A useful trick might also be to have `apply(T, T, T*)` instead of just `apply(T*)`. * Factory methods for observables and instance methods should take implicit scheduler, default is different per method (Isn't this a contradiction? In other words: If I call a method without providing a scheduler, should the default scheduler be used or the implicit that I provided?) Find in .NET source the list of which schedulers goes with which operators by default. If no other default, use NewThreadScheduler. Note that there are methods in Scala Observable which should have an overload taking a Scheduler, but do not yet have it! Also remember Erik saying that he would like to "minimize magically injected concurrency". -* Bring `BooleanSubscription`, `CompositeSubscription`, `MultipleAssignmentSubscription` to Scala, `compositeSubscription.subscription = ...`instead of setter method, add on `CompositeSubscription` should be `+=` * Convert executor to scheduler -* Java Scheduler methods take `state` arguments (they were needed to schedule work on a different machine, but are now considered a bad idea). Remove these `state` arguments from all Scala schedulers. * Check if TestScheduler added in 0.14.3 is sufficient * Infinite Iterables: the java Observable.from version unfolds an iterable, even it is infinite. Should be fixed in java. * subscribe methods: There are many overloads, but still not all combinations one might need. Make this nicer and complete, maybe using default arguments. Also try to make sure that `subscribe(println)` works, not only `subscribe(println(_))`. `foreach(println)` works on collections, but not on `subscribe(println)`, because subscribe is overloaded. @@ -18,9 +16,6 @@ TODOs which came up at the meeting with Erik Meijer on 2013-10-11: * There are no examples yet using `async`, but `async` will be used in the course. Write examples and check if everything works as expected when combined with `async`. * Futures: For the moment, just add a Future->Observable converter method to `object Observable`. Later, think if `Future[T] extends Observable[T]`. * Operator `delay`: Once Erik has commented on [this](https://github.com/Netflix/RxJava/pull/384), make sure this operator is added accordingly to RxJava and then to RxScala -* add wrappers or aliases for `AsyncSubject`, `BehaviorSubject`, `PublishSubject`, and `ReplaySubject` -* go through Erik's code that he showed at the meeting and check if everything can now be done nicely -* get Erik's slides from the course and check if they are compatible with the library Some more TODOs: From a0133fb4179c32e5bf9d88820ef6d0b4d4104094 Mon Sep 17 00:00:00 2001 From: samuelgruetter Date: Fri, 22 Nov 2013 22:32:52 +0100 Subject: [PATCH 306/333] fix one scaladoc link --- .../lang/scala/subscriptions/MultiAssignmentSubscription.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala index 1cdd485d37..7955766810 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala @@ -24,7 +24,7 @@ object MultipleAssignmentSubscription { /** - * Represents a [[rx.lang.scala.subscriptions.Subscription]] whose underlying subscription can be swapped for another subscription. + * Represents a [[rx.lang.scala.Subscription]] whose underlying subscription can be swapped for another subscription. */ class MultipleAssignmentSubscription private[scala] (val asJavaSubscription: rx.subscriptions.MultipleAssignmentSubscription) extends Subscription { From 0b5484632e2687461000ee28990da5e98a533d1a Mon Sep 17 00:00:00 2001 From: DavidMGross Date: Fri, 22 Nov 2013 13:45:06 -0800 Subject: [PATCH 307/333] Update Observable.java standardizing javadoc comments & adding wiki/image links for new methods --- rxjava-core/src/main/java/rx/Observable.java | 916 ++++++++++++++++--- 1 file changed, 766 insertions(+), 150 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index b9ae791382..18c267cc1e 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -26,6 +26,8 @@ import java.util.concurrent.TimeUnit; import rx.concurrency.Schedulers; +import rx.joins.Pattern2; +import rx.joins.Plan0; import rx.observables.BlockingObservable; import rx.observables.ConnectableObservable; import rx.observables.GroupedObservable; @@ -51,6 +53,7 @@ import rx.operators.OperationFirstOrDefault; import rx.operators.OperationGroupBy; import rx.operators.OperationInterval; +import rx.operators.OperationJoinPatterns; import rx.operators.OperationLast; import rx.operators.OperationMap; import rx.operators.OperationMaterialize; @@ -64,6 +67,7 @@ import rx.operators.OperationOnErrorReturn; import rx.operators.OperationOnExceptionResumeNextViaObservable; import rx.operators.OperationParallel; +import rx.operators.OperationParallelMerge; import rx.operators.OperationRetry; import rx.operators.OperationSample; import rx.operators.OperationScan; @@ -129,7 +133,7 @@ * The documentation for this interface makes use of marble diagrams. The * following legend explains these diagrams: *

    - * + * *

    * For more information see the * RxJava Wiki @@ -302,6 +306,33 @@ private Subscription protectivelyWrapAndSubscribe(Observer o) { return subscription.wrap(subscribe(new SafeObserver(subscription, o))); } + /** + * Subscribe and ignore all events. + * + * @return + */ + public Subscription subscribe() { + return protectivelyWrapAndSubscribe(new Observer() { + + @Override + public void onCompleted() { + // do nothing + } + + @Override + public void onError(Throwable e) { + handleError(e); + throw new OnErrorNotImplementedException(e); + } + + @Override + public void onNext(T args) { + // do nothing + } + + }); + } + /** * An {@link Observer} must call an Observable's {@code subscribe} method * in order to receive items and notifications from the Observable. @@ -477,6 +508,7 @@ public Subscription subscribe(final Action1 onNext, final Action1Observable.publish() and Observable.multicast() */ public ConnectableObservable multicast(Subject subject) { return OperationMulticast.multicast(this, subject); @@ -546,7 +578,7 @@ public Subscription onSubscribe(Observer observer) { * Creates an Observable that will execute the given function when an * {@link Observer} subscribes to it. *

    - * + * *

    * Write the function you pass to create so that it behaves as * an Observable: It should invoke the Observer's @@ -567,6 +599,7 @@ public Subscription onSubscribe(Observer observer) { * allow the Observer to cancel the subscription * @return an Observable that, when an {@link Observer} subscribes to it, * will execute the given function + * @see create() */ public static Observable create(OnSubscribeFunc func) { return new Observable(func); @@ -582,6 +615,7 @@ public static Observable create(OnSubscribeFunc func) { * @return an Observable that returns no data to the {@link Observer} and * immediately invokes the {@link Observer}'s * {@link Observer#onCompleted() onCompleted} method + * @see empty() * @see MSDN: Observable.Empty Method */ public static Observable empty() { @@ -602,6 +636,7 @@ public static Observable empty() { * immediately invokes the {@link Observer}'s * {@link Observer#onCompleted() onCompleted} method with the * specified scheduler + * @see empty() * @see MSDN: Observable.Empty Method (IScheduler) */ public static Observable empty(Scheduler scheduler) { @@ -620,6 +655,7 @@ public static Observable empty(Scheduler scheduler) { * @return an Observable that invokes the {@link Observer}'s * {@link Observer#onError onError} method when the Observer * subscribes to it + * @see error() * @see MSDN: Observable.Throw Method */ public static Observable error(Throwable exception) { @@ -639,6 +675,7 @@ public static Observable error(Throwable exception) { * @return an Observable that invokes the {@link Observer}'s * {@link Observer#onError onError} method with the specified * scheduler + * @see error() * @see MSDN: Observable.Throw Method */ public static Observable error(Throwable exception, Scheduler scheduler) { @@ -648,7 +685,7 @@ public static Observable error(Throwable exception, Scheduler scheduler) /** * Converts an {@link Iterable} sequence into an Observable. *

    - * + * *

    * Note: the entire iterable sequence is immediately emitted each time an * {@link Observer} subscribes. Since this occurs before the @@ -660,15 +697,34 @@ public static Observable error(Throwable exception, Scheduler scheduler) * type of items to be emitted by the resulting Observable * @return an Observable that emits each item in the source {@link Iterable} * sequence + * @see from() */ public static Observable from(Iterable iterable) { return create(OperationToObservableIterable.toObservableIterable(iterable)); } + /** + * Converts an {@link Iterable} sequence into an Observable with the specified scheduler. + *

    + * + * + * @param iterable the source {@link Iterable} sequence + * @param scheduler the scheduler to emit the items of the iterable + * @param the type of items in the {@link Iterable} sequence and the + * type of items to be emitted by the resulting Observable + * @return an Observable that emits each item in the source {@link Iterable} + * sequence with the specified scheduler + * @see from() + * @see MSDN: Observable.ToObservable + */ + public static Observable from(Iterable iterable, Scheduler scheduler) { + return from(iterable).observeOn(scheduler); + } + /** * Converts an Array into an Observable. *

    - * + * *

    * Note: the entire array is immediately emitted each time an * {@link Observer} subscribes. Since this occurs before the @@ -679,6 +735,7 @@ public static Observable from(Iterable iterable) { * @param the type of items in the Array and the type of items to be * emitted by the resulting Observable * @return an Observable that emits each item in the source Array + * @see from() */ public static Observable from(T[] items) { return create(OperationToObservableIterable.toObservableIterable(Arrays.asList(items))); @@ -687,7 +744,7 @@ public static Observable from(T[] items) { /** * Converts an item into an Observable that emits that item. *

    - * + * *

    * Note: the item is immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -698,6 +755,7 @@ public static Observable from(T[] items) { * @param the type of the item, and the type of the item to be * emitted by the resulting Observable * @return an Observable that emits the item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -708,7 +766,7 @@ public static Observable from(T t1) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -720,6 +778,7 @@ public static Observable from(T t1) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -730,7 +789,7 @@ public static Observable from(T t1, T t2) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -743,6 +802,7 @@ public static Observable from(T t1, T t2) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -753,7 +813,7 @@ public static Observable from(T t1, T t2, T t3) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -767,6 +827,7 @@ public static Observable from(T t1, T t2, T t3) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -777,7 +838,7 @@ public static Observable from(T t1, T t2, T t3, T t4) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -792,6 +853,7 @@ public static Observable from(T t1, T t2, T t3, T t4) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -802,7 +864,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -818,6 +880,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -828,7 +891,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -845,6 +908,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -855,7 +919,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -873,6 +937,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -883,7 +948,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -902,6 +967,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -912,7 +978,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T /** * Converts a series of items into an Observable. *

    - * + * *

    * Note: the items will be immediately emitted each time an {@link Observer} * subscribes. Since this occurs before the {@link Subscription} is @@ -932,6 +998,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item + * @see from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -943,7 +1010,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * Generates an Observable that emits a sequence of integers within a * specified range. *

    - * + * *

    * Note: the entire range is immediately emitted each time an * {@link Observer} subscribes. Since this occurs before the @@ -953,18 +1020,35 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * @param start the value of the first integer in the sequence * @param count the number of sequential integers to generate * @return an Observable that emits a range of sequential integers + * @see range() * @see Observable.Range Method (Int32, Int32) */ public static Observable range(int start, int count) { return from(Range.createWithCount(start, count)); } + /** + * Generates an Observable that emits a sequence of integers within a + * specified range with the specified scheduler. + *

    + * + * @param start the value of the first integer in the sequence + * @param count the number of sequential integers to generate + * @param scheduler the scheduler to run the generator loop on + * @return an Observable that emits a range of sequential integers + * @see range() + * @see Observable.Range Method (Int32, Int32, IScheduler) + */ + public static Observable range(int start, int count, Scheduler scheduler) { + return range(start, count).observeOn(scheduler); + } + /** * Returns an Observable that calls an Observable factory to create its * Observable for each new Observer that subscribes. That is, for each * subscriber, the actuall Observable is determined by the factory function. *

    - * + * *

    * The defer operator allows you to defer or delay emitting items from an * Observable until such time as an Observer subscribes to the Observable. @@ -977,6 +1061,7 @@ public static Observable range(int start, int count) { * @param the type of the items emitted by the Observable * @return an Observable whose {@link Observer}s trigger an invocation of * the given Observable factory function + * @see defer() */ public static Observable defer(Func0> observableFactory) { return create(OperationDefer.defer(observableFactory)); @@ -1000,6 +1085,7 @@ public static Observable defer(Func0> o * {@link Observer#onNext onNext} method * @param the type of that item * @return an Observable that emits a single item and then completes + * @see just() */ public static Observable just(T value) { List list = new ArrayList(); @@ -1020,6 +1106,7 @@ public static Observable just(T value) { * @param scheduler the scheduler to send the single element on * @return an Observable that emits a single item and then completes on a * specified scheduler + * @see just() */ public static Observable just(T value, Scheduler scheduler) { return just(value).observeOn(scheduler); @@ -1038,6 +1125,7 @@ public static Observable just(T value, Scheduler scheduler) { * @return an Observable that emits items that are the result of flattening * the items emitted by the Observables emitted by the * {@code source} Observable + * @see merge() * @see MSDN: Observable.Merge Method */ public static Observable merge(Observable> source) { @@ -1057,6 +1145,7 @@ public static Observable merge(Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1079,6 +1168,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1102,6 +1192,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1126,6 +1217,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1151,6 +1243,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1177,6 +1270,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1204,6 +1298,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1232,6 +1327,7 @@ public static Observable merge(Observable t1, Observablemerge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1250,6 +1346,7 @@ public static Observable merge(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ public static Observable concat(Observable> observables) { @@ -1267,6 +1364,7 @@ public static Observable concat(Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1288,6 +1386,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1309,6 +1408,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1331,6 +1431,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1354,6 +1455,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1378,6 +1480,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1403,6 +1506,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1429,6 +1533,7 @@ public static Observable concat(Observable t1, Observableconcat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1458,6 +1563,7 @@ public static Observable concat(Observable t1, ObservablemergeDelayError() * @see MSDN: Observable.Merge Method */ public static Observable mergeDelayError(Observable> source) { @@ -1485,6 +1591,7 @@ public static Observable mergeDelayError(ObservablemergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1515,6 +1622,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t3 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1547,6 +1655,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t4 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1579,6 +1688,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t5 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1612,6 +1722,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t6 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1646,6 +1757,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t7 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1681,6 +1793,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t8 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1717,6 +1830,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t9 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables + * @see mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1729,13 +1843,14 @@ public static Observable mergeDelayError(Observable t1, Obse * Returns an Observable that never sends any items or notifications to an * {@link Observer}. *

    - * + * *

    * This Observable is useful primarily for testing purposes. * * @param the type of items (not) emitted by the Observable * @return an Observable that never sends any items or notifications to an * {@link Observer} + * @see never() */ public static Observable never() { return new NeverObservable(); @@ -1746,11 +1861,12 @@ public static Observable never() { * that emits the items emitted by the most recently published of those * Observables. *

    - * + * * * @param sequenceOfSequences the source Observable that emits Observables * @return an Observable that emits only the items emitted by the most * recently published Observable + * @see switchOnNext() * @deprecated use {@link #switchOnNext} */ @Deprecated @@ -1763,11 +1879,12 @@ public static Observable switchDo(Observable - * + * * * @param sequenceOfSequences the source Observable that emits Observables * @return an Observable that emits only the items emitted by the most * recently published Observable + * @see switchOnNext() */ public static Observable switchOnNext(Observable> sequenceOfSequences) { return create(OperationSwitch.switchDo(sequenceOfSequences)); @@ -1777,7 +1894,7 @@ public static Observable switchOnNext(Observable - * + * *

    * A well-behaved Observable does not interleave its invocations of the * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, @@ -1791,6 +1908,7 @@ public static Observable switchOnNext(Observablesynchronize() */ public Observable synchronize() { return create(OperationSynchronize.synchronize(this)); @@ -1802,7 +1920,7 @@ public Observable synchronize() { * accomplished by acquiring a mutual-exclusion lock for the object * provided as the lock parameter. *

    - * + * *

    * A well-behaved Observable does not interleave its invocations of the * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, @@ -1817,6 +1935,7 @@ public Observable synchronize() { * @return an Observable that is a chronologically well-behaved version of * the source Observable, and that synchronously notifies its * {@link Observer}s + * @see synchronize() */ public Observable synchronize(Object lock) { return create(OperationSynchronize.synchronize(this, lock)); @@ -1833,12 +1952,13 @@ public static Observable synchronize(Observable source) { /** * Emits an item each time interval (containing a sequential number). *

    - * + * * * @param interval interval size in time units (see below) * @param unit time units to use for the interval size * @return an Observable that emits an item each time interval - * @see MSDN: Observable.Interval + * @see interval() + * @see MSDN: Observable.Interval */ public static Observable interval(long interval, TimeUnit unit) { return create(OperationInterval.interval(interval, unit)); @@ -1847,13 +1967,14 @@ public static Observable interval(long interval, TimeUnit unit) { /** * Emits an item each time interval (containing a sequential number). *

    - * + * * * @param interval interval size in time units (see below) * @param unit time units to use for the interval size * @param scheduler the scheduler to use for scheduling the items * @return an Observable that emits an item each time interval - * @see MSDN: Observable.Interval + * @see interval() + * @see MSDN: Observable.Interval */ public static Observable interval(long interval, TimeUnit unit, Scheduler scheduler) { return create(OperationInterval.interval(interval, unit, scheduler)); @@ -1866,7 +1987,7 @@ public static Observable interval(long interval, TimeUnit unit, Scheduler * Note: If events keep firing faster than the timeout then no data will be * emitted. *

    - * + * *

    * Information on debounce vs throttle: *

    @@ -1881,6 +2002,7 @@ public static Observable interval(long interval, TimeUnit unit, Scheduler * @param unit the {@link TimeUnit} for the timeout * @return an {@link Observable} that filters out items that are too * quickly followed by newer items + * @see debounce() * @see #throttleWithTimeout(long, TimeUnit) */ public Observable debounce(long timeout, TimeUnit unit) { @@ -1894,7 +2016,7 @@ public Observable debounce(long timeout, TimeUnit unit) { * Note: If events keep firing faster than the timeout then no data will be * emitted. *

    - * + * *

    * Information on debounce vs throttle: *

    @@ -1911,6 +2033,7 @@ public Observable debounce(long timeout, TimeUnit unit) { * timers that handle the timeout for each event * @return an {@link Observable} that filters out items that are too * quickly followed by newer items + * @see debounce() * @see #throttleWithTimeout(long, TimeUnit, Scheduler) */ public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) { @@ -1924,7 +2047,7 @@ public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) * Note: If events keep firing faster than the timeout then no data will be * emitted. *

    - * + * *

    * Information on debounce vs throttle: *

    @@ -1939,6 +2062,7 @@ public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) * @param unit the {@link TimeUnit} for the timeout * @return an {@link Observable} that filters out items that are too * quickly followed by newer items + * @see throttleWithTimeout() * @see #debounce(long, TimeUnit) */ public Observable throttleWithTimeout(long timeout, TimeUnit unit) { @@ -1952,7 +2076,7 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit) { * Note: If events keep firing faster than the timeout then no data will be * emitted. *

    - * + * *

    * Information on debounce vs throttle: *

    @@ -1969,6 +2093,7 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit) { * timers that handle the timeout for each event * @return an {@link Observable} that filters out items that are too * quickly followed by newer items + * @see throttleWithTimeout() * @see #debounce(long, TimeUnit, Scheduler) */ public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler scheduler) { @@ -1982,12 +2107,13 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler * This differs from {@link #throttleLast} in that this only tracks passage * of time whereas {@link #throttleLast} ticks at scheduled intervals. *

    - * + * * * @param windowDuration time to wait before sending another item after * emitting the last item * @param unit the unit of time for the specified timeout * @return an Observable that performs the throttle operation + * @see throttleFirst() */ public Observable throttleFirst(long windowDuration, TimeUnit unit) { return create(OperationThrottleFirst.throttleFirst(this, windowDuration, unit)); @@ -2000,7 +2126,7 @@ public Observable throttleFirst(long windowDuration, TimeUnit unit) { * This differs from {@link #throttleLast} in that this only tracks passage * of time whereas {@link #throttleLast} ticks at scheduled intervals. *

    - * + * * * @param skipDuration time to wait before sending another item after * emitting the last item @@ -2008,6 +2134,7 @@ public Observable throttleFirst(long windowDuration, TimeUnit unit) { * @param scheduler the {@link Scheduler} to use internally to manage the * timers that handle timeout for each event * @return an Observable that performs the throttle operation + * @see throttleFirst() */ public Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler scheduler) { return create(OperationThrottleFirst.throttleFirst(this, skipDuration, unit, scheduler)); @@ -2021,12 +2148,13 @@ public Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler s * scheduled interval whereas {@link #throttleFirst} does not tick, it just * tracks passage of time. *

    - * + * * * @param intervalDuration duration of windows within which the last item * will be emitted * @param unit the unit of time for the specified interval * @return an Observable that performs the throttle operation + * @see throttleLast() * @see #sample(long, TimeUnit) */ public Observable throttleLast(long intervalDuration, TimeUnit unit) { @@ -2041,12 +2169,13 @@ public Observable throttleLast(long intervalDuration, TimeUnit unit) { * scheduled interval whereas {@link #throttleFirst} does not tick, it just * tracks passage of time. *

    - * + * * * @param intervalDuration duration of windows within which the last item * will be emitted * @param unit the unit of time for the specified interval * @return an Observable that performs the throttle operation + * @see throttleLast() * @see #sample(long, TimeUnit, Scheduler) */ public Observable throttleLast(long intervalDuration, TimeUnit unit, Scheduler scheduler) { @@ -2057,10 +2186,11 @@ public Observable throttleLast(long intervalDuration, TimeUnit unit, Schedule * Wraps each item emitted by a source Observable in a {@link Timestamped} * object. *

    - * + * * * @return an Observable that emits timestamped items from the source * Observable + * @see timestamp() */ public Observable> timestamp() { return create(OperationTimestamp.timestamp(this)); @@ -2069,7 +2199,7 @@ public Observable> timestamp() { /** * Converts a {@link Future} into an Observable. *

    - * + * *

    * You can convert any object that supports the {@link Future} interface * into an Observable that emits the return value of the {@link Future#get} @@ -2083,6 +2213,7 @@ public Observable> timestamp() { * @param the type of object that the {@link Future} returns, and also * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source Future + * @see from() */ public static Observable from(Future future) { return create(OperationToObservableFuture.toObservableFuture(future)); @@ -2091,7 +2222,7 @@ public static Observable from(Future future) { /** * Converts a {@link Future} into an Observable. *

    - * + * *

    * You can convert any object that supports the {@link Future} interface * into an Observable that emits the return value of the {@link Future#get} @@ -2106,6 +2237,7 @@ public static Observable from(Future future) { * @param the type of object that the {@link Future} returns, and also * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source Future + * @see from() */ public static Observable from(Future future, Scheduler scheduler) { return create(OperationToObservableFuture.toObservableFuture(future)).subscribeOn(scheduler); @@ -2114,7 +2246,7 @@ public static Observable from(Future future, Scheduler sched /** * Converts a {@link Future} into an Observable with timeout. *

    - * + * *

    * You can convert any object that supports the {@link Future} interface * into an Observable that emits the return value of the {link Future#get} @@ -2130,6 +2262,7 @@ public static Observable from(Future future, Scheduler sched * @param the type of object that the {@link Future} returns, and also * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source {@link Future} + * @see from() */ public static Observable from(Future future, long timeout, TimeUnit unit) { return create(OperationToObservableFuture.toObservableFuture(future, timeout, unit)); @@ -2139,13 +2272,14 @@ public static Observable from(Future future, long timeout, T * Returns an Observable that emits Boolean values that indicate whether the * pairs of items emitted by two source Observables are equal. *

    - * + * * * @param first the first Observable to compare * @param second the second Observable to compare * @param the type of items emitted by each Observable * @return an Observable that emits Booleans that indicate whether the * corresponding items emitted by the source Observables are equal + * @see sequenceEqual() */ public static Observable sequenceEqual(Observable first, Observable second) { return sequenceEqual(first, second, new Func2() { @@ -2161,7 +2295,7 @@ public Boolean call(T first, T second) { * pairs of items emitted by two source Observables are equal based on the * results of a specified equality function. *

    - * + * * * @param first the first Observable to compare * @param second the second Observable to compare @@ -2170,6 +2304,7 @@ public Boolean call(T first, T second) { * @param the type of items emitted by each Observable * @return an Observable that emits Booleans that indicate whether the * corresponding items emitted by the source Observables are equal + * @see sequenceEqual() */ public static Observable sequenceEqual(Observable first, Observable second, Func2 equality) { return zip(first, second, equality); @@ -2200,6 +2335,7 @@ public static Observable sequenceEqual(Observable firs * each of the source Observables, results in an item that will * be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable o1, Observable o2, Func2 zipFunction) { return create(OperationZip.zip(o1, o2, zipFunction)); @@ -2232,6 +2368,7 @@ public static Observable zip(Observable o1, Observa * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Func3 zipFunction) { return create(OperationZip.zip(o1, o2, o3, zipFunction)); @@ -2265,6 +2402,7 @@ public static Observable zip(Observable o1, Obs * each of the source Observables, results in an item that will * be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Func4 zipFunction) { return create(OperationZip.zip(o1, o2, o3, o4, zipFunction)); @@ -2299,6 +2437,7 @@ public static Observable zip(Observable o1, * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 zipFunction) { return create(OperationZip.zip(o1, o2, o3, o4, o5, zipFunction)); @@ -2332,6 +2471,7 @@ public static Observable zip(Observable * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Func6 zipFunction) { @@ -2367,6 +2507,7 @@ public static Observable zip(Observablezip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Func7 zipFunction) { @@ -2403,6 +2544,7 @@ public static Observable zip(Observablezip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Func8 zipFunction) { @@ -2440,6 +2582,7 @@ public static Observable zip(Observablezip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9, Func9 zipFunction) { @@ -2460,6 +2603,7 @@ public static Observable zip(Observab * source observable values * @return an Observable that combines the source Observables with the * given combine function + * @see combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Func2 combineFunction) { return create(OperationCombineLatest.combineLatest(o1, o2, combineFunction)); @@ -2480,6 +2624,7 @@ public static Observable combineLatest(Observable o * source observable values * @return an Observable that combines the source Observables with the * given combine function + * @see combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Func3 combineFunction) { return create(OperationCombineLatest.combineLatest(o1, o2, o3, combineFunction)); @@ -2501,6 +2646,7 @@ public static Observable combineLatest(ObservablecombineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Func4 combineFunction) { @@ -2524,6 +2670,7 @@ public static Observable combineLatest(ObservablecombineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 combineFunction) { @@ -2548,6 +2695,7 @@ public static Observable combineLatest(ObservablecombineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Func6 combineFunction) { @@ -2573,6 +2721,7 @@ public static Observable combineLatest(Observable * source observable values * @return an Observable that combines the source Observables with the * given combine function + * @see combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Func7 combineFunction) { @@ -2599,6 +2748,7 @@ public static Observable combineLatest(Observ * source observable values * @return an Observable that combines the source Observables with the * given combine function + * @see combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Func8 combineFunction) { @@ -2626,6 +2776,7 @@ public static Observable combineLatest(Ob * source observable values * @return an Observable that combines the source Observables with the * given combine function + * @see combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9, Func9 combineFunction) { @@ -2635,7 +2786,7 @@ public static Observable combineLates /** * Creates an Observable that produces buffers of collected items. *

    - * + * *

    * This Observable produces connected, non-overlapping buffers. The current * buffer is emitted and replaced with a new buffer when the Observable @@ -2654,6 +2805,7 @@ public static Observable combineLates * buffers, which are emitted when the current {@link Observable} * created with the {@link Func0} argument produces a * {@link rx.util.Closing} object + * @see buffer() */ public Observable> buffer(Func0> bufferClosingSelector) { return create(OperationBuffer.buffer(this, bufferClosingSelector)); @@ -2662,7 +2814,7 @@ public Observable> buffer(Func0> /** * Creates an Observable which produces buffers of collected values. *

    - * + * *

    * This Observable produces buffers. Buffers are created when the specified * bufferOpenings Observable produces a {@link rx.util.Opening} @@ -2682,6 +2834,7 @@ public Observable> buffer(Func0> * @return an {@link Observable} that produces buffers that are created and * emitted when the specified {@link Observable}s publish certain * objects + * @see buffer() */ public Observable> buffer(Observable bufferOpenings, Func1> bufferClosingSelector) { return create(OperationBuffer.buffer(this, bufferOpenings, bufferClosingSelector)); @@ -2690,7 +2843,7 @@ public Observable> buffer(Observable bufferOpenings, /** * Creates an Observable that produces buffers of collected items. *

    - * + * *

    * This Observable produces connected, non-overlapping buffers, each * containing count items. When the source Observable completes @@ -2700,6 +2853,7 @@ public Observable> buffer(Observable bufferOpenings, * @param count the maximum size of each buffer before it should be emitted * @return an {@link Observable} that produces connected, non-overlapping * buffers containing at most "count" items + * @see buffer() */ public Observable> buffer(int count) { return create(OperationBuffer.buffer(this, count)); @@ -2708,7 +2862,7 @@ public Observable> buffer(int count) { /** * Creates an Observable which produces buffers of collected items. *

    - * + * *

    * This Observable produces buffers every skip items, each * containing count items. When the source Observable @@ -2723,6 +2877,7 @@ public Observable> buffer(int count) { * @return an {@link Observable} that produces buffers every * skip item containing at most count * items + * @see buffer() */ public Observable> buffer(int count, int skip) { return create(OperationBuffer.buffer(this, count, skip)); @@ -2731,7 +2886,7 @@ public Observable> buffer(int count, int skip) { /** * Creates an Observable that produces buffers of collected values. *

    - * + * *

    * This Observable produces connected, non-overlapping buffers, each of a * fixed duration specified by the timespan argument. When the @@ -2744,6 +2899,7 @@ public Observable> buffer(int count, int skip) { * argument * @return an {@link Observable} that produces connected, non-overlapping * buffers with a fixed duration + * @see buffer() */ public Observable> buffer(long timespan, TimeUnit unit) { return create(OperationBuffer.buffer(this, timespan, unit)); @@ -2752,7 +2908,7 @@ public Observable> buffer(long timespan, TimeUnit unit) { /** * Creates an Observable that produces buffers of collected values. *

    - * + * *

    * This Observable produces connected, non-overlapping buffers, each of a * fixed duration specified by the timespan argument. When the @@ -2767,6 +2923,7 @@ public Observable> buffer(long timespan, TimeUnit unit) { * and start of a buffer * @return an {@link Observable} that produces connected, non-overlapping * buffers with a fixed duration + * @see buffer() */ public Observable> buffer(long timespan, TimeUnit unit, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, unit, scheduler)); @@ -2780,7 +2937,7 @@ public Observable> buffer(long timespan, TimeUnit unit, Scheduler schedu * first). When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each buffer collects values before it * should be emitted and replaced with a new buffer @@ -2790,6 +2947,7 @@ public Observable> buffer(long timespan, TimeUnit unit, Scheduler schedu * @return an {@link Observable} that produces connected, non-overlapping * buffers that are emitted after a fixed duration or when the * buffer reaches maximum capacity (whichever occurs first) + * @see buffer() */ public Observable> buffer(long timespan, TimeUnit unit, int count) { return create(OperationBuffer.buffer(this, timespan, unit, count)); @@ -2803,7 +2961,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count) { * first). When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each buffer collects values before it * should be emitted and replaced with a new buffer @@ -2815,6 +2973,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count) { * @return an {@link Observable} that produces connected, non-overlapping * buffers that are emitted after a fixed duration or when the * buffer has reached maximum capacity (whichever occurs first) + * @see buffer() */ public Observable> buffer(long timespan, TimeUnit unit, int count, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, unit, count, scheduler)); @@ -2828,7 +2987,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count, Sched * source Observable completes or encounters an error, the current buffer is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each buffer collects values before it * should be emitted @@ -2838,6 +2997,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count, Sched * and timeshift arguments * @return an {@link Observable} that produces new buffers periodically and * emits these after a fixed timespan has elapsed. + * @see buffer() */ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) { return create(OperationBuffer.buffer(this, timespan, timeshift, unit)); @@ -2851,7 +3011,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) * source Observable completes or encounters an error, the current buffer is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each buffer collects values before it * should be emitted @@ -2863,6 +3023,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) * and start of a buffer * @return an {@link Observable} that produces new buffers periodically and * emits these after a fixed timespan has elapsed + * @see buffer() */ public Observable> buffer(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, timeshift, unit, scheduler)); @@ -2877,7 +3038,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit, * then be used to create a new Observable to listen for the end of the next * window. *

    - * + * * * @param closingSelector the {@link Func0} used to produce an * {@link Observable} for every window created. When this @@ -2887,6 +3048,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit, * windows, which are emitted when the current {@link Observable} * created with the closingSelector argument emits a * {@link rx.util.Closing} object. + * @see window() */ public Observable> window(Func0> closingSelector) { return create(OperationWindow.window(this, closingSelector)); @@ -2900,7 +3062,7 @@ public Observable> window(Func0 - * + * * * @param windowOpenings the {@link Observable} that, when it produces a * {@link rx.util.Opening} object, causes another @@ -2913,6 +3075,7 @@ public Observable> window(Func0window() */ public Observable> window(Observable windowOpenings, Func1> closingSelector) { return create(OperationWindow.window(this, windowOpenings, closingSelector)); @@ -2925,11 +3088,12 @@ public Observable> window(Observable windowOpen * encounters an error, the current window is emitted, and the event is * propagated. *

    - * + * * * @param count the maximum size of each window before it should be emitted * @return an {@link Observable} that produces connected, non-overlapping * windows containing at most count items + * @see window() */ public Observable> window(int count) { return create(OperationWindow.window(this, count)); @@ -2942,7 +3106,7 @@ public Observable> window(int count) { * completes or encounters an error, the current window is emitted and the * event is propagated. *

    - * + * * * @param count the maximum size of each window before it should be emitted * @param skip how many items need to be skipped before starting a new @@ -2950,6 +3114,7 @@ public Observable> window(int count) { * are equal this is the same operation as {@link #window(int)}. * @return an {@link Observable} that produces windows every "skipped" * items containing at most count items + * @see window() */ public Observable> window(int count, int skip) { return create(OperationWindow.window(this, count, skip)); @@ -2962,7 +3127,7 @@ public Observable> window(int count, int skip) { * Observable completes or encounters an error, the current window is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects items before it * should be emitted and replaced with a new window @@ -2970,6 +3135,7 @@ public Observable> window(int count, int skip) { * argument * @return an {@link Observable} that produces connected, non-overlapping * windows with a fixed duration + * @see window() */ public Observable> window(long timespan, TimeUnit unit) { return create(OperationWindow.window(this, timespan, unit)); @@ -2982,7 +3148,7 @@ public Observable> window(long timespan, TimeUnit unit) { * source Observable completes or encounters an error, the current window is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects items before it * should be emitted and replaced with a new window @@ -2992,6 +3158,7 @@ public Observable> window(long timespan, TimeUnit unit) { * and start of a window * @return an {@link Observable} that produces connected, non-overlapping * windows with a fixed duration + * @see window() */ public Observable> window(long timespan, TimeUnit unit, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, unit, scheduler)); @@ -3005,7 +3172,7 @@ public Observable> window(long timespan, TimeUnit unit, Scheduler * reached first). When the source Observable completes or encounters an * error, the current window is emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects values before it * should be emitted and replaced with a new window @@ -3015,6 +3182,7 @@ public Observable> window(long timespan, TimeUnit unit, Scheduler * @return an {@link Observable} that produces connected, non-overlapping * windows that are emitted after a fixed duration or when the * window has reached maximum capacity (whichever occurs first) + * @see window() */ public Observable> window(long timespan, TimeUnit unit, int count) { return create(OperationWindow.window(this, timespan, unit, count)); @@ -3028,7 +3196,7 @@ public Observable> window(long timespan, TimeUnit unit, int count) * first). When the source Observable completes or encounters an error, the * current window is emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects values before it * should be emitted and replaced with a new window @@ -3040,6 +3208,7 @@ public Observable> window(long timespan, TimeUnit unit, int count) * @return an {@link Observable} that produces connected non-overlapping * windows that are emitted after a fixed duration or when the * window has reached maximum capacity (whichever occurs first). + * @see window() */ public Observable> window(long timespan, TimeUnit unit, int count, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, unit, count, scheduler)); @@ -3053,7 +3222,7 @@ public Observable> window(long timespan, TimeUnit unit, int count, * source Observable completes or encounters an error, the current window is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects values before it * should be emitted @@ -3063,6 +3232,7 @@ public Observable> window(long timespan, TimeUnit unit, int count, * and timeshift arguments * @return an {@link Observable} that produces new windows periodically and * emits these after a fixed timespan has elapsed + * @see window() */ public Observable> window(long timespan, long timeshift, TimeUnit unit) { return create(OperationWindow.window(this, timespan, timeshift, unit)); @@ -3076,7 +3246,7 @@ public Observable> window(long timespan, long timeshift, TimeUnit * source Observable completes or encounters an error, the current window is * emitted and the event is propagated. *

    - * + * * * @param timespan the period of time each window collects values before it * should be emitted @@ -3088,6 +3258,7 @@ public Observable> window(long timespan, long timeshift, TimeUnit * and start of a window * @return an {@link Observable} that produces new windows periodically and * emits these after a fixed timespan has elapsed + * @see window() */ public Observable> window(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, timeshift, unit, scheduler)); @@ -3116,6 +3287,7 @@ public Observable> window(long timespan, long timeshift, TimeUnit * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Observable> ws, final FuncN zipFunction) { return ws.toList().mapMany(new Func1>, Observable>() { @@ -3149,6 +3321,7 @@ public Observable call(List> wsList) { * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results + * @see zip() */ public static Observable zip(Iterable> ws, FuncN zipFunction) { return create(OperationZip.zip(ws, zipFunction)); @@ -3164,6 +3337,7 @@ public static Observable zip(Iterable> ws, FuncN< * the filter * @return an Observable that emits only those items in the original * Observable that the filter evaluates as {@code true} + * @see filter() */ public Observable filter(Func1 predicate) { return create(OperationFilter.filter(this, predicate)); @@ -3176,7 +3350,8 @@ public Observable filter(Func1 predicate) { * * * @return an Observable of sequentially distinct items - * @see MSDN: Observable.distinctUntilChanged + * @see distinctUntilChanged() + * @see MSDN: Observable.distinctUntilChanged */ public Observable distinctUntilChanged() { return create(OperationDistinctUntilChanged.distinctUntilChanged(this)); @@ -3193,7 +3368,8 @@ public Observable distinctUntilChanged() { * value that is used for deciding whether an item is * sequentially distinct from another one or not * @return an Observable of sequentially distinct items - * @see MSDN: Observable.distinctUntilChanged + * @see distinctUntilChanged() + * @see MSDN: Observable.distinctUntilChanged */ public Observable distinctUntilChanged(Func1 keySelector) { return create(OperationDistinctUntilChanged.distinctUntilChanged(this, keySelector)); @@ -3206,7 +3382,8 @@ public Observable distinctUntilChanged(Func1 keyS * * * @return an Observable of distinct items - * @see MSDN: Observable.distinct + * @see distinct() + * @see MSDN: Observable.distinct */ public Observable distinct() { return create(OperationDistinct.distinct(this)); @@ -3222,7 +3399,8 @@ public Observable distinct() { * value that is used to decide whether an item is * distinct from another one or not * @return an Observable that emits distinct items - * @see MSDN: Observable.distinct + * @see distinct() + * @see MSDN: Observable.distinct */ public Observable distinct(Func1 keySelector) { return create(OperationDistinct.distinct(this, keySelector)); @@ -3240,6 +3418,7 @@ public Observable distinct(Func1 keySelector) { * or equal to the number of elements in * the source sequence * @throws IndexOutOfBoundsException if index is less than 0 + * @see elementAt() */ public Observable elementAt(int index) { return create(OperationElementAt.elementAt(this, index)); @@ -3257,6 +3436,7 @@ public Observable elementAt(int index) { * the source sequence, or the default item if the index is outside * the bounds of the source sequence * @throws IndexOutOfBoundsException if index is less than 0 + * @see elementAtOrDefault() */ public Observable elementAtOrDefault(int index, T defaultValue) { return create(OperationElementAt.elementAtOrDefault(this, index, defaultValue)); @@ -3275,6 +3455,7 @@ public Observable elementAtOrDefault(int index, T defaultValue) { * * @param predicate the condition to test every element * @return a subscription function for creating the target Observable + * @see exists() * @see MSDN: Observable.Any Note: the description in this page is wrong. */ public Observable exists(Func1 predicate) { @@ -3289,6 +3470,7 @@ public Observable exists(Func1 predicate) { * @param element the item to search in the sequence * @return an Observable that emits true if the item is in the * source sequence + * @see contains() * @see MSDN: Observable.Contains */ public Observable contains(final T element) { @@ -3304,12 +3486,13 @@ public Boolean call(T t1) { * {@link Observer#onCompleted onCompleted} or * {@link Observer#onError onError}. *

    - * + * * * @param action an {@link Action0} to be invoked when the source * Observable finishes * @return an Observable that emits the same items as the source Observable, * then invokes the {@link Action0} + * @see finallyDo() * @see MSDN: Observable.Finally Method */ public Observable finallyDo(Action0 action) { @@ -3332,6 +3515,7 @@ public Observable finallyDo(Action0 action) { * transformation function to each item emitted by the source * Observable and merging the results of the Observables obtained * from this transformation. + * @see flatMap() * @see #mapMany(Func1) */ public Observable flatMap(Func1> func) { @@ -3348,6 +3532,7 @@ public Observable flatMap(Func1where() * @see #filter(Func1) */ public Observable where(Func1 predicate) { @@ -3363,7 +3548,8 @@ public Observable where(Func1 predicate) { * @param func a function to apply to each item emitted by the Observable * @return an Observable that emits the items from the source Observable, * transformed by the given function - * @see MSDN: Observable.Select + * @see map() + * @see MSDN: Observable.Select */ public Observable map(Func1 func) { return create(OperationMap.map(this, func)); @@ -3380,7 +3566,8 @@ public Observable map(Func1 func) { * additional parameter. * @return an Observable that emits the items from the source Observable, * transformed by the given function - * @see MSDN: Observable.Select + * @see mapWithIndex() + * @see MSDN: Observable.Select */ public Observable mapWithIndex(Func2 func) { return create(OperationMap.mapWithIndex(this, func)); @@ -3402,6 +3589,7 @@ public Observable mapWithIndex(Func2 fun * transformation function to each item emitted by the source * Observable and merging the results of the Observables obtained * from this transformation. + * @see mapMany() * @see #flatMap(Func1) */ public Observable mapMany(Func1> func) { @@ -3417,6 +3605,7 @@ public Observable mapMany(Func1materialize() * @see MSDN: Observable.materialize */ public Observable> materialize() { @@ -3427,12 +3616,13 @@ public Observable> materialize() { * Asynchronously subscribes and unsubscribes Observers on the specified * {@link Scheduler}. *

    - * + * * * @param scheduler the {@link Scheduler} to perform subscription and * unsubscription actions on * @return the source Observable modified so that its subscriptions and * unsubscriptions happen on the specified {@link Scheduler} + * @see subscribeOn() */ public Observable subscribeOn(Scheduler scheduler) { return create(OperationSubscribeOn.subscribeOn(this, scheduler)); @@ -3442,11 +3632,12 @@ public Observable subscribeOn(Scheduler scheduler) { * Asynchronously notify {@link Observer}s on the specified * {@link Scheduler}. *

    - * + * * * @param scheduler the {@link Scheduler} to notify {@link Observer}s on * @return the source Observable modified so that its {@link Observer}s are * notified on the specified {@link Scheduler} + * @see observeOn() */ public Observable observeOn(Scheduler scheduler) { return create(OperationObserveOn.observeOn(this, scheduler)); @@ -3458,13 +3649,14 @@ public Observable observeOn(Scheduler scheduler) { * objects emitted by the source Observable into the items or notifications * they represent. *

    - * + * * * @return an Observable that emits the items and notifications embedded in * the {@link Notification} objects emitted by the source Observable - * @see MSDN: Observable.dematerialize * @throws Throwable if the source Observable is not of type - * {@code Observable>}. + * {@code Observable>} + * @see dematerialize() + * @see MSDN: Observable.dematerialize */ @SuppressWarnings("unchecked") public Observable dematerialize() { @@ -3498,6 +3690,7 @@ public Observable dematerialize() { * take over if the source Observable encounters an * error * @return the original Observable, with appropriately modified behavior + * @see onErrorResumeNext() */ public Observable onErrorResumeNext(final Func1> resumeFunction) { return create(OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction(this, resumeFunction)); @@ -3530,6 +3723,7 @@ public Observable onErrorResumeNext(final Func1onErrorResumeNext() */ public Observable onErrorResumeNext(final Observable resumeSequence) { return create(OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable(this, resumeSequence)); @@ -3567,6 +3761,7 @@ public Observable onErrorResumeNext(final Observable resumeSeque * take over if the source Observable encounters an * error * @return the original Observable, with appropriately modified behavior + * @see onExceptionResumeNextViaObservable() */ public Observable onExceptionResumeNext(final Observable resumeSequence) { return create(OperationOnExceptionResumeNextViaObservable.onExceptionResumeNextViaObservable(this, resumeSequence)); @@ -3577,7 +3772,7 @@ public Observable onExceptionResumeNext(final Observable resumeS * rather than invoking {@link Observer#onError onError} if it encounters an * error. *

    - * + * *

    * By default, when an Observable encounters an error that prevents it from * emitting the expected item to its {@link Observer}, the Observable @@ -3597,6 +3792,7 @@ public Observable onExceptionResumeNext(final Observable resumeS * Observable will emit if the source Observable * encounters an error * @return the original Observable with appropriately modified behavior + * @see onErrorReturn() */ public Observable onErrorReturn(Func1 resumeFunction) { return create(OperationOnErrorReturn.onErrorReturn(this, resumeFunction)); @@ -3623,7 +3819,8 @@ public Observable onErrorReturn(Func1 resumeFunction) * @return an Observable that emits a single item that is the result of * accumulating the output from the source Observable * @throws IllegalArgumentException if the Observable sequence is empty - * @see MSDN: Observable.Aggregate + * @see reduce() + * @see MSDN: Observable.Aggregate * @see Wikipedia: Fold (higher-order function) */ public Observable reduce(Func2 accumulator) { @@ -3643,7 +3840,8 @@ public Observable reduce(Func2 accumulator) { * * @return an Observable that emits the number of counted elements of the * source Observable as its single item - * @see MSDN: Observable.Count + * @see count() + * @see MSDN: Observable.Count */ public Observable count() { return reduce(0, new Func2() { @@ -3663,7 +3861,8 @@ public Integer call(Integer t1, T t2) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see MSDN: Observable.Sum + * @see sum() + * @see MSDN: Observable.Sum */ public static Observable sum(Observable source) { return OperationSum.sum(source); @@ -3678,7 +3877,8 @@ public static Observable sum(Observable source) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see MSDN: Observable.Sum + * @see sumLongs() + * @see MSDN: Observable.Sum */ public static Observable sumLongs(Observable source) { return OperationSum.sumLongs(source); @@ -3693,7 +3893,8 @@ public static Observable sumLongs(Observable source) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see MSDN: Observable.Sum + * @see sumFloats() + * @see MSDN: Observable.Sum */ public static Observable sumFloats(Observable source) { return OperationSum.sumFloats(source); @@ -3708,7 +3909,8 @@ public static Observable sumFloats(Observable source) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see MSDN: Observable.Sum + * @see sumDoubles() + * @see MSDN: Observable.Sum */ public static Observable sumDoubles(Observable source) { return OperationSum.sumDoubles(source); @@ -3724,7 +3926,8 @@ public static Observable sumDoubles(Observable source) { * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item * @throws IllegalArgumentException if the Observable sequence is empty - * @see MSDN: Observable.Average + * @see average() + * @see MSDN: Observable.Average */ public static Observable average(Observable source) { return OperationAverage.average(source); @@ -3739,7 +3942,8 @@ public static Observable average(Observable source) { * @param source source observable to compute the average of * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item - * @see MSDN: Observable.Average + * @see averageLongs() + * @see MSDN: Observable.Average */ public static Observable averageLongs(Observable source) { return OperationAverage.averageLongs(source); @@ -3754,7 +3958,8 @@ public static Observable averageLongs(Observable source) { * @param source source observable to compute the average of * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item - * @see MSDN: Observable.Average + * @see averageFloats() + * @see MSDN: Observable.Average */ public static Observable averageFloats(Observable source) { return OperationAverage.averageFloats(source); @@ -3769,7 +3974,8 @@ public static Observable averageFloats(Observable source) { * @param source source observable to compute the average of * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item - * @see MSDN: Observable.Average + * @see averageDoubles() + * @see MSDN: Observable.Average */ public static Observable averageDoubles(Observable source) { return OperationAverage.averageDoubles(source); @@ -3786,7 +3992,7 @@ public static Observable averageDoubles(Observable source) { * @throws IllegalArgumentException if the source is empty * @see MSDN: Observable.Min */ - public static > Observable min(Observable source) { + public static > Observable min(Observable source) { return OperationMinMax.min(source); } @@ -3801,9 +4007,10 @@ public static > Observable min(Observable source) * @return an Observable that emits the minimum value according to the * specified comparator * @throws IllegalArgumentException if the source is empty + * @see min() * @see MSDN: Observable.Min */ - public Observable min(Comparator comparator) { + public Observable min(Comparator comparator) { return OperationMinMax.min(this, comparator); } @@ -3817,9 +4024,10 @@ public Observable min(Comparator comparator) { * @param selector the key selector function * @return an Observable that emits a List of the items with the minimum key * value + * @see minBy() * @see MSDN: Observable.MinBy */ - public > Observable> minBy(Func1 selector) { + public > Observable> minBy(Func1 selector) { return OperationMinMax.minBy(this, selector); } @@ -3834,9 +4042,10 @@ public > Observable> minBy(Func1 selector) * @param comparator the comparator used to compare key values * @return an Observable that emits a List of the elements with the minimum * key value according to the specified comparator + * @see minBy() * @see MSDN: Observable.MinBy */ - public Observable> minBy(Func1 selector, Comparator comparator) { + public Observable> minBy(Func1 selector, Comparator comparator) { return OperationMinMax.minBy(this, selector, comparator); } @@ -3849,9 +4058,10 @@ public Observable> minBy(Func1 selector, Comparator compara * @param source an Observable to determine the maximum item of * @return an Observable that emits the maximum element * @throws IllegalArgumentException if the source is empty + * @see max() * @see MSDN: Observable.Max */ - public static > Observable max(Observable source) { + public static > Observable max(Observable source) { return OperationMinMax.max(source); } @@ -3866,9 +4076,10 @@ public static > Observable max(Observable source) * @return an Observable that emits the maximum item according to the * specified comparator * @throws IllegalArgumentException if the source is empty + * @see max() * @see MSDN: Observable.Max */ - public Observable max(Comparator comparator) { + public Observable max(Comparator comparator) { return OperationMinMax.max(this, comparator); } @@ -3881,9 +4092,10 @@ public Observable max(Comparator comparator) { * @param selector the key selector function * @return an Observable that emits a List of the items with the maximum key * value + * @see maxBy() * @see MSDN: Observable.MaxBy */ - public > Observable> maxBy(Func1 selector) { + public > Observable> maxBy(Func1 selector) { return OperationMinMax.maxBy(this, selector); } @@ -3898,9 +4110,10 @@ public > Observable> maxBy(Func1 selector) * @param comparator the comparator used to compare key values * @return an Observable that emits a List of the elements with the maximum * key value according to the specified comparator + * @see maxBy() * @see MSDN: Observable.MaxBy */ - public Observable> maxBy(Func1 selector, Comparator comparator) { + public Observable> maxBy(Func1 selector, Comparator comparator) { return OperationMinMax.maxBy(this, selector, comparator); } @@ -3913,6 +4126,7 @@ public Observable> maxBy(Func1 selector, Comparator compara * * @return a {@link ConnectableObservable} that upon connection causes the * source Observable to emit items to its {@link Observer}s + * @see replay() */ public ConnectableObservable replay() { return OperationMulticast.multicast(this, ReplaySubject. create()); @@ -3935,6 +4149,7 @@ public ConnectableObservable replay() { * * @param retryCount number of retry attempts before failing * @return an Observable with retry logic + * @see retry() */ public Observable retry(int retryCount) { return create(OperationRetry.retry(this, retryCount)); @@ -3957,6 +4172,7 @@ public Observable retry(int retryCount) { * output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. * * @return an Observable with retry logic + * @see retry() */ public Observable retry() { return create(OperationRetry.retry(this)); @@ -3967,7 +4183,7 @@ public Observable retry() { * auto-subscribes to the source Observable rather than returning a * {@link ConnectableObservable}. *

    - * + * *

    * This is useful when you want an Observable to cache responses and you * can't control the subscribe/unsubscribe behavior of all the @@ -3980,6 +4196,7 @@ public Observable retry() { * * @return an Observable that, when first subscribed to, caches all of its * notifications for the benefit of subsequent subscribers. + * @see cache() */ public Observable cache() { return create(OperationCache.cache(this)); @@ -3997,6 +4214,7 @@ public Observable cache() { * {@code Observable} * @return an Observable with the output of the {@link Func1} executed on a * {@link Scheduler} + * @see parallel() */ public Observable parallel(Func1, Observable> f) { return OperationParallel.parallel(this, f); @@ -4014,22 +4232,76 @@ public Observable parallel(Func1, Observable> f) { * @param s a {@link Scheduler} to perform the work on * @return an Observable with the output of the {@link Func1} executed on a * {@link Scheduler} + * @see parallel() */ public Observable parallel(final Func1, Observable> f, final Scheduler s) { return OperationParallel.parallel(this, f, s); } + + /** + * Merges an Observable<Observable<T>> to + * Observable<Observable<T>> with the number of + * inner Observables defined by parallelObservables. + *

    + * For example, if the original + * Observable<Observable<T>> has 100 Observables to + * be emitted and parallelObservables is 8, the 100 will be + * grouped onto 8 output Observables. + *

    + * This is a mechanism for efficiently processing n number of + * Observables on a smaller m number of resources (typically CPU + * cores). + *

    + * + * + * @param parallelObservables the number of Observables to merge into + * @return an Observable of Observables constrained to number defined by + * parallelObservables + * @see parallelMerge() + */ + public static Observable> parallelMerge(Observable> source, int parallelObservables) { + return OperationParallelMerge.parallelMerge(source, parallelObservables); + } + + /** + * Merges an Observable<Observable<T>> to + * Observable<Observable<T>> with the number of + * inner Observables defined by parallelObservables and runs + * each Observable on the defined Scheduler. + *

    + * For example, if the original + * Observable<Observable<T>> has 100 Observables to + * be emitted and parallelObservables is 8, the 100 will be + * grouped onto 8 output Observables. + *

    + * This is a mechanism for efficiently processing n number of + * Observables on a smaller m number of resources (typically CPU + * cores). + *

    + * + * + * @param parallelObservables the number of Observables to merge into + * @return an Observable of Observables constrained to number defined by + * parallelObservables. + * @see parallelMerge() + */ + public static Observable> parallelMerge(Observable> source, int parallelObservables, Scheduler scheduler) { + return OperationParallelMerge.parallelMerge(source, parallelObservables, scheduler); + } + /** * Returns a {@link ConnectableObservable}, which waits until its * {@link ConnectableObservable#connect connect} method is called before it * begins emitting items to those {@link Observer}s that have subscribed to * it. *

    - * + * * * @return a {@link ConnectableObservable} that upon connection causes the * source Observable to emit items to its {@link Observer}s + * @see publish() */ public ConnectableObservable publish() { return OperationMulticast.multicast(this, PublishSubject. create()); @@ -4039,9 +4311,10 @@ public ConnectableObservable publish() { * Returns a {@link ConnectableObservable} that shares a single subscription * that contains the last notification only. *

    - * + * * * @return a {@link ConnectableObservable} + * @see publishLast() */ public ConnectableObservable publishLast() { return OperationMulticast.multicast(this, AsyncSubject. create()); @@ -4051,7 +4324,8 @@ public ConnectableObservable publishLast() { * Synonymous with reduce(). *

    * - * + * + * @see aggregate() * @see #reduce(Func2) */ public Observable aggregate(Func2 accumulator) { @@ -4080,6 +4354,7 @@ public Observable aggregate(Func2 accumulator) { * @return an Observable that emits a single item that is the result of * accumulating the output from the items emitted by the source * Observable + * @see reduce() * @see MSDN: Observable.Aggregate * @see Wikipedia: Fold (higher-order function) */ @@ -4092,6 +4367,7 @@ public Observable reduce(R initialValue, Func2 accumulat *

    * * + * @see aggregate() * @see #reduce(Object, Func2) */ public Observable aggregate(R initialValue, Func2 accumulator) { @@ -4119,7 +4395,8 @@ public Observable aggregate(R initialValue, Func2 accumu * accumulator call * @return an Observable that emits the results of each call to the * accumulator function - * @see MSDN: Observable.Scan + * @see scan() + * @see MSDN: Observable.Scan */ public Observable scan(Func2 accumulator) { return create(OperationScan.scan(this, accumulator)); @@ -4129,12 +4406,13 @@ public Observable scan(Func2 accumulator) { * Returns an Observable that emits the results of sampling the items * emitted by the source Observable at a specified time interval. *

    - * + * * * @param period the sampling rate * @param unit the {@link TimeUnit} in which period is defined * @return an Observable that emits the results of sampling the items * emitted by the source Observable at the specified time interval + * @see sample() */ public Observable sample(long period, TimeUnit unit) { return create(OperationSample.sample(this, period, unit)); @@ -4144,13 +4422,14 @@ public Observable sample(long period, TimeUnit unit) { * Returns an Observable that emits the results of sampling the items * emitted by the source Observable at a specified time interval. *

    - * + * * * @param period the sampling rate * @param unit the {@link TimeUnit} in which period is defined * @param scheduler the {@link Scheduler} to use when sampling * @return an Observable that emits the results of sampling the items * emitted by the source Observable at the specified time interval + * @see sample() */ public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { return create(OperationSample.sample(this, period, unit, scheduler)); @@ -4178,7 +4457,8 @@ public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { * accumulator call * @return an Observable that emits the results of each call to the * accumulator function - * @see MSDN: Observable.Scan + * @see scan() + * @see MSDN: Observable.Scan */ public Observable scan(R initialValue, Func2 accumulator) { return create(OperationScan.scan(this, initialValue, accumulator)); @@ -4188,12 +4468,13 @@ public Observable scan(R initialValue, Func2 accumulator * Returns an Observable that emits a Boolean that indicates whether all of * the items emitted by the source Observable satisfy a condition. *

    - * + * * * @param predicate a function that evaluates an item and returns a Boolean * @return an Observable that emits true if all items emitted * by the source Observable satisfy the predicate; otherwise, * false + * @see all() */ public Observable all(Func1 predicate) { return create(OperationAll.all(this, predicate)); @@ -4213,6 +4494,7 @@ public Observable all(Func1 predicate) { * @return an Observable that is identical to the source Observable except * that it does not emit the first num items that the * source emits + * @see skip() */ public Observable skip(int num) { return create(OperationSkip.skip(this, num)); @@ -4226,8 +4508,9 @@ public Observable skip(int num) { * * @return an Observable that emits only the very first item from the * source, or none if the source Observable completes without - * emitting a single item. - * @see MSDN: Observable.First + * emitting a single item + * @see first() + * @see MSDN: Observable.First */ public Observable first() { return take(1); @@ -4242,8 +4525,9 @@ public Observable first() { * @param predicate the condition any source emitted item has to satisfy * @return an Observable that emits only the very first item satisfying the * given condition from the source, or none if the source Observable - * completes without emitting a single matching item. - * @see MSDN: Observable.First + * completes without emitting a single matching item + * @see first() + * @see MSDN: Observable.First */ public Observable first(Func1 predicate) { return skipWhile(not(predicate)).take(1); @@ -4260,7 +4544,8 @@ public Observable first(Func1 predicate) { * @return an Observable that emits only the very first item from the * source, or a default value if the source Observable completes * without emitting a single item - * @see MSDN: Observable.FirstOrDefault + * @see firstOrDefault() + * @see MSDN: Observable.FirstOrDefault */ public Observable firstOrDefault(T defaultValue) { return create(OperationFirstOrDefault.firstOrDefault(this, defaultValue)); @@ -4278,7 +4563,8 @@ public Observable firstOrDefault(T defaultValue) { * doesn't emit anything that satisfies the given condition * @return an Observable that emits only the very first item from the source * that satisfies the given condition, or a default value otherwise - * @see MSDN: Observable.FirstOrDefault + * @see firstOrDefault() + * @see MSDN: Observable.FirstOrDefault */ public Observable firstOrDefault(Func1 predicate, T defaultValue) { return create(OperationFirstOrDefault.firstOrDefault(this, predicate, defaultValue)); @@ -4288,12 +4574,12 @@ public Observable firstOrDefault(Func1 predicate, T defau * Returns the elements of the specified sequence or the specified default * value in a singleton sequence if the sequence is empty. *

    - * + * * * @param defaultValue the value to return if the sequence is empty * @return an Observable that emits the specified default value if the * source is empty; otherwise, the items emitted by the source - * + * @see defaultIfEmpty() * @see MSDN: Observable.DefaultIfEmpty */ public Observable defaultIfEmpty(T defaultValue) { @@ -4316,6 +4602,7 @@ public Observable defaultIfEmpty(T defaultValue) { * from the source Observable, or all of the items from the source * Observable if that Observable emits fewer than num * items + * @see take() */ public Observable take(final int num) { return create(OperationTake.take(this, num)); @@ -4325,13 +4612,14 @@ public Observable take(final int num) { * Returns an Observable that emits items emitted by the source Observable * so long as a specified condition is true. *

    - * + * * * @param predicate a function that evaluates an item emitted by the source * Observable and returns a Boolean * @return an Observable that emits the items from the source Observable so * long as each item satisfies the condition defined by * predicate + * @see takeWhile() */ public Observable takeWhile(final Func1 predicate) { return create(OperationTakeWhile.takeWhile(this, predicate)); @@ -4342,7 +4630,7 @@ public Observable takeWhile(final Func1 predicate) { * so long as a given predicate remains true, where the predicate can * operate on both the item and its index relative to the complete sequence. *

    - * + * * * @param predicate a function to test each item emitted by the source * Observable for a condition; the second parameter of the @@ -4350,6 +4638,7 @@ public Observable takeWhile(final Func1 predicate) { * @return an Observable that emits items from the source Observable so long * as the predicate continues to return true for each * item, then completes + * @see takeWhileWithIndex() */ public Observable takeWhileWithIndex(final Func2 predicate) { return create(OperationTakeWhile.takeWhileWithIndex(this, predicate)); @@ -4363,8 +4652,9 @@ public Observable takeWhileWithIndex(final Func2MSDN: Observable.First + * emitting a single item + * @see first() + * @see MSDN: Observable.First * @see #first() */ public Observable takeFirst() { @@ -4380,8 +4670,9 @@ public Observable takeFirst() { * @param predicate the condition any source emitted item has to satisfy * @return an Observable that emits only the very first item satisfying the * given condition from the source, or none if the source Observable - * completes without emitting a single matching item. - * @see MSDN: Observable.First + * completes without emitting a single matching item + * @see first() + * @see MSDN: Observable.First * @see #first(Func1) */ public Observable takeFirst(Func1 predicate) { @@ -4392,12 +4683,13 @@ public Observable takeFirst(Func1 predicate) { * Returns an Observable that emits only the last count items * emitted by the source Observable. *

    - * + * * * @param count the number of items to emit from the end of the sequence * emitted by the source Observable * @return an Observable that emits only the last count items * emitted by the source Observable + * @see takeLast() */ public Observable takeLast(final int count) { return create(OperationTakeLast.takeLast(this, count)); @@ -4407,7 +4699,7 @@ public Observable takeLast(final int count) { * Returns an Observable that emits the items from the source Observable * only until the other Observable emits an item. *

    - * + * * * @param other the Observable whose first emitted item will cause * takeUntil to stop emitting items from the @@ -4415,6 +4707,7 @@ public Observable takeLast(final int count) { * @param the type of items emitted by other * @return an Observable that emits the items of the source Observable until * such time as other emits its first item + * @see takeUntil() */ public Observable takeUntil(Observable other) { return OperationTakeUntil.takeUntil(this, other); @@ -4433,7 +4726,8 @@ public Observable takeUntil(Observable other) { * as a second parameter. * @return an Observable that emits all items from the source Observable as * soon as the condition becomes false - * @see MSDN: Observable.SkipWhile + * @see skipWhileWithIndex() + * @see MSDN: Observable.SkipWhile */ public Observable skipWhileWithIndex(Func2 predicate) { return create(OperationSkipWhile.skipWhileWithIndex(this, predicate)); @@ -4450,7 +4744,8 @@ public Observable skipWhileWithIndex(Func2 predi * Observable for a condition * @return an Observable that emits all items from the source Observable as * soon as the condition becomes false - * @see MSDN: Observable.SkipWhile + * @see skipWhile() + * @see MSDN: Observable.SkipWhile */ public Observable skipWhile(Func1 predicate) { return create(OperationSkipWhile.skipWhile(this, predicate)); @@ -4465,15 +4760,14 @@ public Observable skipWhile(Func1 predicate) { * from the front of the queue and produced on the result sequence. This * causes elements to be delayed. *

    - * + * * * @param count number of elements to bypass at the end of the source * sequence * @return an Observable sequence emitting the source sequence items * except for the bypassed ones at the end - * * @throws IndexOutOfBoundsException if count is less than zero - * + * @see skipLast() * @see MSDN: Observable.SkipLast */ public Observable skipLast(int count) { @@ -4500,6 +4794,7 @@ public Observable skipLast(int count) { * * @return an Observable that emits a single item: a List containing all of * the items emitted by the source Observable. + * @see toList() */ public Observable> toList() { return create(OperationToObservableList.toObservableList(this)); @@ -4518,6 +4813,7 @@ public Observable> toList() { * all other items emitted by the Observable * @return an Observable that emits the items from the source Observable in * sorted order + * @see toSortedList() */ public Observable> toSortedList() { return create(OperationToObservableSortedList.toSortedList(this)); @@ -4534,6 +4830,7 @@ public Observable> toSortedList() { * indicates their sort order * @return an Observable that emits the items from the source Observable in * sorted order + * @see toSortedList() */ public Observable> toSortedList(Func2 sortFunction) { return create(OperationToObservableSortedList.toSortedList(this, sortFunction)); @@ -4548,11 +4845,45 @@ public Observable> toSortedList(Func2 sor * @param values Iterable of the items you want the modified Observable to * emit first * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(Iterable values) { return concat(Observable. from(values), this); } + /** + * Emit a specified set of items with the specified scheduler before + * beginning to emit items from the source Observable. + *

    + * + * + * @param values iterable of the items you want the modified Observable to + * emit first + * @param scheduler the scheduler to emit the prepended values on + * @return an Observable that exhibits the modified behavior + * @see startWith() + * @see MSDN: Observable.StartWith + */ + public Observable startWith(Iterable values, Scheduler scheduler) { + return concat(from(values, scheduler), this); + } + + /** + * Emit a specified array of items with the specified scheduler before + * beginning to emit items from the source Observable. + *

    + * + * + * @param values the items you want the modified Observable to emit first + * @param scheduler the scheduler to emit the prepended values on + * @return an Observable that exhibits the modified behavior + * @see startWith() + * @see MSDN: Observable.StartWith + */ + public Observable startWith(T[] values, Scheduler scheduler) { + return startWith(Arrays.asList(values), scheduler); + } + /** * Emit a specified item before beginning to emit items from the source * Observable. @@ -4561,6 +4892,7 @@ public Observable startWith(Iterable values) { * * @param t1 item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1) { return concat(Observable. from(t1), this); @@ -4575,6 +4907,7 @@ public Observable startWith(T t1) { * @param t1 first item to emit * @param t2 second item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2) { return concat(Observable. from(t1, t2), this); @@ -4590,6 +4923,7 @@ public Observable startWith(T t1, T t2) { * @param t2 second item to emit * @param t3 third item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3) { return concat(Observable. from(t1, t2, t3), this); @@ -4606,6 +4940,7 @@ public Observable startWith(T t1, T t2, T t3) { * @param t3 third item to emit * @param t4 fourth item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4) { return concat(Observable. from(t1, t2, t3, t4), this); @@ -4623,6 +4958,7 @@ public Observable startWith(T t1, T t2, T t3, T t4) { * @param t4 fourth item to emit * @param t5 fifth item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5) { return concat(Observable. from(t1, t2, t3, t4, t5), this); @@ -4641,6 +4977,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5) { * @param t5 fifth item to emit * @param t6 sixth item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { return concat(Observable. from(t1, t2, t3, t4, t5, t6), this); @@ -4660,6 +4997,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { * @param t6 sixth item to emit * @param t7 seventh item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7), this); @@ -4680,6 +5018,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { * @param t7 seventh item to emit * @param t8 eighth item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8), this); @@ -4701,6 +5040,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { * @param t8 eighth item to emit * @param t9 ninth item to emit * @return an Observable that exhibits the modified behavior + * @see startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8, t9), this); @@ -4711,7 +5051,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T * criterion, and emits these grouped items as {@link GroupedObservable}s, * one GroupedObservable per group. *

    - * + * * * @param keySelector a function that extracts the key from an item * @param elementSelector a function to map a source item to an item in a @@ -4723,6 +5063,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T * which corresponds to a unique key value and emits items * representing items from the source Observable that share that key * value + * @see groupBy */ public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) { return create(OperationGroupBy.groupBy(this, keySelector, elementSelector)); @@ -4733,7 +5074,7 @@ public Observable> groupBy(final Func1 - * + * * * @param keySelector a function that extracts the key for each item * @param the key type @@ -4741,6 +5082,7 @@ public Observable> groupBy(final Func1groupBy */ public Observable> groupBy(final Func1 keySelector) { return create(OperationGroupBy.groupBy(this, keySelector)); @@ -4753,9 +5095,10 @@ public Observable> groupBy(final Func1any operator but renamed in * RxJava to better match Java naming idioms. *

    - * + * * * @return an Observable that emits a Boolean + * @see isEmpty() * @see MSDN: Observable.Any */ public Observable isEmpty() { @@ -4767,9 +5110,10 @@ public Observable isEmpty() { * source or an IllegalArgumentException if the source * {@link Observable} is empty. *

    - * + * * * @return + * @see last() */ public Observable last() { return create(OperationLast.last(this)); @@ -4789,11 +5133,12 @@ public BlockingObservable toBlockingObservable() { /** * Converts the items emitted by an Observable to the specified type. *

    - * + * * * @param klass the target class type which the items will be converted to * @return an Observable that emits each item from the source Observable * converted to the specified type + * @see cast() * @see MSDN: Observable.Cast */ public Observable cast(final Class klass) { @@ -4803,12 +5148,13 @@ public Observable cast(final Class klass) { /** * Filters the items emitted by an Observable based on the specified type. *

    - * + * * * @param klass the class type to filter the items emitted by the source * Observable * @return an Observable that emits items from the source Observable of * type klass. + * @see ofClass() * @see MSDN: Observable.OfType */ public Observable ofType(final Class klass) { @@ -4823,11 +5169,11 @@ public Boolean call(T t) { * Ignores all items emitted by an Observable and only calls * onCompleted or onError. *

    - * + * * * @return an empty Observable that only calls onCompleted or * onError - * + * @see ignoreElements() * @see MSDN: Observable.IgnoreElements */ public Observable ignoreElements() { @@ -4840,13 +5186,14 @@ public Observable ignoreElements() { * isn't received within the specified timeout duration starting from its * predecessor, a TimeoutException is propagated to the observer. *

    - * + * * * @param timeout maximum duration between values before a timeout occurs * @param timeUnit the unit of time which applies to the * timeout argument. * @return the source Observable with a TimeoutException in * case of a timeout + * @see timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit) { @@ -4860,7 +5207,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit) { * predecessor, the other observable sequence is used to produce future * messages from that point on. *

    - * + * * * @param timeout maximum duration between values before a timeout occurs * @param timeUnit the unit of time which applies to the @@ -4868,6 +5215,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit) { * @param other sequence to return in case of a timeout * @return the source sequence switching to the other sequence in case of a * timeout + * @see timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Observable other) { @@ -4880,7 +5228,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Observable - * + * * * @param timeout maximum duration between values before a timeout occurs * @param timeUnit the unit of time which applies to the @@ -4888,6 +5236,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, ObservableTimeoutException in case * of a timeout + * @see timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { @@ -4901,7 +5250,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler schedule * predecessor, the other observable sequence is used to produce future * messages from that point on. *

    - * + * * * @param timeout maximum duration between values before a timeout occurs * @param timeUnit the unit of time which applies to the @@ -4910,6 +5259,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler schedule * @param scheduler Scheduler to run the timeout timers on * @return the source sequence switching to the other sequence in case of a * timeout + * @see timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Observable other, Scheduler scheduler) { @@ -4920,9 +5270,10 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Observable - * + * * * @return an Observable that emits time interval information items + * @see timeInterval() * @see MSDN: Observable.TimeInterval */ public Observable> timeInterval() { @@ -4933,10 +5284,11 @@ public Observable> timeInterval() { * Records the time interval between consecutive items emitted by an * Observable, using the specified Scheduler to compute time intervals. *

    - * + * * * @param scheduler Scheduler used to compute time intervals * @return an Observable that emits time interval information items + * @see timeInterval() * @see MSDN: Observable.TimeInterval */ public Observable> timeInterval(Scheduler scheduler) { @@ -4946,13 +5298,14 @@ public Observable> timeInterval(Scheduler scheduler) { /** * Constructs an Observable that depends on a resource object. *

    - * + * * * @param resourceFactory the factory function to obtain a resource object * that depends on the Observable * @param observableFactory the factory function to obtain an Observable * @return the Observable whose lifetime controls the lifetime of the * dependent resource object + * @see using() * @see MSDN: Observable.Using */ public static Observable using(Func0 resourceFactory, Func1> observableFactory) { @@ -4962,12 +5315,13 @@ public static Observable using(Func0 - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first * @return an Observable that reflects whichever of the given Observables * reacted first + * @see amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2) { @@ -4977,13 +5331,14 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first * @param o3 an Observable competing to react first * @return an Observable that reflects whichever of the given Observables * reacted first + * @see amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3) { @@ -4993,7 +5348,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5001,6 +5356,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4) { @@ -5010,7 +5366,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5019,6 +5375,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5) { @@ -5028,7 +5385,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5038,6 +5395,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6) { @@ -5047,7 +5405,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5058,6 +5416,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7) { @@ -5067,7 +5426,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5079,6 +5438,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8) { @@ -5088,7 +5448,7 @@ public static Observable amb(Observable o1, Observable - * + * * * @param o1 an Observable competing to react first * @param o2 an Observable competing to react first @@ -5101,6 +5461,7 @@ public static Observable amb(Observable o1, Observableamb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9) { @@ -5110,26 +5471,27 @@ public static Observable amb(Observable o1, Observable - * + * * * @param sources Observable sources competing to react first * @return an Observable that reflects whichever of the given Observables * reacted first + * @see amb() * @see MSDN: Observable.Amb */ public static Observable amb(Iterable> sources) { return create(OperationAmb.amb(sources)); } - /** * Invokes an action for each item emitted by the Observable. *

    - * + * * * @param observer the action to invoke for each item emitted in the source * sequence * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(Observer observer) { @@ -5139,11 +5501,12 @@ public Observable doOnEach(Observer observer) { /** * Invokes an action for each item emitted by an Observable. *

    - * + * * * @param onNext the action to invoke for each item in the source * sequence * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext) { @@ -5168,10 +5531,11 @@ public void onNext(T args) { /** * Invokes an action if onError is called from the Observable. *

    - * + * * * @param onError the action to invoke if onError is invoked * @return the source sequence with the side-effecting behavior applied + * @see doOnError() * @see MSDN: Observable.Do */ public Observable doOnError(final Action1 onError) { @@ -5197,11 +5561,12 @@ public void onNext(T args) { } * Invokes an action when onCompleted is called by the * Observable. *

    - * + * * * @param onCompleted the action to invoke when onCompleted is * called * @return the source sequence with the side-effecting behavior applied + * @see doOnCompleted() * @see MSDN: Observable.Do */ public Observable doOnCompleted(final Action0 onCompleted) { @@ -5230,6 +5595,7 @@ public void onNext(T args) { } * @param onError the action to invoke when the source Observable calls * onError * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext, final Action1 onError) { @@ -5253,7 +5619,6 @@ public void onNext(T args) { return create(OperationDoOnEach.doOnEach(this, observer)); } - /** * Invokes an action for each item emitted by an Observable. * @@ -5263,6 +5628,7 @@ public void onNext(T args) { * @param onCompleted the action to invoke when the source Observable calls * onCompleted * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext, final Action1 onError, final Action0 onCompleted) { @@ -5326,4 +5692,254 @@ private boolean isInternalImplementation(Object o) { } } + /** + * Creates a pattern that matches when both Observable sequences have an + * available item. + *

    + * + * + * @param right Observable sequence to match with the left sequence + * @return Pattern object that matches when both Observable sequences have + * an available item + * @throws NullPointerException if right is null + * @see and() + * @see MSDN: Observable.And + */ + public Pattern2 and(Observable right) { + return OperationJoinPatterns.and(this, right); + } + + /** + * Matches when the Observable sequence has an available item and + * projects the item by invoking the selector function. + *

    + * + * + * @param selector Selector that will be invoked for elements in the source + * sequence + * @return Plan that produces the projected results, to be fed (with other + * plans) to the When operator + * @throws NullPointerException if selector is null + * @see then() + * @see MSDN: Observable.Then + */ + public Plan0 then(Func1 selector) { + return OperationJoinPatterns.then(this, selector); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param plans a series of plans created by use of the Then operator on + * patterns + * @return an Observable sequence with the results from matching several + * patterns + * @throws NullPointerException if plans is null + * @see when() + * @see MSDN: Observable.When + */ + public static Observable when(Plan0... plans) { + return create(OperationJoinPatterns.when(plans)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param plans a series of plans created by use of the Then operator on + * patterns + * @return an Observable sequence with the results from matching several + * patterns + * @throws NullPointerException if plans is null + * @see when() + * @see MSDN: Observable.When + */ + public static Observable when(Iterable> plans) { + if (plans == null) { + throw new NullPointerException("plans"); + } + return create(OperationJoinPatterns.when(plans)); + } + + /** + * Joins the results from a pattern. + *

    + * + * + * @param p1 the plan to join + * @return an Observable sequence with the results from matching a pattern + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1) { + return create(OperationJoinPatterns.when(p1)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2) { + return create(OperationJoinPatterns.when(p1, p2)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3) { + return create(OperationJoinPatterns.when(p1, p2, p3)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @param p7 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @param p7 a plan + * @param p8 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @param p7 a plan + * @param p8 a plan + * @param p9 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8, Plan0 p9) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8, p9)); + } } From 8d77fbf823f5cd8b3ebb5ab9cc31731d9e9c36b0 Mon Sep 17 00:00:00 2001 From: David Gross Date: Fri, 22 Nov 2013 13:59:14 -0800 Subject: [PATCH 308/333] standardizing javadoc comments, adding wiki links and diagrams, for and/then/when --- rxjava-core/src/main/java/rx/Observable.java | 130 +++++++++++++++---- 1 file changed, 106 insertions(+), 24 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 07c89c0b19..5a703c5019 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -5483,7 +5483,6 @@ public static Observable amb(Iterable> return create(OperationAmb.amb(sources)); } - /** * Invokes an action for each item emitted by the Observable. *

    @@ -5692,42 +5691,71 @@ private boolean isInternalImplementation(Object o) { return isInternal; } } + /** - * Creates a pattern that matches when both observable sequences have an available element. - * @param right Observable sequence to match with the left sequence. - * @return Pattern object that matches when both observable sequences have an available element. + * Creates a pattern that matches when both Observable sequences have an + * available item. + *

    + * + * + * @param right Observable sequence to match with the left sequence + * @return Pattern object that matches when both Observable sequences have + * an available item + * @throws NullPointerException if right is null + * @see and() * @see MSDN: Observable.And - * @throws NullPointerException if right is null */ public Pattern2 and(Observable right) { return OperationJoinPatterns.and(this, right); } + /** - * Matches when the observable sequence has an available element and projects the element by invoking the selector function. - * @param selector Selector that will be invoked for elements in the source sequence. - * @return Plan that produces the projected results, to be fed (with other plans) to the When operator. + * Matches when the Observable sequence has an available item and + * projects the item by invoking the selector function. + *

    + * + * + * @param selector Selector that will be invoked for elements in the source + * sequence + * @return Plan that produces the projected results, to be fed (with other + * plans) to the When operator + * @throws NullPointerException if selector is null + * @see then() * @see MSDN: Observable.Then - * @throws NullPointerException if selector is null */ public Plan0 then(Func1 selector) { return OperationJoinPatterns.then(this, selector); } + /** * Joins together the results from several patterns. - * @param plans A series of plans created by use of the Then operator on patterns. - * @return An observable sequence with the results from matching several patterns. + *

    + * + * + * @param plans a series of plans created by use of the Then operator on + * patterns + * @return an Observable sequence with the results from matching several + * patterns + * @throws NullPointerException if plans is null + * @see when() * @see MSDN: Observable.When - * @throws NullPointerException if plans is null */ public static Observable when(Plan0... plans) { return create(OperationJoinPatterns.when(plans)); } + /** * Joins together the results from several patterns. - * @param plans A series of plans created by use of the Then operator on patterns. - * @return An observable sequence with the results from matching several patterns. + *

    + * + * + * @param plans a series of plans created by use of the Then operator on + * patterns + * @return an Observable sequence with the results from matching several + * patterns + * @throws NullPointerException if plans is null + * @see when() * @see MSDN: Observable.When - * @throws NullPointerException if plans is null */ public static Observable when(Iterable> plans) { if (plans == null) { @@ -5735,83 +5763,122 @@ public static Observable when(Iterable> plans) { } return create(OperationJoinPatterns.when(plans)); } + /** * Joins the results from a pattern. + *

    + * + * * @param p1 the plan to join - * @return An observable sequence with the results from matching a pattern + * @return an Observable sequence with the results from matching a pattern + * @see when() * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1) { return create(OperationJoinPatterns.when(p1)); } + /** * Joins together the results from several patterns. + *

    + * + * * @param p1 a plan * @param p2 a plan - * @return An observable sequence with the results from matching several patterns + * @return an Observable sequence with the results from matching several + * patterns + * @see when() * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2) { return create(OperationJoinPatterns.when(p1, p2)); } + /** * Joins together the results from several patterns. + *

    + * + * * @param p1 a plan * @param p2 a plan * @param p3 a plan - * @return An observable sequence with the results from matching several patterns + * @return an Observable sequence with the results from matching several + * patterns + * @see when() * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3) { return create(OperationJoinPatterns.when(p1, p2, p3)); } + /** * Joins together the results from several patterns. + *

    + * + * * @param p1 a plan * @param p2 a plan * @param p3 a plan * @param p4 a plan - * @return An observable sequence with the results from matching several patterns + * @return an Observable sequence with the results from matching several + * patterns + * @see when() * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4) { return create(OperationJoinPatterns.when(p1, p2, p3, p4)); } + /** * Joins together the results from several patterns. + *

    + * + * * @param p1 a plan * @param p2 a plan * @param p3 a plan * @param p4 a plan * @param p5 a plan - * @return An observable sequence with the results from matching several patterns + * @return an Observable sequence with the results from matching several + * patterns + * @see when() * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5) { return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5)); } + /** * Joins together the results from several patterns. + *

    + * + * * @param p1 a plan * @param p2 a plan * @param p3 a plan * @param p4 a plan * @param p5 a plan * @param p6 a plan - * @return An observable sequence with the results from matching several patterns + * @return an Observable sequence with the results from matching several + * patterns + * @see when() * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6) { return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6)); } + /** * Joins together the results from several patterns. + *

    + * + * * @param p1 a plan * @param p2 a plan * @param p3 a plan @@ -5819,15 +5886,21 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p5 a plan * @param p6 a plan * @param p7 a plan - * @return An observable sequence with the results from matching several patterns + * @return an Observable sequence with the results from matching several + * patterns + * @see when() * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7) { return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7)); } + /** * Joins together the results from several patterns. + *

    + * + * * @param p1 a plan * @param p2 a plan * @param p3 a plan @@ -5836,15 +5909,21 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p6 a plan * @param p7 a plan * @param p8 a plan - * @return An observable sequence with the results from matching several patterns + * @return an Observable sequence with the results from matching several + * patterns + * @see when() * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8) { return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8)); } + /** * Joins together the results from several patterns. + *

    + * + * * @param p1 a plan * @param p2 a plan * @param p3 a plan @@ -5854,7 +5933,9 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p7 a plan * @param p8 a plan * @param p9 a plan - * @return An observable sequence with the results from matching several patterns + * @return an Observable sequence with the results from matching several + * patterns + * @see when() * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") @@ -5862,3 +5943,4 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8, p9)); } } + From e74d0becb143142db43510a05abf3b9053b8c337 Mon Sep 17 00:00:00 2001 From: akarnokd Date: Fri, 22 Nov 2013 23:15:54 +0100 Subject: [PATCH 309/333] Operations toMap (3) and toMultimap (4) --- .../rx/lang/groovy/ObservableTests.groovy | 137 ++++++++++ rxjava-core/src/main/java/rx/Observable.java | 127 +++++++++ .../java/rx/operators/OperationToMap.java | 159 +++++++++++ .../rx/operators/OperationToMultimap.java | 206 +++++++++++++++ .../java/rx/operators/OperationToMapTest.java | 215 +++++++++++++++ .../rx/operators/OperationToMultimapTest.java | 250 ++++++++++++++++++ 6 files changed, 1094 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationToMap.java create mode 100644 rxjava-core/src/main/java/rx/operators/OperationToMultimap.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationToMapTest.java create mode 100644 rxjava-core/src/test/java/rx/operators/OperationToMultimapTest.java diff --git a/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/ObservableTests.groovy b/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/ObservableTests.groovy index ceeb25dcf6..9c6e244149 100644 --- a/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/ObservableTests.groovy +++ b/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/ObservableTests.groovy @@ -345,6 +345,143 @@ def class ObservableTests { assertEquals(6, count); } + @Test + public void testToMap1() { + Map actual = new HashMap(); + + Observable.from("a", "bb", "ccc", "dddd") + .toMap({String s -> s.length()}) + .toBlockingObservable() + .forEach({s -> actual.putAll(s); }); + + Map expected = new HashMap(); + expected.put(1, "a"); + expected.put(2, "bb"); + expected.put(3, "ccc"); + expected.put(4, "dddd"); + + assertEquals(expected, actual); + } + + @Test + public void testToMap2() { + Map actual = new HashMap(); + + Observable.from("a", "bb", "ccc", "dddd") + .toMap({String s -> s.length()}, {String s -> s + s}) + .toBlockingObservable() + .forEach({s -> actual.putAll(s); }); + + Map expected = new HashMap(); + expected.put(1, "aa"); + expected.put(2, "bbbb"); + expected.put(3, "cccccc"); + expected.put(4, "dddddddd"); + + assertEquals(expected, actual); + } + + @Test + public void testToMap3() { + Map actual = new HashMap(); + + LinkedHashMap last3 = new LinkedHashMap() { + public boolean removeEldestEntry(Map.Entry e) { + return size() > 3; + } + }; + + Observable.from("a", "bb", "ccc", "dddd") + .toMap({String s -> s.length()}, {String s -> s + s}, { last3 }) + .toBlockingObservable() + .forEach({s -> actual.putAll(s); }); + + Map expected = new HashMap(); + expected.put(2, "bbbb"); + expected.put(3, "cccccc"); + expected.put(4, "dddddddd"); + + assertEquals(expected, actual); + } + @Test + public void testToMultimap1() { + Map actual = new HashMap(); + + Observable.from("a", "b", "cc", "dd") + .toMultimap({String s -> s.length()}) + .toBlockingObservable() + .forEach({s -> actual.putAll(s); }); + + Map expected = new HashMap(); + + expected.put(1, Arrays.asList("a", "b")); + expected.put(2, Arrays.asList("cc", "dd")); + + assertEquals(expected, actual); + } + + @Test + public void testToMultimap2() { + Map actual = new HashMap(); + + Observable.from("a", "b", "cc", "dd") + .toMultimap({String s -> s.length()}, {String s -> s + s}) + .toBlockingObservable() + .forEach({s -> actual.putAll(s); }); + + Map expected = new HashMap(); + + expected.put(1, Arrays.asList("aa", "bb")); + expected.put(2, Arrays.asList("cccc", "dddd")); + + assertEquals(expected, actual); + } + + @Test + public void testToMultimap3() { + Map actual = new HashMap(); + + LinkedHashMap last1 = new LinkedHashMap() { + public boolean removeEldestEntry(Map.Entry e) { + return size() > 1; + } + }; + + Observable.from("a", "b", "cc", "dd") + .toMultimap({String s -> s.length()}, {String s -> s + s}, { last1 }) + .toBlockingObservable() + .forEach({s -> actual.putAll(s); }); + + Map expected = new HashMap(); + + expected.put(2, Arrays.asList("cccc", "dddd")); + + assertEquals(expected, actual); + } + + @Test + public void testToMultimap4() { + Map actual = new HashMap(); + + LinkedHashMap last1 = new LinkedHashMap() { + public boolean removeEldestEntry(Map.Entry e) { + return size() > 2; + } + }; + + Observable.from("a", "b", "cc", "dd", "eee", "eee") + .toMultimap({String s -> s.length()}, {String s -> s + s}, { last1 }, + {i -> i == 2 ? new ArrayList() : new HashSet() }) + .toBlockingObservable() + .forEach({s -> actual.putAll(s); }); + + Map expected = new HashMap(); + + expected.put(2, Arrays.asList("cccc", "dddd")); + expected.put(3, new HashSet(Arrays.asList("eeeeee"))); + + assertEquals(expected, actual); + } def class AsyncObservable implements OnSubscribeFunc { diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 07c89c0b19..7bad82666e 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -19,8 +19,10 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -86,6 +88,8 @@ import rx.operators.OperationTimeInterval; import rx.operators.OperationTimeout; import rx.operators.OperationTimestamp; +import rx.operators.OperationToMap; +import rx.operators.OperationToMultimap; import rx.operators.OperationToObservableFuture; import rx.operators.OperationToObservableIterable; import rx.operators.OperationToObservableList; @@ -5861,4 +5865,127 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8, Plan0 p9) { return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8, p9)); } + + /** + * Return an Observable that emits a single HashMap containing all items + * emitted by the source Observable, mapped by the keys returned by the + * {@code keySelector} function. + * + * If a source item maps to the same key, the HashMap will contain the latest + * of those items. + * + * @param keySelector the function that extracts the key from the source items + * to be used as keys in the HashMap. + * @return an Observable that emits a single HashMap containing the mapped + * values of the source Observable + * @see MSDN: Observable.ToDictionary + */ + public Observable> toMap(Func1 keySelector) { + return create(OperationToMap.toMap(this, keySelector)); + } + + /** + * Return an Observable that emits a single HashMap containing elements with + * key and value extracted from the values emitted by the source Observable. + * + * If a source item maps to the same key, the HashMap will contain the latest + * of those items. + * + * @param keySelector the function that extracts the key from the source items + * to be used as key in the HashMap + * @param valueSelector the function that extracts the value from the source items + * to be used as value in the HashMap + * @return an Observable that emits a single HashMap containing the mapped + * values of the source Observable + * @see MSDN: Observable.ToDictionary + */ + public Observable> toMap(Func1 keySelector, Func1 valueSelector) { + return create(OperationToMap.toMap(this, keySelector, valueSelector)); + } + + /** + * Return an Observable that emits a single Map, returned by the mapFactory function, + * containing key and value extracted from the values emitted by the source Observable. + * + * @param keySelector the function that extracts the key from the source items + * to be used as key in the Map + * @param valueSelector the function that extracts the value from the source items + * to be used as value in the Map + * @param mapFactory the function that returns an Map instance to be used + * @return an Observable that emits a single Map containing the mapped + * values of the source Observable + */ + public Observable> toMap(Func1 keySelector, Func1 valueSelector, Func0> mapFactory) { + return create(OperationToMap.toMap(this, keySelector, valueSelector, mapFactory)); + } + + /** + * Return an Observable that emits a single HashMap containing an ArrayList of elements, + * emitted by the source Observable and keyed by the keySelector function. + * + * @param keySelector the function that extracts the key from the source items + * to be used as key in the HashMap + * @return an Observable that emits a single HashMap containing an ArrayList of elements + * mapped from the source Observable + * @see MSDN: Observable.ToLookup + */ + public Observable>> toMultimap(Func1 keySelector) { + return create(OperationToMultimap.toMultimap(this, keySelector)); + } + + /** + * Return an Observable that emits a single HashMap containing an ArrayList of values, + * extracted by the valueSelector function, emitted by the source Observable + * and keyed by the keySelector function. + * + * @param keySelector the function that extracts the key from the source items + * to be used as key in the HashMap + * @param valueSelector the function that extracts the value from the source items + * to be used as value in the Map + * @return an Observable that emits a single HashMap containing an ArrayList of elements + * mapped from the source Observable + * + * @see MSDN: Observable.ToLookup + */ + public Observable>> toMultimap(Func1 keySelector, Func1 valueSelector) { + return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector)); + } + + /** + * Return an Observable that emits a single Map, returned by the mapFactory function, + * containing an ArrayList of values, extracted by the valueSelector function, + * emitted by the source Observable and keyed by the + * keySelector function. + * + * @param keySelector the function that extracts the key from the source items + * to be used as key in the Map + * @param valueSelector the function that extracts the value from the source items + * to be used as value in the Map + * @param mapFactory the function that returns an Map instance to be used + * @return an Observable that emits a single Map containing the list of mapped values + * of the source observable. + */ + public Observable>> toMultimap(Func1 keySelector, Func1 valueSelector, Func0>> mapFactory) { + return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector, mapFactory)); + } + + /** + * Return an Observable that emits a single Map, returned by the mapFactory function, + * containing a custom collection of values, extracted by the valueSelector function, + * emitted by the source Observable and keyed by the + * keySelector function. + * + * @param keySelector the function that extracts the key from the source items + * to be used as key in the Map + * @param valueSelector the function that extracts the value from the source items + * to be used as value in the Map + * @param mapFactory the function that returns an Map instance to be used + * @param collectionFactory the function that returns a Collection instance for + * a particular key to be used in the Map + * @return an Observable that emits a single Map containing the collection of mapped values + * of the source observable. + */ + public Observable>> toMultimap(Func1 keySelector, Func1 valueSelector, Func0>> mapFactory, Func1> collectionFactory) { + return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector, mapFactory, collectionFactory)); + } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationToMap.java b/rxjava-core/src/main/java/rx/operators/OperationToMap.java new file mode 100644 index 0000000000..c52b0244f2 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationToMap.java @@ -0,0 +1,159 @@ +/** + * Copyright 2013 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.operators; + +import java.util.HashMap; +import java.util.Map; +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Func0; +import rx.util.functions.Func1; +import rx.util.functions.Functions; + +/** + * Maps the elements of the source observable into a java.util.Map instance and + * emits that once the source observable completes. + * + * @see Issue #96 + */ +public class OperationToMap { + /** + * ToMap with key selector, identity value selector and default HashMap factory. + */ + public static OnSubscribeFunc> toMap(Observable source, + Func1 keySelector) { + return new ToMap(source, keySelector, + Functions.identity(), new DefaultToMapFactory()); + } + + /** + * ToMap with key selector, value selector and default HashMap factory. + */ + public static OnSubscribeFunc> toMap(Observable source, + Func1 keySelector, + Func1 valueSelector) { + return new ToMap(source, keySelector, + valueSelector, new DefaultToMapFactory()); + } + + /** + * ToMap with key selector, value selector and custom Map factory. + */ + public static OnSubscribeFunc> toMap(Observable source, + Func1 keySelector, + Func1 valueSelector, + Func0> mapFactory) { + return new ToMap(source, keySelector, + valueSelector, mapFactory); + } + + /** The default map factory. */ + public static class DefaultToMapFactory implements Func0> { + @Override + public Map call() { + return new HashMap(); + } + } + /** + * Maps the elements of the source observable into a java.util.Map instance + * returned by the mapFactory function by using the keySelector and + * valueSelector. + * @param the source's value type + * @param the key type + * @param the value type + */ + public static class ToMap implements OnSubscribeFunc> { + /** The source. */ + private final Observable source; + /** Key extractor. */ + private final Func1 keySelector; + /** Value extractor. */ + private final Func1 valueSelector; + /** Map factory. */ + private final Func0> mapFactory; + public ToMap( + Observable source, + Func1 keySelector, + Func1 valueSelector, + Func0> mapFactory + ) { + this.source = source; + this.keySelector = keySelector; + this.valueSelector = valueSelector; + this.mapFactory = mapFactory; + + } + @Override + public Subscription onSubscribe(Observer> t1) { + Map map; + try { + map = mapFactory.call(); + } catch (Throwable t) { + t1.onError(t); + return Subscriptions.empty(); + } + return source.subscribe(new ToMapObserver( + t1, keySelector, valueSelector, map)); + } + /** + * Observer that collects the source values of T into + * a map. + */ + public static class ToMapObserver implements Observer { + /** The map. */ + Map map; + /** Key extractor. */ + private final Func1 keySelector; + /** Value extractor. */ + private final Func1 valueSelector; + /** The observer who is receiving the completed map. */ + private final Observer> t1; + + public ToMapObserver( + Observer> t1, + Func1 keySelector, + Func1 valueSelector, + Map map) { + this.map = map; + this.t1 = t1; + this.keySelector = keySelector; + this.valueSelector = valueSelector; + } + @Override + public void onNext(T args) { + K key = keySelector.call(args); + V value = valueSelector.call(args); + map.put(key, value); + } + @Override + public void onError(Throwable e) { + map = null; + t1.onError(e); + } + @Override + public void onCompleted() { + Map map0 = map; + map = null; + t1.onNext(map0); + t1.onCompleted(); + } + } + } +} diff --git a/rxjava-core/src/main/java/rx/operators/OperationToMultimap.java b/rxjava-core/src/main/java/rx/operators/OperationToMultimap.java new file mode 100644 index 0000000000..e9cf8d9413 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationToMultimap.java @@ -0,0 +1,206 @@ +/** + * Copyright 2013 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.operators; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Func0; +import rx.util.functions.Func1; +import rx.util.functions.Functions; + +/** + * Maps the elements of the source observable into a multimap + * (Map<K, Collection<V>>) where each + * key entry has a collection of the source's values. + * + * @see Issue #97 + */ +public class OperationToMultimap { + /** + * ToMultimap with key selector, identitiy value selector, + * default HashMap factory and default ArrayList collection factory. + */ + public static OnSubscribeFunc>> toMultimap( + Observable source, + Func1 keySelector + ) { + return new ToMultimap( + source, keySelector, Functions.identity(), + new DefaultToMultimapFactory(), + new DefaultMultimapCollectionFactory() + ); + } + + /** + * ToMultimap with key selector, custom value selector, + * default HashMap factory and default ArrayList collection factory. + */ + public static OnSubscribeFunc>> toMultimap( + Observable source, + Func1 keySelector, + Func1 valueSelector + ) { + return new ToMultimap( + source, keySelector, valueSelector, + new DefaultToMultimapFactory(), + new DefaultMultimapCollectionFactory() + ); + } + /** + * ToMultimap with key selector, custom value selector, + * custom Map factory and default ArrayList collection factory. + */ + public static OnSubscribeFunc>> toMultimap( + Observable source, + Func1 keySelector, + Func1 valueSelector, + Func0>> mapFactory + ) { + return new ToMultimap( + source, keySelector, valueSelector, + mapFactory, + new DefaultMultimapCollectionFactory() + ); + } + /** + * ToMultimap with key selector, custom value selector, + * custom Map factory and custom collection factory. + */ + public static OnSubscribeFunc>> toMultimap( + Observable source, + Func1 keySelector, + Func1 valueSelector, + Func0>> mapFactory, + Func1> collectionFactory + ) { + return new ToMultimap( + source, keySelector, valueSelector, + mapFactory, + collectionFactory + ); + } + /** + * The default multimap factory returning a HashMap. + */ + public static class DefaultToMultimapFactory implements Func0>> { + @Override + public Map> call() { + return new HashMap>(); + } + } + /** + * The default collection factory for a key in the multimap returning + * an ArrayList independent of the key. + */ + public static class DefaultMultimapCollectionFactory + implements Func1> { + @Override + public Collection call(K t1) { + return new ArrayList(); + } + } + /** + * Maps the elements of the source observable int a multimap customized + * by various selectors and factories. + */ + public static class ToMultimap implements OnSubscribeFunc>> { + private final Observable source; + private final Func1 keySelector; + private final Func1 valueSelector; + private final Func0>> mapFactory; + private final Func1> collectionFactory; + public ToMultimap( + Observable source, + Func1 keySelector, + Func1 valueSelector, + Func0>> mapFactory, + Func1> collectionFactory + ) { + this.source = source; + this.keySelector = keySelector; + this.valueSelector = valueSelector; + this.mapFactory = mapFactory; + this.collectionFactory = collectionFactory; + } + @Override + public Subscription onSubscribe(Observer>> t1) { + Map> map; + try { + map = mapFactory.call(); + } catch (Throwable t) { + t1.onError(t); + return Subscriptions.empty(); + } + return source.subscribe(new ToMultimapObserver( + t1, keySelector, valueSelector, map, collectionFactory + )); + } + /** + * Observer that collects the source values of Ts into a multimap. + */ + public static class ToMultimapObserver implements Observer { + private final Func1 keySelector; + private final Func1 valueSelector; + private final Func1> collectionFactory; + private Map> map; + private Observer>> t1; + public ToMultimapObserver( + Observer>> t1, + Func1 keySelector, + Func1 valueSelector, + Map> map, + Func1> collectionFactory + ) { + this.t1 = t1; + this.keySelector = keySelector; + this.valueSelector = valueSelector; + this.collectionFactory = collectionFactory; + this.map = map; + } + @Override + public void onNext(T args) { + K key = keySelector.call(args); + V value = valueSelector.call(args); + Collection collection = map.get(key); + if (collection == null) { + collection = collectionFactory.call(key); + map.put(key, collection); + } + collection.add(value); + } + @Override + public void onError(Throwable e) { + map = null; + t1.onError(e); + } + @Override + public void onCompleted() { + Map> map0 = map; + map = null; + t1.onNext(map0); + t1.onCompleted(); + } + } + } +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationToMapTest.java b/rxjava-core/src/test/java/rx/operators/OperationToMapTest.java new file mode 100644 index 0000000000..ab1a9fb9de --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToMapTest.java @@ -0,0 +1,215 @@ +/** + * Copyright 2013 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.operators; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import org.junit.Test; +import org.junit.Before; +import org.mockito.*; +import static org.mockito.Mockito.*; +import rx.Observable; +import rx.Observer; +import rx.util.functions.Func0; +import rx.util.functions.Func1; +import rx.util.functions.Functions; + +public class OperationToMapTest { + @Mock + Observer objectObserver; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + Func1 lengthFunc = new Func1() { + @Override + public Integer call(String t1) { + return t1.length(); + } + }; + Func1 duplicate = new Func1() { + @Override + public String call(String t1) { + return t1 + t1; + } + }; + @Test + public void testToMap() { + Observable source = Observable.from("a", "bb", "ccc", "dddd"); + + + Observable> mapped = Observable.create(OperationToMap.toMap(source, lengthFunc)); + + Map expected = new HashMap(); + expected.put(1, "a"); + expected.put(2, "bb"); + expected.put(3, "ccc"); + expected.put(4, "dddd"); + + mapped.subscribe(objectObserver); + + verify(objectObserver, never()).onError(any(Throwable.class)); + verify(objectObserver, times(1)).onNext(expected); + verify(objectObserver, times(1)).onCompleted(); + } + + @Test + public void testToMapWithValueSelector() { + Observable source = Observable.from("a", "bb", "ccc", "dddd"); + + Observable> mapped = Observable.create(OperationToMap.toMap(source, lengthFunc, duplicate)); + + Map expected = new HashMap(); + expected.put(1, "aa"); + expected.put(2, "bbbb"); + expected.put(3, "cccccc"); + expected.put(4, "dddddddd"); + + mapped.subscribe(objectObserver); + + verify(objectObserver, never()).onError(any(Throwable.class)); + verify(objectObserver, times(1)).onNext(expected); + verify(objectObserver, times(1)).onCompleted(); + } + + @Test + public void testToMapWithError() { + Observable source = Observable.from("a", "bb", "ccc", "dddd"); + + Func1 lengthFuncErr = new Func1() { + @Override + public Integer call(String t1) { + if ("bb".equals(t1)) { + throw new RuntimeException("Forced Failure"); + } + return t1.length(); + } + }; + Observable> mapped = Observable.create(OperationToMap.toMap(source, lengthFuncErr)); + + Map expected = new HashMap(); + expected.put(1, "a"); + expected.put(2, "bb"); + expected.put(3, "ccc"); + expected.put(4, "dddd"); + + mapped.subscribe(objectObserver); + + verify(objectObserver, never()).onNext(expected); + verify(objectObserver, never()).onCompleted(); + verify(objectObserver, times(1)).onError(any(Throwable.class)); + + } + + @Test + public void testToMapWithErrorInValueSelector() { + Observable source = Observable.from("a", "bb", "ccc", "dddd"); + + Func1 duplicateErr = new Func1() { + @Override + public String call(String t1) { + if ("bb".equals(t1)) { + throw new RuntimeException("Forced failure"); + } + return t1 + t1; + } + }; + + Observable> mapped = Observable.create(OperationToMap.toMap(source, lengthFunc, duplicateErr)); + + Map expected = new HashMap(); + expected.put(1, "aa"); + expected.put(2, "bbbb"); + expected.put(3, "cccccc"); + expected.put(4, "dddddddd"); + + mapped.subscribe(objectObserver); + + verify(objectObserver, never()).onNext(expected); + verify(objectObserver, never()).onCompleted(); + verify(objectObserver, times(1)).onError(any(Throwable.class)); + + } + + @Test + public void testToMapWithFactory() { + Observable source = Observable.from("a", "bb", "ccc", "dddd"); + + Func0> mapFactory = new Func0>() { + @Override + public Map call() { + return new LinkedHashMap() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > 3; + } + }; + } + }; + + Func1 lengthFunc = new Func1() { + @Override + public Integer call(String t1) { + return t1.length(); + } + }; + Observable> mapped = Observable.create(OperationToMap.toMap(source, lengthFunc, Functions.identity(), mapFactory)); + + Map expected = new LinkedHashMap(); + expected.put(2, "bb"); + expected.put(3, "ccc"); + expected.put(4, "dddd"); + + mapped.subscribe(objectObserver); + + verify(objectObserver, never()).onError(any(Throwable.class)); + verify(objectObserver, times(1)).onNext(expected); + verify(objectObserver, times(1)).onCompleted(); + } + @Test + public void testToMapWithErrorThrowingFactory() { + Observable source = Observable.from("a", "bb", "ccc", "dddd"); + + Func0> mapFactory = new Func0>() { + @Override + public Map call() { + throw new RuntimeException("Forced failure"); + } + }; + + Func1 lengthFunc = new Func1() { + @Override + public Integer call(String t1) { + return t1.length(); + } + }; + Observable> mapped = Observable.create(OperationToMap.toMap(source, lengthFunc, Functions.identity(), mapFactory)); + + Map expected = new LinkedHashMap(); + expected.put(2, "bb"); + expected.put(3, "ccc"); + expected.put(4, "dddd"); + + mapped.subscribe(objectObserver); + + verify(objectObserver, never()).onNext(expected); + verify(objectObserver, never()).onCompleted(); + verify(objectObserver, times(1)).onError(any(Throwable.class)); + } + +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationToMultimapTest.java b/rxjava-core/src/test/java/rx/operators/OperationToMultimapTest.java new file mode 100644 index 0000000000..f715e69c61 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToMultimapTest.java @@ -0,0 +1,250 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package rx.operators; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import org.junit.Test; +import org.junit.Before; +import org.mockito.*; +import static org.mockito.Mockito.*; +import rx.Observable; +import rx.Observer; +import rx.operators.OperationToMultimap.DefaultMultimapCollectionFactory; +import rx.operators.OperationToMultimap.DefaultToMultimapFactory; +import rx.util.functions.Func0; +import rx.util.functions.Func1; +import rx.util.functions.Functions; +public class OperationToMultimapTest { + @Mock + Observer objectObserver; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + Func1 lengthFunc = new Func1() { + @Override + public Integer call(String t1) { + return t1.length(); + } + }; + Func1 duplicate = new Func1() { + @Override + public String call(String t1) { + return t1 + t1; + } + }; + @Test + public void testToMultimap() { + Observable source = Observable.from("a", "b", "cc", "dd"); + + + Observable>> mapped = Observable.create(OperationToMultimap.toMultimap(source, lengthFunc)); + + Map> expected = new HashMap>(); + expected.put(1, Arrays.asList("a", "b")); + expected.put(2, Arrays.asList("cc", "dd")); + + mapped.subscribe(objectObserver); + + verify(objectObserver, never()).onError(any(Throwable.class)); + verify(objectObserver, times(1)).onNext(expected); + verify(objectObserver, times(1)).onCompleted(); + } + @Test + public void testToMultimapWithValueSelector() { + Observable source = Observable.from("a", "b", "cc", "dd"); + + + Observable>> mapped = Observable.create(OperationToMultimap.toMultimap(source, lengthFunc, duplicate)); + + Map> expected = new HashMap>(); + expected.put(1, Arrays.asList("aa", "bb")); + expected.put(2, Arrays.asList("cccc", "dddd")); + + mapped.subscribe(objectObserver); + + verify(objectObserver, never()).onError(any(Throwable.class)); + verify(objectObserver, times(1)).onNext(expected); + verify(objectObserver, times(1)).onCompleted(); + } + @Test + public void testToMultimapWithMapFactory() { + Observable source = Observable.from("a", "b", "cc", "dd", "eee", "fff"); + + Func0>> mapFactory = new Func0>>() { + @Override + public Map> call() { + return new LinkedHashMap>() { + @Override + protected boolean removeEldestEntry(Map.Entry> eldest) { + return size() > 2; + } + }; + } + }; + + Observable>> mapped = Observable.create( + OperationToMultimap.toMultimap(source, + lengthFunc, Functions.identity(), + mapFactory, new DefaultMultimapCollectionFactory())); + + Map> expected = new HashMap>(); + expected.put(2, Arrays.asList("cc", "dd")); + expected.put(3, Arrays.asList("eee", "fff")); + + mapped.subscribe(objectObserver); + + verify(objectObserver, never()).onError(any(Throwable.class)); + verify(objectObserver, times(1)).onNext(expected); + verify(objectObserver, times(1)).onCompleted(); + } + @Test + public void testToMultimapWithCollectionFactory() { + Observable source = Observable.from("cc", "dd", "eee", "eee"); + + Func1> collectionFactory = new Func1>() { + + @Override + public Collection call(Integer t1) { + if (t1 == 2) { + return new ArrayList(); + } else { + return new HashSet(); + } + } + }; + + Observable>> mapped = Observable.create( + OperationToMultimap.toMultimap( + source, lengthFunc, Functions.identity(), + new DefaultToMultimapFactory(), collectionFactory)); + + Map> expected = new HashMap>(); + expected.put(2, Arrays.asList("cc", "dd")); + expected.put(3, new HashSet(Arrays.asList("eee"))); + + mapped.subscribe(objectObserver); + + verify(objectObserver, never()).onError(any(Throwable.class)); + verify(objectObserver, times(1)).onNext(expected); + verify(objectObserver, times(1)).onCompleted(); + } + @Test + public void testToMultimapWithError() { + Observable source = Observable.from("a", "b", "cc", "dd"); + + Func1 lengthFuncErr = new Func1() { + @Override + public Integer call(String t1) { + if ("b".equals(t1)) { + throw new RuntimeException("Forced Failure"); + } + return t1.length(); + } + }; + + Observable>> mapped = Observable.create(OperationToMultimap.toMultimap(source, lengthFuncErr)); + + Map> expected = new HashMap>(); + expected.put(1, Arrays.asList("a", "b")); + expected.put(2, Arrays.asList("cc", "dd")); + + mapped.subscribe(objectObserver); + + verify(objectObserver, times(1)).onError(any(Throwable.class)); + verify(objectObserver, never()).onNext(expected); + verify(objectObserver, never()).onCompleted(); + } + @Test + public void testToMultimapWithErrorInValueSelector() { + Observable source = Observable.from("a", "b", "cc", "dd"); + + Func1 duplicateErr = new Func1() { + @Override + public String call(String t1) { + if ("b".equals(t1)) { + throw new RuntimeException("Forced failure"); + } + return t1 + t1; + } + }; + + Observable>> mapped = Observable.create(OperationToMultimap.toMultimap(source, lengthFunc, duplicateErr)); + + Map> expected = new HashMap>(); + expected.put(1, Arrays.asList("aa", "bb")); + expected.put(2, Arrays.asList("cccc", "dddd")); + + mapped.subscribe(objectObserver); + + verify(objectObserver, times(1)).onError(any(Throwable.class)); + verify(objectObserver, never()).onNext(expected); + verify(objectObserver, never()).onCompleted(); + } + + @Test + public void testToMultimapWithMapThrowingFactory() { + Observable source = Observable.from("a", "b", "cc", "dd", "eee", "fff"); + + Func0>> mapFactory = new Func0>>() { + @Override + public Map> call() { + throw new RuntimeException("Forced failure"); + } + }; + + Observable>> mapped = Observable.create( + OperationToMultimap.toMultimap(source, lengthFunc, Functions.identity(), mapFactory)); + + Map> expected = new HashMap>(); + expected.put(2, Arrays.asList("cc", "dd")); + expected.put(3, Arrays.asList("eee", "fff")); + + mapped.subscribe(objectObserver); + + verify(objectObserver, times(1)).onError(any(Throwable.class)); + verify(objectObserver, never()).onNext(expected); + verify(objectObserver, never()).onCompleted(); + } + @Test + public void testToMultimapWithThrowingCollectionFactory() { + Observable source = Observable.from("cc", "cc", "eee", "eee"); + + Func1> collectionFactory = new Func1>() { + + @Override + public Collection call(Integer t1) { + if (t1 == 2) { + throw new RuntimeException("Forced failure"); + } else { + return new HashSet(); + } + } + }; + + Observable>> mapped = Observable.create( + OperationToMultimap.toMultimap( + source, lengthFunc, Functions.identity(), new DefaultToMultimapFactory(), collectionFactory)); + + Map> expected = new HashMap>(); + expected.put(2, Arrays.asList("cc", "dd")); + expected.put(3, Collections.singleton("eee")); + + mapped.subscribe(objectObserver); + + verify(objectObserver, times(1)).onError(any(Throwable.class)); + verify(objectObserver, never()).onNext(expected); + verify(objectObserver, never()).onCompleted(); + } +} From a4b1b5fb546708e427652c53cf1060dcaf600b2b Mon Sep 17 00:00:00 2001 From: akarnokd Date: Fri, 22 Nov 2013 23:42:09 +0100 Subject: [PATCH 310/333] Merge upstream/master into OperationJoin2 Conflicts: rxjava-core/src/main/java/rx/Observable.java --- rxjava-core/src/main/java/rx/Observable.java | 11914 +++++++++-------- 1 file changed, 5968 insertions(+), 5946 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index f510bf1a1a..06a08ef576 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -1,5946 +1,5968 @@ -/** - * Copyright 2013 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 rx.util.functions.Functions.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import rx.concurrency.Schedulers; -import rx.joins.Pattern2; -import rx.joins.Plan0; -import rx.observables.BlockingObservable; -import rx.observables.ConnectableObservable; -import rx.observables.GroupedObservable; -import rx.operators.OperationAll; -import rx.operators.OperationAmb; -import rx.operators.OperationAny; -import rx.operators.OperationAverage; -import rx.operators.OperationBuffer; -import rx.operators.OperationCache; -import rx.operators.OperationCast; -import rx.operators.OperationCombineLatest; -import rx.operators.OperationConcat; -import rx.operators.OperationDebounce; -import rx.operators.OperationDefaultIfEmpty; -import rx.operators.OperationDefer; -import rx.operators.OperationDematerialize; -import rx.operators.OperationDistinct; -import rx.operators.OperationDistinctUntilChanged; -import rx.operators.OperationDoOnEach; -import rx.operators.OperationElementAt; -import rx.operators.OperationFilter; -import rx.operators.OperationFinally; -import rx.operators.OperationFirstOrDefault; -import rx.operators.OperationGroupBy; -import rx.operators.OperationInterval; -import rx.operators.OperationJoinPatterns; -import rx.operators.OperationLast; -import rx.operators.OperationMap; -import rx.operators.OperationMaterialize; -import rx.operators.OperationMerge; -import rx.operators.OperationMergeDelayError; -import rx.operators.OperationMinMax; -import rx.operators.OperationMulticast; -import rx.operators.OperationObserveOn; -import rx.operators.OperationOnErrorResumeNextViaFunction; -import rx.operators.OperationOnErrorResumeNextViaObservable; -import rx.operators.OperationOnErrorReturn; -import rx.operators.OperationOnExceptionResumeNextViaObservable; -import rx.operators.OperationParallel; -import rx.operators.OperationParallelMerge; -import rx.operators.OperationRetry; -import rx.operators.OperationSample; -import rx.operators.OperationScan; -import rx.operators.OperationSkip; -import rx.operators.OperationSkipLast; -import rx.operators.OperationSkipWhile; -import rx.operators.OperationSubscribeOn; -import rx.operators.OperationSum; -import rx.operators.OperationSwitch; -import rx.operators.OperationSynchronize; -import rx.operators.OperationTake; -import rx.operators.OperationTakeLast; -import rx.operators.OperationTakeUntil; -import rx.operators.OperationTakeWhile; -import rx.operators.OperationThrottleFirst; -import rx.operators.OperationTimeInterval; -import rx.operators.OperationTimeout; -import rx.operators.OperationTimestamp; -import rx.operators.OperationToObservableFuture; -import rx.operators.OperationToObservableIterable; -import rx.operators.OperationToObservableList; -import rx.operators.OperationToObservableSortedList; -import rx.operators.OperationUsing; -import rx.operators.OperationWindow; -import rx.operators.OperationZip; -import rx.operators.SafeObservableSubscription; -import rx.operators.SafeObserver; -import rx.plugins.RxJavaErrorHandler; -import rx.plugins.RxJavaObservableExecutionHook; -import rx.plugins.RxJavaPlugins; -import rx.subjects.AsyncSubject; -import rx.subjects.PublishSubject; -import rx.subjects.ReplaySubject; -import rx.subjects.Subject; -import rx.subscriptions.Subscriptions; -import rx.util.Closing; -import rx.util.OnErrorNotImplementedException; -import rx.util.Opening; -import rx.util.Range; -import rx.util.TimeInterval; -import rx.util.Timestamped; -import rx.util.functions.Action0; -import rx.util.functions.Action1; -import rx.util.functions.Func0; -import rx.util.functions.Func1; -import rx.util.functions.Func2; -import rx.util.functions.Func3; -import rx.util.functions.Func4; -import rx.util.functions.Func5; -import rx.util.functions.Func6; -import rx.util.functions.Func7; -import rx.util.functions.Func8; -import rx.util.functions.Func9; -import rx.util.functions.FuncN; -import rx.util.functions.Function; - -/** - * The Observable interface that implements the Reactive Pattern. - *

    - * This interface provides overloaded methods for subscribing as well as - * delegate methods to the various operators. - *

    - * The documentation for this interface makes use of marble diagrams. The - * following legend explains these diagrams: - *

    - * - *

    - * For more information see the - * RxJava Wiki - * - * @param the type of the item emitted by the Observable - */ -public class Observable { - - private final static ConcurrentHashMap internalClassMap = new ConcurrentHashMap(); - - /** - * Executed when 'subscribe' is invoked. - */ - private final OnSubscribeFunc onSubscribe; - - /** - * Function interface for work to be performed when an {@link Observable} - * is subscribed to via {@link Observable#subscribe(Observer)} - * - * @param - */ - public static interface OnSubscribeFunc extends Function { - - public Subscription onSubscribe(Observer t1); - - } - - /** - * Observable with Function to execute when subscribed to. - *

    - * NOTE: Use {@link #create(OnSubscribeFunc)} to create an Observable - * instead of this constructor unless you specifically have a need for - * inheritance. - * - * @param onSubscribe {@link OnSubscribeFunc} to be executed when - * {@link #subscribe(Observer)} is called - */ - protected Observable(OnSubscribeFunc onSubscribe) { - this.onSubscribe = onSubscribe; - } - - private final static RxJavaObservableExecutionHook hook = RxJavaPlugins.getInstance().getObservableExecutionHook(); - - /** - * An {@link Observer} must call an Observable's {@code subscribe} method in - * order to receive items and notifications from the Observable. - *

    - * A typical implementation of {@code subscribe} does the following: - *

      - *
    1. It stores a reference to the Observer in a collection object, such as - * a {@code List} object.
    2. - *
    3. It returns a reference to the {@link Subscription} interface. This - * enables Observers to unsubscribe, that is, to stop receiving items - * and notifications before the Observable stops sending them, which - * also invokes the Observer's {@link Observer#onCompleted onCompleted} - * method.
    4. - *

    - * An Observable<T> instance is responsible for accepting - * all subscriptions and notifying all Observers. Unless the documentation - * for a particular Observable<T> implementation - * indicates otherwise, Observers should make no assumptions about the order - * in which multiple Observers will receive their notifications. - *

    - * For more information see the - * RxJava Wiki - * - * @param observer the Observer - * @return a {@link Subscription} reference with which the {@link Observer} - * can stop receiving items before the Observable has finished - * sending them - * @throws IllegalArgumentException if the {@link Observer} provided as the - * argument to {@code subscribe()} is - * {@code null} - */ - public Subscription subscribe(Observer observer) { - // allow the hook to intercept and/or decorate - OnSubscribeFunc onSubscribeFunction = hook.onSubscribeStart(this, onSubscribe); - // validate and proceed - if (observer == null) { - throw new IllegalArgumentException("observer can not be null"); - } - if (onSubscribeFunction == null) { - throw new IllegalStateException("onSubscribe function can not be null."); - // the subscribe function can also be overridden but generally that's not the appropriate approach so I won't mention that in the exception - } - try { - /** - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - if (isInternalImplementation(observer)) { - Subscription s = onSubscribeFunction.onSubscribe(observer); - if (s == null) { - // this generally shouldn't be the case on a 'trusted' onSubscribe but in case it happens - // we want to gracefully handle it the same as AtomicObservableSubscription does - return hook.onSubscribeReturn(this, Subscriptions.empty()); - } else { - return hook.onSubscribeReturn(this, s); - } - } else { - SafeObservableSubscription subscription = new SafeObservableSubscription(); - subscription.wrap(onSubscribeFunction.onSubscribe(new SafeObserver(subscription, observer))); - return hook.onSubscribeReturn(this, subscription); - } - } catch (OnErrorNotImplementedException e) { - // special handling when onError is not implemented ... we just rethrow - throw e; - } catch (Throwable e) { - // if an unhandled error occurs executing the onSubscribe we will propagate it - try { - observer.onError(hook.onSubscribeError(this, e)); - } catch (OnErrorNotImplementedException e2) { - // special handling when onError is not implemented ... we just rethrow - throw e2; - } catch (Throwable e2) { - // if this happens it means the onError itself failed (perhaps an invalid function implementation) - // so we are unable to propagate the error correctly and will just throw - RuntimeException r = new RuntimeException("Error occurred attempting to subscribe [" + e.getMessage() + "] and then again while trying to pass to onError.", e2); - hook.onSubscribeError(this, r); - throw r; - } - return Subscriptions.empty(); - } - } - - /** - * An {@link Observer} must call an Observable's {@code subscribe} method in - * order to receive items and notifications from the Observable. - *

    - * A typical implementation of {@code subscribe} does the following: - *

      - *
    1. It stores a reference to the Observer in a collection object, such as - * a {@code List} object.
    2. - *
    3. It returns a reference to the {@link Subscription} interface. This - * enables Observers to unsubscribe, that is, to stop receiving items - * and notifications before the Observable stops sending them, which - * also invokes the Observer's {@link Observer#onCompleted onCompleted} - * method.
    4. - *

    - * An {@code Observable} instance is responsible for accepting all - * subscriptions and notifying all Observers. Unless the documentation for a - * particular {@code Observable} implementation indicates otherwise, - * Observers should make no assumptions about the order in which multiple - * Observers will receive their notifications. - *

    - * For more information see the - * RxJava Wiki - * - * @param observer the Observer - * @param scheduler the {@link Scheduler} on which Observers subscribe to - * the Observable - * @return a {@link Subscription} reference with which Observers can stop - * receiving items and notifications before the Observable has - * finished sending them - * @throws IllegalArgumentException if an argument to {@code subscribe()} - * is {@code null} - */ - public Subscription subscribe(Observer observer, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(observer); - } - - /** - * Protects against errors being thrown from Observer implementations and - * ensures onNext/onError/onCompleted contract compliance. - *

    - * See https://github.com/Netflix/RxJava/issues/216 for a discussion on - * "Guideline 6.4: Protect calls to user code from within an operator" - */ - private Subscription protectivelyWrapAndSubscribe(Observer o) { - SafeObservableSubscription subscription = new SafeObservableSubscription(); - return subscription.wrap(subscribe(new SafeObserver(subscription, o))); - } - - /** - * Subscribe and ignore all events. - * - * @return - */ - public Subscription subscribe() { - return protectivelyWrapAndSubscribe(new Observer() { - - @Override - public void onCompleted() { - // do nothing - } - - @Override - public void onError(Throwable e) { - handleError(e); - throw new OnErrorNotImplementedException(e); - } - - @Override - public void onNext(T args) { - // do nothing - } - - }); - } - - /** - * An {@link Observer} must call an Observable's {@code subscribe} method - * in order to receive items and notifications from the Observable. - * - * @param onNext - * @return - */ - public Subscription subscribe(final Action1 onNext) { - if (onNext == null) { - throw new IllegalArgumentException("onNext can not be null"); - } - - /** - * Wrapping since raw functions provided by the user are being invoked. - * - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - return protectivelyWrapAndSubscribe(new Observer() { - - @Override - public void onCompleted() { - // do nothing - } - - @Override - public void onError(Throwable e) { - handleError(e); - throw new OnErrorNotImplementedException(e); - } - - @Override - public void onNext(T args) { - onNext.call(args); - } - - }); - } - - /** - * An {@link Observer} must call an Observable's {@code subscribe} method in - * order to receive items and notifications from the Observable. - * - * @param onNext - * @param scheduler - * @return - */ - public Subscription subscribe(final Action1 onNext, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(onNext); - } - - /** - * An {@link Observer} must call an Observable's {@code subscribe} method in - * order to receive items and notifications from the Observable. - * - * @param onNext - * @param onError - * @return - */ - public Subscription subscribe(final Action1 onNext, final Action1 onError) { - if (onNext == null) { - throw new IllegalArgumentException("onNext can not be null"); - } - if (onError == null) { - throw new IllegalArgumentException("onError can not be null"); - } - - /** - * Wrapping since raw functions provided by the user are being invoked. - * - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - return protectivelyWrapAndSubscribe(new Observer() { - - @Override - public void onCompleted() { - // do nothing - } - - @Override - public void onError(Throwable e) { - handleError(e); - onError.call(e); - } - - @Override - public void onNext(T args) { - onNext.call(args); - } - - }); - } - - /** - * An {@link Observer} must call an Observable's {@code subscribe} method in - * order to receive items and notifications from the Observable. - * - * @param onNext - * @param onError - * @param scheduler - * @return - */ - public Subscription subscribe(final Action1 onNext, final Action1 onError, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(onNext, onError); - } - - /** - * An {@link Observer} must call an Observable's {@code subscribe} method in - * order to receive items and notifications from the Observable. - * - * @param onNext - * @param onError - * @param onComplete - * @return - */ - public Subscription subscribe(final Action1 onNext, final Action1 onError, final Action0 onComplete) { - if (onNext == null) { - throw new IllegalArgumentException("onNext can not be null"); - } - if (onError == null) { - throw new IllegalArgumentException("onError can not be null"); - } - if (onComplete == null) { - throw new IllegalArgumentException("onComplete can not be null"); - } - - /** - * Wrapping since raw functions provided by the user are being invoked. - * - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - return protectivelyWrapAndSubscribe(new Observer() { - - @Override - public void onCompleted() { - onComplete.call(); - } - - @Override - public void onError(Throwable e) { - handleError(e); - onError.call(e); - } - - @Override - public void onNext(T args) { - onNext.call(args); - } - - }); - } - - /** - * An {@link Observer} must call an Observable's {@code subscribe} method in - * order to receive items and notifications from the Observable. - * - * @param onNext - * @param onError - * @param onComplete - * @param scheduler - * @return - */ - public Subscription subscribe(final Action1 onNext, final Action1 onError, final Action0 onComplete, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(onNext, onError, onComplete); - } - - /** - * Returns a {@link ConnectableObservable} that upon connection causes the - * source Observable to push results into the specified subject. - * - * @param subject the {@link Subject} for the {@link ConnectableObservable} - * to push source items into - * @param result type - * @return a {@link ConnectableObservable} that upon connection causes the - * source Observable to push results into the specified - * {@link Subject} - * @see Observable.publish() and Observable.multicast() - */ - public ConnectableObservable multicast(Subject subject) { - return OperationMulticast.multicast(this, subject); - } - - /** - * Allow the {@link RxJavaErrorHandler} to receive the exception from - * onError. - * - * @param e - */ - private void handleError(Throwable e) { - // onError should be rare so we'll only fetch when needed - RxJavaPlugins.getInstance().getErrorHandler().handleError(e); - } - - /** - * An Observable that never sends any information to an {@link Observer}. - * - * This Observable is useful primarily for testing purposes. - * - * @param the type of item emitted by the Observable - */ - private static class NeverObservable extends Observable { - public NeverObservable() { - super(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(Observer t1) { - return Subscriptions.empty(); - } - - }); - } - } - - /** - * An Observable that invokes {@link Observer#onError onError} when the - * {@link Observer} subscribes to it. - * - * @param the type of item emitted by the Observable - */ - private static class ThrowObservable extends Observable { - - public ThrowObservable(final Throwable exception) { - super(new OnSubscribeFunc() { - - /** - * Accepts an {@link Observer} and calls its - * {@link Observer#onError onError} method. - * - * @param observer an {@link Observer} of this Observable - * @return a reference to the subscription - */ - @Override - public Subscription onSubscribe(Observer observer) { - observer.onError(exception); - return Subscriptions.empty(); - } - - }); - } - - } - - /** - * Creates an Observable that will execute the given function when an - * {@link Observer} subscribes to it. - *

    - * - *

    - * Write the function you pass to create so that it behaves as - * an Observable: It should invoke the Observer's - * {@link Observer#onNext onNext}, {@link Observer#onError onError}, and - * {@link Observer#onCompleted onCompleted} methods appropriately. - *

    - * A well-formed Observable must invoke either the Observer's - * onCompleted method exactly once or its onError - * method exactly once. - *

    - * See Rx Design - * Guidelines (PDF) for detailed information. - * - * @param the type of the items that this Observable emits - * @param func a function that accepts an {@code Observer}, invokes its - * {@code onNext}, {@code onError}, and {@code onCompleted} - * methods as appropriate, and returns a {@link Subscription} to - * allow the Observer to cancel the subscription - * @return an Observable that, when an {@link Observer} subscribes to it, - * will execute the given function - * @see create() - */ - public static Observable create(OnSubscribeFunc func) { - return new Observable(func); - } - - /** - * Returns an Observable that emits no data to the {@link Observer} and - * immediately invokes its {@link Observer#onCompleted onCompleted} method. - *

    - * - * - * @param the type of the items (ostensibly) emitted by the Observable - * @return an Observable that returns no data to the {@link Observer} and - * immediately invokes the {@link Observer}'s - * {@link Observer#onCompleted() onCompleted} method - * @see empty() - * @see MSDN: Observable.Empty Method - */ - public static Observable empty() { - return from(new ArrayList()); - } - - /** - * Returns an Observable that emits no data to the {@link Observer} and - * immediately invokes its {@link Observer#onCompleted onCompleted} method - * with the specified scheduler. - *

    - * - * - * @param scheduler the scheduler to call the - {@link Observer#onCompleted onCompleted} method - * @param the type of the items (ostensibly) emitted by the Observable - * @return an Observable that returns no data to the {@link Observer} and - * immediately invokes the {@link Observer}'s - * {@link Observer#onCompleted() onCompleted} method with the - * specified scheduler - * @see empty() - * @see MSDN: Observable.Empty Method (IScheduler) - */ - public static Observable empty(Scheduler scheduler) { - return Observable. empty().subscribeOn(scheduler); - } - - /** - * Returns an Observable that invokes an {@link Observer}'s - * {@link Observer#onError onError} method when the Observer subscribes to - * it. - *

    - * - * - * @param exception the particular error to report - * @param the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the {@link Observer}'s - * {@link Observer#onError onError} method when the Observer - * subscribes to it - * @see error() - * @see MSDN: Observable.Throw Method - */ - public static Observable error(Throwable exception) { - return new ThrowObservable(exception); - } - - /** - * Returns an Observable that invokes an {@link Observer}'s - * {@link Observer#onError onError} method with the specified scheduler. - *

    - * - * - * @param exception the particular error to report - * @param scheduler the scheduler to call the - * {@link Observer#onError onError} method - * @param the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the {@link Observer}'s - * {@link Observer#onError onError} method with the specified - * scheduler - * @see error() - * @see MSDN: Observable.Throw Method - */ - public static Observable error(Throwable exception, Scheduler scheduler) { - return Observable. error(exception).subscribeOn(scheduler); - } - - /** - * Converts an {@link Iterable} sequence into an Observable. - *

    - * - *

    - * Note: the entire iterable sequence is immediately emitted each time an - * {@link Observer} subscribes. Since this occurs before the - * {@link Subscription} is returned, it is not possible to unsubscribe from - * the sequence before it completes. - * - * @param iterable the source {@link Iterable} sequence - * @param the type of items in the {@link Iterable} sequence and the - * type of items to be emitted by the resulting Observable - * @return an Observable that emits each item in the source {@link Iterable} - * sequence - * @see from() - */ - public static Observable from(Iterable iterable) { - return create(OperationToObservableIterable.toObservableIterable(iterable)); - } - - /** - * Converts an {@link Iterable} sequence into an Observable with the specified scheduler. - *

    - * - * - * @param iterable the source {@link Iterable} sequence - * @param scheduler the scheduler to emit the items of the iterable - * @param the type of items in the {@link Iterable} sequence and the - * type of items to be emitted by the resulting Observable - * @return an Observable that emits each item in the source {@link Iterable} - * sequence with the specified scheduler - * @see from() - * @see MSDN: Observable.ToObservable - */ - public static Observable from(Iterable iterable, Scheduler scheduler) { - return from(iterable).observeOn(scheduler); - } - - /** - * Converts an Array into an Observable. - *

    - * - *

    - * Note: the entire array is immediately emitted each time an - * {@link Observer} subscribes. Since this occurs before the - * {@link Subscription} is returned, it is not possible to unsubscribe from - * the sequence before it completes. - * - * @param items the source sequence - * @param the type of items in the Array and the type of items to be - * emitted by the resulting Observable - * @return an Observable that emits each item in the source Array - * @see from() - */ - public static Observable from(T[] items) { - return create(OperationToObservableIterable.toObservableIterable(Arrays.asList(items))); - } - - /** - * Converts an item into an Observable that emits that item. - *

    - * - *

    - * Note: the item is immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is - * returned, it is not possible to unsubscribe from the sequence before it - * completes. - * - * @param t1 the item - * @param the type of the item, and the type of the item to be - * emitted by the resulting Observable - * @return an Observable that emits the item - * @see from() - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1) { - return from(Arrays.asList(t1)); - } - - /** - * Converts a series of items into an Observable. - *

    - * - *

    - * Note: the items will be immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is - * returned, it is not possible to unsubscribe from the sequence before it - * completes. - * - * @param t1 first item - * @param t2 second item - * @param the type of items, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item - * @see from() - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2) { - return from(Arrays.asList(t1, t2)); - } - - /** - * Converts a series of items into an Observable. - *

    - * - *

    - * Note: the items will be immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is - * returned, it is not possible to unsubscribe from the sequence before it - * completes. - * - * @param t1 first item - * @param t2 second item - * @param t3 third item - * @param the type of items, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item - * @see from() - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3) { - return from(Arrays.asList(t1, t2, t3)); - } - - /** - * Converts a series of items into an Observable. - *

    - * - *

    - * Note: the items will be immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is - * returned, it is not possible to unsubscribe from the sequence before it - * completes. - * - * @param t1 first item - * @param t2 second item - * @param t3 third item - * @param t4 fourth item - * @param the type of items, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item - * @see from() - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4) { - return from(Arrays.asList(t1, t2, t3, t4)); - } - - /** - * Converts a series of items into an Observable. - *

    - * - *

    - * Note: the items will be immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is - * returned, it is not possible to unsubscribe from the sequence before it - * completes. - * - * @param t1 first item - * @param t2 second item - * @param t3 third item - * @param t4 fourth item - * @param t5 fifth item - * @param the type of items, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item - * @see from() - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5) { - return from(Arrays.asList(t1, t2, t3, t4, t5)); - } - - /** - * Converts a series of items into an Observable. - *

    - * - *

    - * Note: the items will be immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is - * returned, it is not possible to unsubscribe from the sequence before it - * completes. - * - * @param t1 first item - * @param t2 second item - * @param t3 third item - * @param t4 fourth item - * @param t5 fifth item - * @param t6 sixth item - * @param the type of items, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item - * @see from() - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6) { - return from(Arrays.asList(t1, t2, t3, t4, t5, t6)); - } - - /** - * Converts a series of items into an Observable. - *

    - * - *

    - * Note: the items will be immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is - * returned, it is not possible to unsubscribe from the sequence before it - * completes. - * - * @param t1 first item - * @param t2 second item - * @param t3 third item - * @param t4 fourth item - * @param t5 fifth item - * @param t6 sixth item - * @param t7 seventh item - * @param the type of items, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item - * @see from() - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { - return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7)); - } - - /** - * Converts a series of items into an Observable. - *

    - * - *

    - * Note: the items will be immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is - * returned, it is not possible to unsubscribe from the sequence before it - * completes. - * - * @param t1 first item - * @param t2 second item - * @param t3 third item - * @param t4 fourth item - * @param t5 fifth item - * @param t6 sixth item - * @param t7 seventh item - * @param t8 eighth item - * @param the type of items, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item - * @see from() - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { - return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8)); - } - - /** - * Converts a series of items into an Observable. - *

    - * - *

    - * Note: the items will be immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is - * returned, it is not possible to unsubscribe from the sequence before it - * completes. - * - * @param t1 first item - * @param t2 second item - * @param t3 third item - * @param t4 fourth item - * @param t5 fifth item - * @param t6 sixth item - * @param t7 seventh item - * @param t8 eighth item - * @param t9 ninth item - * @param the type of items, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item - * @see from() - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { - return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8, t9)); - } - - /** - * Converts a series of items into an Observable. - *

    - * - *

    - * Note: the items will be immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is - * returned, it is not possible to unsubscribe from the sequence before it - * completes. - * - * @param t1 first item - * @param t2 second item - * @param t3 third item - * @param t4 fourth item - * @param t5 fifth item - * @param t6 sixth item - * @param t7 seventh item - * @param t8 eighth item - * @param t9 ninth item - * @param t10 tenth item - * @param the type of items, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item - * @see from() - */ - @SuppressWarnings("unchecked") - // suppress unchecked because we are using varargs inside the method - public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9, T t10) { - return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)); - } - - /** - * Generates an Observable that emits a sequence of integers within a - * specified range. - *

    - * - *

    - * Note: the entire range is immediately emitted each time an - * {@link Observer} subscribes. Since this occurs before the - * {@link Subscription} is returned, it is not possible to unsubscribe from - * the sequence before it completes. - * - * @param start the value of the first integer in the sequence - * @param count the number of sequential integers to generate - * @return an Observable that emits a range of sequential integers - * @see range() - * @see Observable.Range Method (Int32, Int32) - */ - public static Observable range(int start, int count) { - return from(Range.createWithCount(start, count)); - } - - /** - * Generates an Observable that emits a sequence of integers within a - * specified range with the specified scheduler. - *

    - * - * @param start the value of the first integer in the sequence - * @param count the number of sequential integers to generate - * @param scheduler the scheduler to run the generator loop on - * @return an Observable that emits a range of sequential integers - * @see range() - * @see Observable.Range Method (Int32, Int32, IScheduler) - */ - public static Observable range(int start, int count, Scheduler scheduler) { - return range(start, count).observeOn(scheduler); - } - - /** - * Returns an Observable that calls an Observable factory to create its - * Observable for each new Observer that subscribes. That is, for each - * subscriber, the actuall Observable is determined by the factory function. - *

    - * - *

    - * The defer operator allows you to defer or delay emitting items from an - * Observable until such time as an Observer subscribes to the Observable. - * This allows an {@link Observer} to easily obtain updates or a refreshed - * version of the sequence. - * - * @param observableFactory the Observable factory function to invoke for - * each {@link Observer} that subscribes to the - * resulting Observable - * @param the type of the items emitted by the Observable - * @return an Observable whose {@link Observer}s trigger an invocation of - * the given Observable factory function - * @see defer() - */ - public static Observable defer(Func0> observableFactory) { - return create(OperationDefer.defer(observableFactory)); - } - - /** - * Returns an Observable that emits a single item and then completes. - *

    - * - *

    - * To convert any object into an Observable that emits that object, pass - * that object into the just method. - *

    - * This is similar to the {@link #from(java.lang.Object[])} method, except - * that from() will convert an {@link Iterable} object into an - * Observable that emits each of the items in the Iterable, one at a time, - * while the just() method converts an Iterable into an - * Observable that emits the entire Iterable as a single item. - * - * @param value the item to pass to the {@link Observer}'s - * {@link Observer#onNext onNext} method - * @param the type of that item - * @return an Observable that emits a single item and then completes - * @see just() - */ - public static Observable just(T value) { - List list = new ArrayList(); - list.add(value); - - return from(list); - } - - /** - * Returns an Observable that emits a single item and then completes on a - * specified scheduler. - *

    - * This is a scheduler version of {@link Observable#just(Object)}. - * - * @param value the item to pass to the {@link Observer}'s - * {@link Observer#onNext onNext} method - * @param the type of that item - * @param scheduler the scheduler to send the single element on - * @return an Observable that emits a single item and then completes on a - * specified scheduler - * @see just() - */ - public static Observable just(T value, Scheduler scheduler) { - return just(value).observeOn(scheduler); - } - - /** - * Flattens a sequence of Observables emitted by an Observable into one - * Observable, without any transformation. - *

    - * - *

    - * You can combine the items emitted by multiple Observables so that they - * act like a single Observable, by using the {@code merge} method. - * - * @param source an Observable that emits Observables - * @return an Observable that emits items that are the result of flattening - * the items emitted by the Observables emitted by the - * {@code source} Observable - * @see merge() - * @see MSDN: Observable.Merge Method - */ - public static Observable merge(Observable> source) { - return create(OperationMerge.merge(source)); - } - - /** - * Flattens a series of Observables into one Observable, without any - * transformation. - *

    - * - *

    - * You can combine items emitted by multiple Observables so that they act - * like a single Observable, by using the {@code merge} method. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see merge() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2) { - return create(OperationMerge.merge(t1, t2)); - } - - /** - * Flattens a series of Observables into one Observable, without any - * transformation. - *

    - * - *

    - * You can combine items emitted by multiple Observables so that they act - * like a single Observable, by using the {@code merge} method. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see merge() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3) { - return create(OperationMerge.merge(t1, t2, t3)); - } - - /** - * Flattens a series of Observables into one Observable, without any - * transformation. - *

    - * - *

    - * You can combine items emitted by multiple Observables so that they act - * like a single Observable, by using the {@code merge} method. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see merge() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4) { - return create(OperationMerge.merge(t1, t2, t3, t4)); - } - - /** - * Flattens a series of Observables into one Observable, without any - * transformation. - *

    - * - *

    - * You can combine items emitted by multiple Observables so that they act - * like a single Observable, by using the {@code merge} method. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @param t5 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see merge() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { - return create(OperationMerge.merge(t1, t2, t3, t4, t5)); - } - - /** - * Flattens a series of Observables into one Observable, without any - * transformation. - *

    - * - *

    - * You can combine items emitted by multiple Observables so that they act - * like a single Observable, by using the {@code merge} method. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @param t5 an Observable to be merged - * @param t6 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see merge() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { - return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6)); - } - - /** - * Flattens a series of Observables into one Observable, without any - * transformation. - *

    - * - *

    - * You can combine items emitted by multiple Observables so that they act - * like a single Observable, by using the {@code merge} method. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @param t5 an Observable to be merged - * @param t6 an Observable to be merged - * @param t7 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see merge() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { - return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6, t7)); - } - - /** - * Flattens a series of Observables into one Observable, without any - * transformation. - *

    - * - *

    - * You can combine items emitted by multiple Observables so that they act - * like a single Observable, by using the {@code merge} method. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @param t5 an Observable to be merged - * @param t6 an Observable to be merged - * @param t7 an Observable to be merged - * @param t8 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see merge() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { - return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6, t7, t8)); - } - - /** - * Flattens a series of Observables into one Observable, without any - * transformation. - *

    - * - *

    - * You can combine items emitted by multiple Observables so that they act - * like a single Observable, by using the {@code merge} method. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @param t5 an Observable to be merged - * @param t6 an Observable to be merged - * @param t7 an Observable to be merged - * @param t8 an Observable to be merged - * @param t9 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see merge() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8, Observable t9) { - return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6, t7, t8, t9)); - } - - /** - * Returns an Observable that emits the items emitted by two or more - * Observables, one after the other. - *

    - * - * - * @param observables an Observable that emits Observables - * @return an Observable that emits items that are the result of combining - * the items emitted by the {@code source} Observables, one after - * the other - * @see concat() - * @see MSDN: Observable.Concat Method - */ - public static Observable concat(Observable> observables) { - return create(OperationConcat.concat(observables)); - } - - /** - * Returns an Observable that emits the items emitted by two Observables, - * one after the other. - *

    - * - * - * @param t1 an Observable to be concatenated - * @param t2 an Observable to be concatenated - * @return an Observable that emits items that are the result of combining - * the items emitted by the {@code source} Observables, one after - * the other - * @see concat() - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2) { - return create(OperationConcat.concat(t1, t2)); - } - - /** - * Returns an Observable that emits the items emitted by three Observables, - * one after the other. - *

    - * - * - * @param t1 an Observable to be concatenated - * @param t2 an Observable to be concatenated - * @param t3 an Observable to be concatenated - * - * @return an Observable that emits items that are the result of combining - * the items emitted by the {@code source} Observables, one after - * the other - * @see concat() - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3) { - return create(OperationConcat.concat(t1, t2, t3)); - } - - /** - * Returns an Observable that emits the items emitted by four Observables, - * one after the other. - *

    - * - * - * @param t1 an Observable to be concatenated - * @param t2 an Observable to be concatenated - * @param t3 an Observable to be concatenated - * @param t4 an Observable to be concatenated - * @return an Observable that emits items that are the result of combining - * the items emitted by the {@code source} Observables, one after - * the other - * @see concat() - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4) { - return create(OperationConcat.concat(t1, t2, t3, t4)); - } - - /** - * Returns an Observable that emits the items emitted by five Observables, - * one after the other. - *

    - * - * - * @param t1 an Observable to be concatenated - * @param t2 an Observable to be concatenated - * @param t3 an Observable to be concatenated - * @param t4 an Observable to be concatenated - * @param t5 an Observable to be concatenated - * @return an Observable that emits items that are the result of combining - * the items emitted by the {@code source} Observables, one after - * the other - * @see concat() - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { - return create(OperationConcat.concat(t1, t2, t3, t4, t5)); - } - - /** - * Returns an Observable that emits the items emitted by six Observables, - * one after the other. - *

    - * - * - * @param t1 an Observable to be concatenated - * @param t2 an Observable to be concatenated - * @param t3 an Observable to be concatenated - * @param t4 an Observable to be concatenated - * @param t5 an Observable to be concatenated - * @param t6 an Observable to be concatenated - * @return an Observable that emits items that are the result of combining - * the items emitted by the {@code source} Observables, one after - * the other - * @see concat() - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { - return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6)); - } - - /** - * Returns an Observable that emits the items emitted by secven Observables, - * one after the other. - *

    - * - * - * @param t1 an Observable to be concatenated - * @param t2 an Observable to be concatenated - * @param t3 an Observable to be concatenated - * @param t4 an Observable to be concatenated - * @param t5 an Observable to be concatenated - * @param t6 an Observable to be concatenated - * @param t7 an Observable to be concatenated - * @return an Observable that emits items that are the result of combining - * the items emitted by the {@code source} Observables, one after - * the other - * @see concat() - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { - return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6, t7)); - } - - /** - * Returns an Observable that emits the items emitted by eight Observables, - * one after the other. - *

    - * - * - * @param t1 an Observable to be concatenated - * @param t2 an Observable to be concatenated - * @param t3 an Observable to be concatenated - * @param t4 an Observable to be concatenated - * @param t5 an Observable to be concatenated - * @param t6 an Observable to be concatenated - * @param t7 an Observable to be concatenated - * @param t8 an Observable to be concatenated - * @return an Observable that emits items that are the result of combining - * the items emitted by the {@code source} Observables, one after - * the other - * @see concat() - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { - return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6, t7, t8)); - } - - /** - * Returns an Observable that emits the items emitted by nine Observables, - * one after the other. - *

    - * - * - * @param t1 an Observable to be concatenated - * @param t2 an Observable to be concatenated - * @param t3 an Observable to be concatenated - * @param t4 an Observable to be concatenated - * @param t5 an Observable to be concatenated - * @param t6 an Observable to be concatenated - * @param t7 an Observable to be concatenated - * @param t8 an Observable to be concatenated - * @param t9 an Observable to be concatenated - * @return an Observable that emits items that are the result of combining - * the items emitted by the {@code source} Observables, one after - * the other - * @see concat() - * @see MSDN: Observable.Concat Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8, Observable t9) { - return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6, t7, t8, t9)); - } - - /** - * This behaves like {@link #merge(Observable)} except that if any of the - * merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged - * Observables have finished emitting items. - *

    - * - *

    - * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of - * its Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items - * from all of the source Observables without being interrupted by an error - * notification from one of them. - * - * @param source an Observable that emits Observables - * @return an Observable that emits items that are the result of flattening - * the items emitted by the Observables emitted by the - * {@code source} Observable - * @see mergeDelayError() - * @see MSDN: Observable.Merge Method - */ - public static Observable mergeDelayError(Observable> source) { - return create(OperationMergeDelayError.mergeDelayError(source)); - } - - /** - * This behaves like {@link #merge(Observable, Observable)} except that if - * any of the merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged - * Observables have finished emitting items. - *

    - * - *

    - * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of - * its Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items - * from all of the source Observables without being interrupted by an error - * notification from one of them. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see mergeDelayError() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2)); - } - - /** - * This behaves like {@link #merge(Observable, Observable, Observable)} - * except that if any of the merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged - * Observables have finished emitting items. - *

    - * - *

    - * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of - * its Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items - * from all of the source Observables without being interrupted by an error - * notification from one of them. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see mergeDelayError() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3)); - } - - /** - * This behaves like - * {@link #merge(Observable, Observable, Observable, Observable)} except - * that if any of the merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged - * Observables have finished emitting items. - *

    - * - *

    - * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of - * its Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items - * from all of the source Observables without being interrupted by an error - * notification from one of them. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see mergeDelayError() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4)); - } - - /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable)} - * except that if any of the merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged - * Observables have finished emitting items. - *

    - * - *

    - * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of - * its Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items - * from all of the source Observables without being interrupted by an error - * notification from one of them. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @param t5 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see mergeDelayError() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5)); - } - - /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable)} - * except that if any of the merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged - * Observables have finished emitting items. - *

    - * - *

    - * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of - * its Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items - * from all of the source Observables without being interrupted by an error - * notification from one of them. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @param t5 an Observable to be merged - * @param t6 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see mergeDelayError() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6)); - } - - /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable)} - * except that if any of the merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged - * Observables have finished emitting items. - *

    - * - *

    - * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of - * its Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items - * from all of the source Observables without being interrupted by an error - * notification from one of them. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @param t5 an Observable to be merged - * @param t6 an Observable to be merged - * @param t7 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see mergeDelayError() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6, t7)); - } - - /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} - * except that if any of the merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged - * Observables have finished emitting items. - *

    - * - *

    - * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of - * its Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items - * from all of the source Observables without being interrupted by an error - * notification from one of them. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @param t5 an Observable to be merged - * @param t6 an Observable to be merged - * @param t7 an Observable to be merged - * @param t8 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see mergeDelayError() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6, t7, t8)); - } - - /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} - * except that if any of the merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged - * Observables have finished emitting items. - *

    - * - *

    - * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of - * its Observers once. - *

    - * This method allows an Observer to receive all successfully emitted items - * from all of the source Observables without being interrupted by an error - * notification from one of them. - * - * @param t1 an Observable to be merged - * @param t2 an Observable to be merged - * @param t3 an Observable to be merged - * @param t4 an Observable to be merged - * @param t5 an Observable to be merged - * @param t6 an Observable to be merged - * @param t7 an Observable to be merged - * @param t8 an Observable to be merged - * @param t9 an Observable to be merged - * @return an Observable that emits items that are the result of flattening - * the items emitted by the {@code source} Observables - * @see mergeDelayError() - * @see MSDN: Observable.Merge Method - */ - @SuppressWarnings("unchecked") - // suppress because the types are checked by the method signature before using a vararg - public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8, Observable t9) { - return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6, t7, t8, t9)); - } - - /** - * Returns an Observable that never sends any items or notifications to an - * {@link Observer}. - *

    - * - *

    - * This Observable is useful primarily for testing purposes. - * - * @param the type of items (not) emitted by the Observable - * @return an Observable that never sends any items or notifications to an - * {@link Observer} - * @see never() - */ - public static Observable never() { - return new NeverObservable(); - } - - /** - * Given an Observable that emits Observables, creates a single Observable - * that emits the items emitted by the most recently published of those - * Observables. - *

    - * - * - * @param sequenceOfSequences the source Observable that emits Observables - * @return an Observable that emits only the items emitted by the most - * recently published Observable - * @see switchOnNext() - * @deprecated use {@link #switchOnNext} - */ - @Deprecated - public static Observable switchDo(Observable> sequenceOfSequences) { - return create(OperationSwitch.switchDo(sequenceOfSequences)); - } - - /** - * Given an Observable that emits Observables, creates a single Observable - * that emits the items emitted by the most recently published of those - * Observables. - *

    - * - * - * @param sequenceOfSequences the source Observable that emits Observables - * @return an Observable that emits only the items emitted by the most - * recently published Observable - * @see switchOnNext() - */ - public static Observable switchOnNext(Observable> sequenceOfSequences) { - return create(OperationSwitch.switchDo(sequenceOfSequences)); - } - - /** - * Accepts an Observable and wraps it in another Observable that ensures - * that the resulting Observable is chronologically well-behaved. - *

    - * - *

    - * A well-behaved Observable does not interleave its invocations of the - * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, - * and {@link Observer#onError onError} methods of its {@link Observer}s; it - * invokes {@code onCompleted} or {@code onError} only once; and it never - * invokes {@code onNext} after invoking either {@code onCompleted} or - * {@code onError}. {@code synchronize} enforces this, and the Observable it - * returns invokes {@code onNext} and {@code onCompleted} or {@code onError} - * synchronously. - * - * @return an Observable that is a chronologically well-behaved version of - * the source Observable, and that synchronously notifies its - * {@link Observer}s - * @see synchronize() - */ - public Observable synchronize() { - return create(OperationSynchronize.synchronize(this)); - } - - /** - * Accepts an Observable and wraps it in another Observable that ensures - * that the resulting Observable is chronologically well-behaved. This is - * accomplished by acquiring a mutual-exclusion lock for the object - * provided as the lock parameter. - *

    - * - *

    - * A well-behaved Observable does not interleave its invocations of the - * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, - * and {@link Observer#onError onError} methods of its {@link Observer}s; it - * invokes {@code onCompleted} or {@code onError} only once; and it never - * invokes {@code onNext} after invoking either {@code onCompleted} or - * {@code onError}. {@code synchronize} enforces this, and the Observable it - * returns invokes {@code onNext} and {@code onCompleted} or {@code onError} - * synchronously. - * - * @param lock the lock object to synchronize each observer call on - * @return an Observable that is a chronologically well-behaved version of - * the source Observable, and that synchronously notifies its - * {@link Observer}s - * @see synchronize() - */ - public Observable synchronize(Object lock) { - return create(OperationSynchronize.synchronize(this, lock)); - } - - /** - * @deprecated use {@link #synchronize()} or {@link #synchronize(Object)} - */ - @Deprecated - public static Observable synchronize(Observable source) { - return create(OperationSynchronize.synchronize(source)); - } - - /** - * Emits an item each time interval (containing a sequential number). - *

    - * - * - * @param interval interval size in time units (see below) - * @param unit time units to use for the interval size - * @return an Observable that emits an item each time interval - * @see interval() - * @see MSDN: Observable.Interval - */ - public static Observable interval(long interval, TimeUnit unit) { - return create(OperationInterval.interval(interval, unit)); - } - - /** - * Emits an item each time interval (containing a sequential number). - *

    - * - * - * @param interval interval size in time units (see below) - * @param unit time units to use for the interval size - * @param scheduler the scheduler to use for scheduling the items - * @return an Observable that emits an item each time interval - * @see interval() - * @see MSDN: Observable.Interval - */ - public static Observable interval(long interval, TimeUnit unit, Scheduler scheduler) { - return create(OperationInterval.interval(interval, unit, scheduler)); - } - - /** - * Drops items emitted by an Observable that are followed by newer items - * before a timeout value expires. The timer resets on each emission. - *

    - * Note: If events keep firing faster than the timeout then no data will be - * emitted. - *

    - * - *

    - * Information on debounce vs throttle: - *

    - *

    - * - * @param timeout the time each value has to be "the most recent" of the - * {@link Observable} to ensure that it's not dropped - * @param unit the {@link TimeUnit} for the timeout - * @return an {@link Observable} that filters out items that are too - * quickly followed by newer items - * @see debounce() - * @see #throttleWithTimeout(long, TimeUnit) - */ - public Observable debounce(long timeout, TimeUnit unit) { - return create(OperationDebounce.debounce(this, timeout, unit)); - } - - /** - * Drops items emitted by an Observable that are followed by newer items - * before a timeout value expires. The timer resets on each emission. - *

    - * Note: If events keep firing faster than the timeout then no data will be - * emitted. - *

    - * - *

    - * Information on debounce vs throttle: - *

    - *

    - * - * @param timeout the time each value has to be "the most recent" of the - * {@link Observable} to ensure that it's not dropped - * @param unit the unit of time for the specified timeout - * @param scheduler the {@link Scheduler} to use internally to manage the - * timers that handle the timeout for each event - * @return an {@link Observable} that filters out items that are too - * quickly followed by newer items - * @see debounce() - * @see #throttleWithTimeout(long, TimeUnit, Scheduler) - */ - public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) { - return create(OperationDebounce.debounce(this, timeout, unit, scheduler)); - } - - /** - * Drops items emitted by an Observable that are followed by newer items - * before a timeout value expires. The timer resets on each emission. - *

    - * Note: If events keep firing faster than the timeout then no data will be - * emitted. - *

    - * - *

    - * Information on debounce vs throttle: - *

    - *

    - * - * @param timeout the time each value has to be "the most recent" of the - * {@link Observable} to ensure that it's not dropped - * @param unit the {@link TimeUnit} for the timeout - * @return an {@link Observable} that filters out items that are too - * quickly followed by newer items - * @see throttleWithTimeout() - * @see #debounce(long, TimeUnit) - */ - public Observable throttleWithTimeout(long timeout, TimeUnit unit) { - return create(OperationDebounce.debounce(this, timeout, unit)); - } - - /** - * Drops items emitted by an Observable that are followed by newer items - * before a timeout value expires. The timer resets on each emission. - *

    - * Note: If events keep firing faster than the timeout then no data will be - * emitted. - *

    - * - *

    - * Information on debounce vs throttle: - *

    - *

    - * - * @param timeout the time each value has to be "the most recent" of the - * {@link Observable} to ensure that it's not dropped - * @param unit the {@link TimeUnit} for the timeout - * @param scheduler the {@link Scheduler} to use internally to manage the - * timers that handle the timeout for each event - * @return an {@link Observable} that filters out items that are too - * quickly followed by newer items - * @see throttleWithTimeout() - * @see #debounce(long, TimeUnit, Scheduler) - */ - public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler scheduler) { - return create(OperationDebounce.debounce(this, timeout, unit, scheduler)); - } - - /** - * Throttles by skipping items until "skipDuration" passes and then emits - * the next received item. - *

    - * This differs from {@link #throttleLast} in that this only tracks passage - * of time whereas {@link #throttleLast} ticks at scheduled intervals. - *

    - * - * - * @param windowDuration time to wait before sending another item after - * emitting the last item - * @param unit the unit of time for the specified timeout - * @return an Observable that performs the throttle operation - * @see throttleFirst() - */ - public Observable throttleFirst(long windowDuration, TimeUnit unit) { - return create(OperationThrottleFirst.throttleFirst(this, windowDuration, unit)); - } - - /** - * Throttles by skipping items until "skipDuration" passes and then emits - * the next received item. - *

    - * This differs from {@link #throttleLast} in that this only tracks passage - * of time whereas {@link #throttleLast} ticks at scheduled intervals. - *

    - * - * - * @param skipDuration time to wait before sending another item after - * emitting the last item - * @param unit the unit of time for the specified timeout - * @param scheduler the {@link Scheduler} to use internally to manage the - * timers that handle timeout for each event - * @return an Observable that performs the throttle operation - * @see throttleFirst() - */ - public Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler scheduler) { - return create(OperationThrottleFirst.throttleFirst(this, skipDuration, unit, scheduler)); - } - - /** - * Throttles by emitting the last item in each interval defined by - * intervalDuration. - *

    - * This differs from {@link #throttleFirst} in that this ticks along at a - * scheduled interval whereas {@link #throttleFirst} does not tick, it just - * tracks passage of time. - *

    - * - * - * @param intervalDuration duration of windows within which the last item - * will be emitted - * @param unit the unit of time for the specified interval - * @return an Observable that performs the throttle operation - * @see throttleLast() - * @see #sample(long, TimeUnit) - */ - public Observable throttleLast(long intervalDuration, TimeUnit unit) { - return sample(intervalDuration, unit); - } - - /** - * Throttles by emitting the last item in each interval defined by - * intervalDuration. - *

    - * This differs from {@link #throttleFirst} in that this ticks along at a - * scheduled interval whereas {@link #throttleFirst} does not tick, it just - * tracks passage of time. - *

    - * - * - * @param intervalDuration duration of windows within which the last item - * will be emitted - * @param unit the unit of time for the specified interval - * @return an Observable that performs the throttle operation - * @see throttleLast() - * @see #sample(long, TimeUnit, Scheduler) - */ - public Observable throttleLast(long intervalDuration, TimeUnit unit, Scheduler scheduler) { - return sample(intervalDuration, unit, scheduler); - } - - /** - * Wraps each item emitted by a source Observable in a {@link Timestamped} - * object. - *

    - * - * - * @return an Observable that emits timestamped items from the source - * Observable - * @see timestamp() - */ - public Observable> timestamp() { - return create(OperationTimestamp.timestamp(this)); - } - - /** - * Converts a {@link Future} into an Observable. - *

    - * - *

    - * You can convert any object that supports the {@link Future} interface - * into an Observable that emits the return value of the {@link Future#get} - * method of that object, by passing the object into the {@code from} - * method. - *

    - * Important note: This Observable is blocking; you cannot - * unsubscribe from it. - * - * @param future the source {@link Future} - * @param the type of object that the {@link Future} returns, and also - * the type of item to be emitted by the resulting Observable - * @return an Observable that emits the item from the source Future - * @see from() - */ - public static Observable from(Future future) { - return create(OperationToObservableFuture.toObservableFuture(future)); - } - - /** - * Converts a {@link Future} into an Observable. - *

    - * - *

    - * You can convert any object that supports the {@link Future} interface - * into an Observable that emits the return value of the {@link Future#get} - * method of that object, by passing the object into the {@code from} - * method. - *

    - * - * @param future the source {@link Future} - * @param scheduler the {@link Scheduler} to wait for the Future on. Use a - * Scheduler such as {@link Schedulers#threadPoolForIO()} - * that can block and wait on the future. - * @param the type of object that the {@link Future} returns, and also - * the type of item to be emitted by the resulting Observable - * @return an Observable that emits the item from the source Future - * @see from() - */ - public static Observable from(Future future, Scheduler scheduler) { - return create(OperationToObservableFuture.toObservableFuture(future)).subscribeOn(scheduler); - } - - /** - * Converts a {@link Future} into an Observable with timeout. - *

    - * - *

    - * You can convert any object that supports the {@link Future} interface - * into an Observable that emits the return value of the {link Future#get} - * method of that object, by passing the object into the {@code from} - * method. - *

    - * Important note: This Observable is blocking; you cannot - * unsubscribe from it. - * - * @param future the source {@link Future} - * @param timeout the maximum time to wait before calling get() - * @param unit the {@link TimeUnit} of the timeout argument - * @param the type of object that the {@link Future} returns, and also - * the type of item to be emitted by the resulting Observable - * @return an Observable that emits the item from the source {@link Future} - * @see from() - */ - public static Observable from(Future future, long timeout, TimeUnit unit) { - return create(OperationToObservableFuture.toObservableFuture(future, timeout, unit)); - } - - /** - * Returns an Observable that emits Boolean values that indicate whether the - * pairs of items emitted by two source Observables are equal. - *

    - * - * - * @param first the first Observable to compare - * @param second the second Observable to compare - * @param the type of items emitted by each Observable - * @return an Observable that emits Booleans that indicate whether the - * corresponding items emitted by the source Observables are equal - * @see sequenceEqual() - */ - public static Observable sequenceEqual(Observable first, Observable second) { - return sequenceEqual(first, second, new Func2() { - @Override - public Boolean call(T first, T second) { - return first.equals(second); - } - }); - } - - /** - * Returns an Observable that emits Boolean values that indicate whether the - * pairs of items emitted by two source Observables are equal based on the - * results of a specified equality function. - *

    - * - * - * @param first the first Observable to compare - * @param second the second Observable to compare - * @param equality a function used to compare items emitted by both - * Observables - * @param the type of items emitted by each Observable - * @return an Observable that emits Booleans that indicate whether the - * corresponding items emitted by the source Observables are equal - * @see sequenceEqual() - */ - public static Observable sequenceEqual(Observable first, Observable second, Func2 equality) { - return zip(first, second, equality); - } - - /** - * Returns an Observable that emits the results of a function of your - * choosing applied to combinations of two items emitted, in sequence, by - * two other Observables. - *

    - * - *

    - * {@code zip} applies this function in strict sequence, so the first item - * emitted by the new Observable will be the result of the function applied - * to the first item emitted by {@code o1} and the first item emitted by - * {@code o2}; the second item emitted by the new Observable will be the - * result of the function applied to the second item emitted by {@code o1} - * and the second item emitted by {@code o2}; and so forth. - *

    - * The resulting {@code Observable} returned from {@code zip} will - * invoke {@link Observer#onNext onNext} as many times as the number of - * {@code onNext} invocations of the source Observable that emits the fewest - * items. - * - * @param o1 the first source Observable - * @param o2 another source Observable - * @param zipFunction a function that, when applied to an item emitted by - * each of the source Observables, results in an item that will - * be emitted by the resulting Observable - * @return an Observable that emits the zipped results - * @see zip() - */ - public static Observable zip(Observable o1, Observable o2, Func2 zipFunction) { - return create(OperationZip.zip(o1, o2, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your - * choosing applied to combinations of three items emitted, in sequence, by - * three other Observables. - *

    - * - *

    - * {@code zip} applies this function in strict sequence, so the first item - * emitted by the new Observable will be the result of the function applied - * to the first item emitted by {@code o1}, the first item emitted by - * {@code o2}, and the first item emitted by {@code o3}; the second item - * emitted by the new Observable will be the result of the function applied - * to the second item emitted by {@code o1}, the second item emitted by - * {@code o2}, and the second item emitted by {@code o3}; and so forth. - *

    - * The resulting {@code Observable} returned from {@code zip} will - * invoke {@link Observer#onNext onNext} as many times as the number of - * {@code onNext} invocations of the source Observable that emits the fewest - * items. - * - * @param o1 the first source Observable - * @param o2 a second source Observable - * @param o3 a third source Observable - * @param zipFunction a function that, when applied to an item emitted by - * each of the source Observables, results in an item - * that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - * @see zip() - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Func3 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your - * choosing applied to combinations of four items emitted, in sequence, by - * four other Observables. - *

    - * - *

    - * {@code zip} applies this function in strict sequence, so the first item - * emitted by the new Observable will be the result of the function applied - * to the first item emitted by {@code o1}, the first item emitted by - * {@code o2}, the first item emitted by {@code o3}, and the first item - * emitted by {@code 04}; the second item emitted by the new Observable will - * be the result of the function applied to the second item emitted by each - * of those Observables; and so forth. - *

    - * The resulting {@code Observable} returned from {@code zip} will - * invoke {@link Observer#onNext onNext} as many times as the number of - * {@code onNext} invocations of the source Observable that emits the fewest - * items. - * - * @param o1 one source Observable - * @param o2 a second source Observable - * @param o3 a third source Observable - * @param o4 a fourth source Observable - * @param zipFunction a function that, when applied to an item emitted by - * each of the source Observables, results in an item that will - * be emitted by the resulting Observable - * @return an Observable that emits the zipped results - * @see zip() - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Func4 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your - * choosing applied to combinations of five items emitted, in sequence, by - * five other Observables. - *

    - * - *

    - * {@code zip} applies this function in strict sequence, so the first item - * emitted by the new Observable will be the result of the function applied - * to the first item emitted by {@code o1}, the first item emitted by - * {@code o2}, the first item emitted by {@code o3}, the first item emitted - * by {@code o4}, and the first item emitted by {@code o5}; the second item - * emitted by the new Observable will be the result of the function applied - * to the second item emitted by each of those Observables; and so forth. - *

    - * The resulting {@code Observable} returned from {@code zip} will - * invoke {@link Observer#onNext onNext} as many times as the number of - * {@code onNext} invocations of the source Observable that emits the fewest - * items. - * - * @param o1 the first source Observable - * @param o2 a second source Observable - * @param o3 a third source Observable - * @param o4 a fourth source Observable - * @param o5 a fifth source Observable - * @param zipFunction a function that, when applied to an item emitted by - * each of the source Observables, results in an item - * that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - * @see zip() - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, o5, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your - * choosing applied to combinations of six items emitted, in sequence, by - * six other Observables. - *

    - * - *

    - * {@code zip} applies this function in strict sequence, so the first item - * emitted by the new Observable will be the result of the function applied - * to the first item emitted each source Observable, the second item emitted - * by the new Observable will be the result of the function applied to the - * second item emitted by each of those Observables, and so forth. - *

    - * The resulting {@code Observable} returned from {@code zip} will - * invoke {@link Observer#onNext onNext} as many times as the number of - * {@code onNext} invocations of the source Observable that emits the fewest - * items. - * - * @param o1 the first source Observable - * @param o2 a second source Observable - * @param o3 a third source Observable - * @param o4 a fourth source Observable - * @param o5 a fifth source Observable - * @param o6 a sixth source Observable - * @param zipFunction a function that, when applied to an item emitted by - * each of the source Observables, results in an item - * that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - * @see zip() - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, - Func6 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your - * choosing applied to combinations of seven items emitted, in sequence, by - * seven other Observables. - *

    - * - *

    - * {@code zip} applies this function in strict sequence, so the first item - * emitted by the new Observable will be the result of the function applied - * to the first item emitted each source Observable, the second item emitted - * by the new Observable will be the result of the function applied to the - * second item emitted by each of those Observables, and so forth. - *

    - * The resulting {@code Observable} returned from {@code zip} will - * invoke {@link Observer#onNext onNext} as many times as the number of - * {@code onNext} invocations of the source Observable that emits the fewest - * items. - * - * @param o1 the first source Observable - * @param o2 a second source Observable - * @param o3 a third source Observable - * @param o4 a fourth source Observable - * @param o5 a fifth source Observable - * @param o6 a sixth source Observable - * @param o7 a seventh source Observable - * @param zipFunction a function that, when applied to an item emitted by - * each of the source Observables, results in an item - * that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - * @see zip() - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, - Func7 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, o7, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your - * choosing applied to combinations of eight items emitted, in sequence, by - * eight other Observables. - *

    - * - *

    - * {@code zip} applies this function in strict sequence, so the first item - * emitted by the new Observable will be the result of the function applied - * to the first item emitted each source Observable, the second item emitted - * by the new Observable will be the result of the function applied to the - * second item emitted by each of those Observables, and so forth. - *

    - * The resulting {@code Observable} returned from {@code zip} will - * invoke {@link Observer#onNext onNext} as many times as the number of - * {@code onNext} invocations of the source Observable that emits the fewest - * items. - * - * @param o1 the first source Observable - * @param o2 a second source Observable - * @param o3 a third source Observable - * @param o4 a fourth source Observable - * @param o5 a fifth source Observable - * @param o6 a sixth source Observable - * @param o7 a seventh source Observable - * @param o8 an eighth source Observable - * @param zipFunction a function that, when applied to an item emitted by - * each of the source Observables, results in an item - * that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - * @see zip() - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, - Func8 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, o7, o8, zipFunction)); - } - - /** - * Returns an Observable that emits the results of a function of your - * choosing applied to combinations of nine items emitted, in sequence, by - * nine other Observables. - *

    - * - *

    - * {@code zip} applies this function in strict sequence, so the first item - * emitted by the new Observable will be the result of the function applied - * to the first item emitted each source Observable, the second item emitted - * by the new Observable will be the result of the function applied to the - * second item emitted by each of those Observables, and so forth. - *

    - * The resulting {@code Observable} returned from {@code zip} will - * invoke {@link Observer#onNext onNext} as many times as the number of - * {@code onNext} invocations of the source Observable that emits the fewest - * items. - * - * @param o1 the first source Observable - * @param o2 a second source Observable - * @param o3 a third source Observable - * @param o4 a fourth source Observable - * @param o5 a fifth source Observable - * @param o6 a sixth source Observable - * @param o7 a seventh source Observable - * @param o8 an eighth source Observable - * @param o9 a ninth source Observable - * @param zipFunction a function that, when applied to an item emitted by - * each of the source Observables, results in an item - * that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - * @see zip() - */ - public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, - Observable o9, Func9 zipFunction) { - return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, o7, o8, o9, zipFunction)); - } - - /** - * Combines the given Observables, emitting an event containing an - * aggregation of the latest values of each of the source observables each - * time an event is received from one of the source observables, where the - * aggregation is defined by the given function. - *

    - * - * - * @param o1 the first source Observable - * @param o2 the second source Observable - * @param combineFunction the aggregation function used to combine the - * source observable values - * @return an Observable that combines the source Observables with the - * given combine function - * @see combineLatest() - */ - public static Observable combineLatest(Observable o1, Observable o2, Func2 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, combineFunction)); - } - - /** - * Combines the given Observables, emitting an event containing an - * aggregation of the latest values of each of the source observables each - * time an event is received from one of the source observables, where the - * aggregation is defined by the given function. - *

    - * - * - * @param o1 the first source Observable - * @param o2 the second source Observable - * @param o3 the third source Observable - * @param combineFunction the aggregation function used to combine the - * source observable values - * @return an Observable that combines the source Observables with the - * given combine function - * @see combineLatest() - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Func3 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, combineFunction)); - } - - /** - * Combines the given Observables, emitting an event containing an - * aggregation of the latest values of each of the source observables each - * time an event is received from one of the source observables, where the - * aggregation is defined by the given function. - *

    - * - * - * @param o1 the first source Observable - * @param o2 the second source Observable - * @param o3 the third source Observable - * @param o4 the fourth source Observable - * @param combineFunction the aggregation function used to combine the - * source observable values - * @return an Observable that combines the source Observables with the - * given combine function - * @see combineLatest() - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, - Func4 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, combineFunction)); - } - - /** - * Combines the given Observables, emitting an event containing an - * aggregation of the latest values of each of the source observables each - * time an event is received from one of the source observables, where the - * aggregation is defined by the given function. - *

    - * - * - * @param o1 the first source Observable - * @param o2 the second source Observable - * @param o3 the third source Observable - * @param o4 the fourth source Observable - * @param o5 the fifth source Observable - * @param combineFunction the aggregation function used to combine the - * source observable values - * @return an Observable that combines the source Observables with the - * given combine function - * @see combineLatest() - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, - Func5 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, combineFunction)); - } - - /** - * Combines the given Observables, emitting an event containing an - * aggregation of the latest values of each of the source observables each - * time an event is received from one of the source observables, where the - * aggregation is defined by the given function. - *

    - * - * - * @param o1 the first source Observable - * @param o2 the second source Observable - * @param o3 the third source Observable - * @param o4 the fourth source Observable - * @param o5 the fifth source Observable - * @param o6 the sixth source Observable - * @param combineFunction the aggregation function used to combine the - * source observable values - * @return an Observable that combines the source Observables with the - * given combine function - * @see combineLatest() - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, - Func6 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, combineFunction)); - } - - /** - * Combines the given Observables, emitting an event containing an - * aggregation of the latest values of each of the source observables each - * time an event is received from one of the source observables, where the - * aggregation is defined by the given function. - *

    - * - * - * @param o1 the first source Observable - * @param o2 the second source Observable - * @param o3 the third source Observable - * @param o4 the fourth source Observable - * @param o5 the fifth source Observable - * @param o6 the sixth source Observable - * @param o7 the seventh source Observable - * @param combineFunction the aggregation function used to combine the - * source observable values - * @return an Observable that combines the source Observables with the - * given combine function - * @see combineLatest() - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, - Func7 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, o7, combineFunction)); - } - - /** - * Combines the given Observables, emitting an event containing an - * aggregation of the latest values of each of the source observables each - * time an event is received from one of the source observables, where the - * aggregation is defined by the given function. - *

    - * - * - * @param o1 the first source Observable - * @param o2 the second source Observable - * @param o3 the third source Observable - * @param o4 the fourth source Observable - * @param o5 the fifth source Observable - * @param o6 the sixth source Observable - * @param o7 the seventh source Observable - * @param o8 the eighth source Observable - * @param combineFunction the aggregation function used to combine the - * source observable values - * @return an Observable that combines the source Observables with the - * given combine function - * @see combineLatest() - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, - Func8 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, o7, o8, combineFunction)); - } - - /** - * Combines the given Observables, emitting an event containing an - * aggregation of the latest values of each of the source observables each - * time an event is received from one of the source observables, where the - * aggregation is defined by the given function. - *

    - * - * - * @param o1 the first source Observable - * @param o2 the second source Observable - * @param o3 the third source Observable - * @param o4 the fourth source Observable - * @param o5 the fifth source Observable - * @param o6 the sixth source Observable - * @param o7 the seventh source Observable - * @param o8 the eighth source Observable - * @param o9 the ninth source Observable - * @param combineFunction the aggregation function used to combine the - * source observable values - * @return an Observable that combines the source Observables with the - * given combine function - * @see combineLatest() - */ - public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9, - Func9 combineFunction) { - return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, o7, o8, o9, combineFunction)); - } - - /** - * Creates an Observable that produces buffers of collected items. - *

    - * - *

    - * This Observable produces connected, non-overlapping buffers. The current - * buffer is emitted and replaced with a new buffer when the Observable - * produced by the specified bufferClosingSelector produces a - * {@link rx.util.Closing} object. The bufferClosingSelector - * will then be used to create a new Observable to listen for the end of - * the next buffer. - * - * @param bufferClosingSelector the {@link Func0} which is used to produce - * an {@link Observable} for every buffer - * created. When this {@link Observable} - * produces a {@link rx.util.Closing} object, - * the associated buffer is emitted and - * replaced with a new one. - * @return an {@link Observable} which produces connected, non-overlapping - * buffers, which are emitted when the current {@link Observable} - * created with the {@link Func0} argument produces a - * {@link rx.util.Closing} object - * @see buffer() - */ - public Observable> buffer(Func0> bufferClosingSelector) { - return create(OperationBuffer.buffer(this, bufferClosingSelector)); - } - - /** - * Creates an Observable which produces buffers of collected values. - *

    - * - *

    - * This Observable produces buffers. Buffers are created when the specified - * bufferOpenings Observable produces a {@link rx.util.Opening} - * object. Additionally the bufferClosingSelector argument is - * used to create an Observable which produces {@link rx.util.Closing} - * objects. When this Observable produces such an object, the associated - * buffer is emitted. - * - * @param bufferOpenings the {@link Observable} that, when it produces a - * {@link rx.util.Opening} object, will cause another - * buffer to be created - * @param bufferClosingSelector the {@link Func1} that is used to produce - * an {@link Observable} for every buffer - * created. When this {@link Observable} - * produces a {@link rx.util.Closing} object, - * the associated buffer is emitted. - * @return an {@link Observable} that produces buffers that are created and - * emitted when the specified {@link Observable}s publish certain - * objects - * @see buffer() - */ - public Observable> buffer(Observable bufferOpenings, Func1> bufferClosingSelector) { - return create(OperationBuffer.buffer(this, bufferOpenings, bufferClosingSelector)); - } - - /** - * Creates an Observable that produces buffers of collected items. - *

    - * - *

    - * This Observable produces connected, non-overlapping buffers, each - * containing count items. When the source Observable completes - * or encounters an error, the current buffer is emitted, and the event is - * propagated. - * - * @param count the maximum size of each buffer before it should be emitted - * @return an {@link Observable} that produces connected, non-overlapping - * buffers containing at most "count" items - * @see buffer() - */ - public Observable> buffer(int count) { - return create(OperationBuffer.buffer(this, count)); - } - - /** - * Creates an Observable which produces buffers of collected items. - *

    - * - *

    - * This Observable produces buffers every skip items, each - * containing count items. When the source Observable - * completes or encounters an error, the current buffer is emitted, and the - * event is propagated. - * - * @param count the maximum size of each buffer before it should be emitted - * @param skip how many produced items need to be skipped before starting a - * new buffer. Note that when skip and - * count are equal, this is the same operation as - * {@link Observable#buffer(int)}. - * @return an {@link Observable} that produces buffers every - * skip item containing at most count - * items - * @see buffer() - */ - public Observable> buffer(int count, int skip) { - return create(OperationBuffer.buffer(this, count, skip)); - } - - /** - * Creates an Observable that produces buffers of collected values. - *

    - * - *

    - * This Observable produces connected, non-overlapping buffers, each of a - * fixed duration specified by the timespan argument. When the - * source Observable completes or encounters an error, the current buffer is - * emitted and the event is propagated. - * - * @param timespan the period of time each buffer collects values before it - * should be emitted and replaced with a new buffer - * @param unit the unit of time which applies to the timespan - * argument - * @return an {@link Observable} that produces connected, non-overlapping - * buffers with a fixed duration - * @see buffer() - */ - public Observable> buffer(long timespan, TimeUnit unit) { - return create(OperationBuffer.buffer(this, timespan, unit)); - } - - /** - * Creates an Observable that produces buffers of collected values. - *

    - * - *

    - * This Observable produces connected, non-overlapping buffers, each of a - * fixed duration specified by the timespan argument. When the - * source Observable completes or encounters an error, the current buffer is - * emitted and the event is propagated. - * - * @param timespan the period of time each buffer collects values before it - * should be emitted and replaced with a new buffer - * @param unit the unit of time which applies to the timespan - * argument - * @param scheduler the {@link Scheduler} to use when determining the end - * and start of a buffer - * @return an {@link Observable} that produces connected, non-overlapping - * buffers with a fixed duration - * @see buffer() - */ - public Observable> buffer(long timespan, TimeUnit unit, Scheduler scheduler) { - return create(OperationBuffer.buffer(this, timespan, unit, scheduler)); - } - - /** - * Creates an Observable that produces buffers of collected items. This - * Observable produces connected, non-overlapping buffers, each of a fixed - * duration specified by the timespan argument or a maximum - * size specified by the count argument (whichever is reached - * first). When the source Observable completes or encounters an error, the - * current buffer is emitted and the event is propagated. - *

    - * - * - * @param timespan the period of time each buffer collects values before it - * should be emitted and replaced with a new buffer - * @param unit the unit of time which applies to the timespan - * argument - * @param count the maximum size of each buffer before it should be emitted - * @return an {@link Observable} that produces connected, non-overlapping - * buffers that are emitted after a fixed duration or when the - * buffer reaches maximum capacity (whichever occurs first) - * @see buffer() - */ - public Observable> buffer(long timespan, TimeUnit unit, int count) { - return create(OperationBuffer.buffer(this, timespan, unit, count)); - } - - /** - * Creates an Observable that produces buffers of collected items. This - * Observable produces connected, non-overlapping buffers, each of a fixed - * duration specified by the timespan argument or a maximum - * size specified by the count argument (whichever is reached - * first). When the source Observable completes or encounters an error, the - * current buffer is emitted and the event is propagated. - *

    - * - * - * @param timespan the period of time each buffer collects values before it - * should be emitted and replaced with a new buffer - * @param unit the unit of time which applies to the timespan - * argument - * @param count the maximum size of each buffer before it should be emitted - * @param scheduler the {@link Scheduler} to use when determining the end - and start of a buffer - * @return an {@link Observable} that produces connected, non-overlapping - * buffers that are emitted after a fixed duration or when the - * buffer has reached maximum capacity (whichever occurs first) - * @see buffer() - */ - public Observable> buffer(long timespan, TimeUnit unit, int count, Scheduler scheduler) { - return create(OperationBuffer.buffer(this, timespan, unit, count, scheduler)); - } - - /** - * Creates an Observable that produces buffers of collected items. This - * Observable starts a new buffer periodically, as determined by the - * timeshift argument. Each buffer is emitted after a fixed - * timespan, specified by the timespan argument. When the - * source Observable completes or encounters an error, the current buffer is - * emitted and the event is propagated. - *

    - * - * - * @param timespan the period of time each buffer collects values before it - * should be emitted - * @param timeshift the period of time after which a new buffer will be - * created - * @param unit the unit of time that applies to the timespan - * and timeshift arguments - * @return an {@link Observable} that produces new buffers periodically and - * emits these after a fixed timespan has elapsed. - * @see buffer() - */ - public Observable> buffer(long timespan, long timeshift, TimeUnit unit) { - return create(OperationBuffer.buffer(this, timespan, timeshift, unit)); - } - - /** - * Creates an Observable that produces buffers of collected items. This - * Observable starts a new buffer periodically, as determined by the - * timeshift argument. Each buffer is emitted after a fixed - * timespan, specified by the timespan argument. When the - * source Observable completes or encounters an error, the current buffer is - * emitted and the event is propagated. - *

    - * - * - * @param timespan the period of time each buffer collects values before it - * should be emitted - * @param timeshift the period of time after which a new buffer will be - * created - * @param unit the unit of time that applies to the timespan - * and timeshift arguments - * @param scheduler the {@link Scheduler} to use when determining the end - * and start of a buffer - * @return an {@link Observable} that produces new buffers periodically and - * emits these after a fixed timespan has elapsed - * @see buffer() - */ - public Observable> buffer(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { - return create(OperationBuffer.buffer(this, timespan, timeshift, unit, scheduler)); - } - - /** - * Creates an Observable that produces windows of collected items. This - * Observable produces connected, non-overlapping windows. The current - * window is emitted and replaced with a new window when the Observable - * produced by the specified closingSelector produces a - * {@link rx.util.Closing} object. The closingSelector will - * then be used to create a new Observable to listen for the end of the next - * window. - *

    - * - * - * @param closingSelector the {@link Func0} used to produce an - * {@link Observable} for every window created. When this - * {@link Observable} emits a {@link rx.util.Closing} object, the - * associated window is emitted and replaced with a new one. - * @return an {@link Observable} that produces connected, non-overlapping - * windows, which are emitted when the current {@link Observable} - * created with the closingSelector argument emits a - * {@link rx.util.Closing} object. - * @see window() - */ - public Observable> window(Func0> closingSelector) { - return create(OperationWindow.window(this, closingSelector)); - } - - /** - * Creates an Observable that produces windows of collected items. This - * Observable produces windows. Chunks are created when the - * windowOpenings Observable produces a {@link rx.util.Opening} - * object. Additionally the closingSelector argument creates an - * Observable that produces {@link rx.util.Closing} objects. When this - * Observable produces such an object, the associated window is emitted. - *

    - * - * - * @param windowOpenings the {@link Observable} that, when it produces a - * {@link rx.util.Opening} object, causes another - * window to be created - * @param closingSelector the {@link Func1} that produces an - * {@link Observable} for every window created. When - * this {@link Observable} produces a - * {@link rx.util.Closing} object, the associated - * window is emitted. - * @return an {@link Observable} that produces windows that are created and - * emitted when the specified {@link Observable}s publish certain - * objects - * @see window() - */ - public Observable> window(Observable windowOpenings, Func1> closingSelector) { - return create(OperationWindow.window(this, windowOpenings, closingSelector)); - } - - /** - * Creates an Observable that produces windows of collected items. This - * Observable produces connected, non-overlapping windows, each containing - * count elements. When the source Observable completes or - * encounters an error, the current window is emitted, and the event is - * propagated. - *

    - * - * - * @param count the maximum size of each window before it should be emitted - * @return an {@link Observable} that produces connected, non-overlapping - * windows containing at most count items - * @see window() - */ - public Observable> window(int count) { - return create(OperationWindow.window(this, count)); - } - - /** - * Creates an Observable that produces windows of collected items. This - * Observable produces windows every skip items, each - * containing count elements. When the source Observable - * completes or encounters an error, the current window is emitted and the - * event is propagated. - *

    - * - * - * @param count the maximum size of each window before it should be emitted - * @param skip how many items need to be skipped before starting a new - * window. Note that if skip and count - * are equal this is the same operation as {@link #window(int)}. - * @return an {@link Observable} that produces windows every "skipped" - * items containing at most count items - * @see window() - */ - public Observable> window(int count, int skip) { - return create(OperationWindow.window(this, count, skip)); - } - - /** - * Creates an Observable that produces windows of collected items. This - * Observable produces connected, non-overlapping windows, each of a fixed - * duration specified by the timespan argument. When the source - * Observable completes or encounters an error, the current window is - * emitted and the event is propagated. - *

    - * - * - * @param timespan the period of time each window collects items before it - * should be emitted and replaced with a new window - * @param unit the unit of time that applies to the timespan - * argument - * @return an {@link Observable} that produces connected, non-overlapping - * windows with a fixed duration - * @see window() - */ - public Observable> window(long timespan, TimeUnit unit) { - return create(OperationWindow.window(this, timespan, unit)); - } - - /** - * Creates an Observable that produces windows of collected items. This - * Observable produces connected, non-overlapping windows, each of a fixed - * duration as specified by the timespan argument. When the - * source Observable completes or encounters an error, the current window is - * emitted and the event is propagated. - *

    - * - * - * @param timespan the period of time each window collects items before it - * should be emitted and replaced with a new window - * @param unit the unit of time which applies to the timespan - * argument - * @param scheduler the {@link Scheduler} to use when determining the end - * and start of a window - * @return an {@link Observable} that produces connected, non-overlapping - * windows with a fixed duration - * @see window() - */ - public Observable> window(long timespan, TimeUnit unit, Scheduler scheduler) { - return create(OperationWindow.window(this, timespan, unit, scheduler)); - } - - /** - * Creates an Observable that produces windows of collected items. This - * Observable produces connected non-overlapping windows, each of a fixed - * duration as specified by the timespan argument or a maximum - * size as specified by the count argument (whichever is - * reached first). When the source Observable completes or encounters an - * error, the current window is emitted and the event is propagated. - *

    - * - * - * @param timespan the period of time each window collects values before it - * should be emitted and replaced with a new window - * @param unit the unit of time that applies to the timespan - * argument - * @param count the maximum size of each window before it should be emitted - * @return an {@link Observable} that produces connected, non-overlapping - * windows that are emitted after a fixed duration or when the - * window has reached maximum capacity (whichever occurs first) - * @see window() - */ - public Observable> window(long timespan, TimeUnit unit, int count) { - return create(OperationWindow.window(this, timespan, unit, count)); - } - - /** - * Creates an Observable that produces windows of collected items. This - * Observable produces connected, non-overlapping windows, each of a fixed - * duration specified by the timespan argument or a maximum - * size specified by the count argument (whichever is reached - * first). When the source Observable completes or encounters an error, the - * current window is emitted and the event is propagated. - *

    - * - * - * @param timespan the period of time each window collects values before it - * should be emitted and replaced with a new window - * @param unit the unit of time which applies to the timespan - * argument - * @param count the maximum size of each window before it should be emitted - * @param scheduler the {@link Scheduler} to use when determining the end - * and start of a window. - * @return an {@link Observable} that produces connected non-overlapping - * windows that are emitted after a fixed duration or when the - * window has reached maximum capacity (whichever occurs first). - * @see window() - */ - public Observable> window(long timespan, TimeUnit unit, int count, Scheduler scheduler) { - return create(OperationWindow.window(this, timespan, unit, count, scheduler)); - } - - /** - * Creates an Observable that produces windows of collected items. This - * Observable starts a new window periodically, as determined by the - * timeshift argument. Each window is emitted after a fixed - * timespan, specified by the timespan argument. When the - * source Observable completes or encounters an error, the current window is - * emitted and the event is propagated. - *

    - * - * - * @param timespan the period of time each window collects values before it - * should be emitted - * @param timeshift the period of time after which a new window will be - * created - * @param unit the unit of time that applies to the timespan - * and timeshift arguments - * @return an {@link Observable} that produces new windows periodically and - * emits these after a fixed timespan has elapsed - * @see window() - */ - public Observable> window(long timespan, long timeshift, TimeUnit unit) { - return create(OperationWindow.window(this, timespan, timeshift, unit)); - } - - /** - * Creates an Observable that produces windows of collected items. This - * Observable starts a new window periodically, as determined by the - * timeshift argument. Each window is emitted after a fixed - * timespan, specified by the timespan argument. When the - * source Observable completes or encounters an error, the current window is - * emitted and the event is propagated. - *

    - * - * - * @param timespan the period of time each window collects values before it - * should be emitted - * @param timeshift the period of time after which a new window will be - * created - * @param unit the unit of time that applies to the timespan - * and timeshift arguments - * @param scheduler the {@link Scheduler} to use when determining the end - * and start of a window - * @return an {@link Observable} that produces new windows periodically and - * emits these after a fixed timespan has elapsed - * @see window() - */ - public Observable> window(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { - return create(OperationWindow.window(this, timespan, timeshift, unit, scheduler)); - } - - /** - * Returns an Observable that emits the results of a function of your - * choosing applied to combinations of n items emitted, in sequence, - * by n other Observables as provided by an Iterable. - *

    - * {@code zip} applies this function in strict sequence, so the first item - * emitted by the new Observable will be the result of the function applied - * to the first item emitted by all of the source Observables; the second - * item emitted by the new Observable will be the result of the function - * applied to the second item emitted by each of those Observables; and so - * forth. - *

    - * The resulting {@code Observable} returned from {@code zip} will - * invoke {@code onNext} as many times as the number of {@code onNext} - * invokations of the source Observable that emits the fewest items. - *

    - * - * - * @param ws an Observable of source Observables - * @param zipFunction a function that, when applied to an item emitted by - * each of the source Observables, results in an item - * that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - * @see zip() - */ - public static Observable zip(Observable> ws, final FuncN zipFunction) { - return ws.toList().mapMany(new Func1>, Observable>() { - @Override - public Observable call(List> wsList) { - return create(OperationZip.zip(wsList, zipFunction)); - } - }); - } - - /** - * Returns an Observable that emits the results of a function of your - * choosing applied to combinations items emitted, in sequence, by a - * collection of other Observables. - *

    - * {@code zip} applies this function in strict sequence, so the first item - * emitted by the new Observable will be the result of the function applied - * to the first item emitted by all of the source Observables; the second - * item emitted by the new Observable will be the result of the function - * applied to the second item emitted by each of those Observables; and so - * forth. - *

    - * The resulting {@code Observable} returned from {@code zip} will invoke - * {@code onNext} as many times as the number of {@code onNext} invokations - * of the source Observable that emits the fewest items. - *

    - * - * - * @param ws a collection of source Observables - * @param zipFunction a function that, when applied to an item emitted by - * each of the source Observables, results in an item - * that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - * @see zip() - */ - public static Observable zip(Iterable> ws, FuncN zipFunction) { - return create(OperationZip.zip(ws, zipFunction)); - } - - /** - * Filter items emitted by an Observable. - *

    - * - * - * @param predicate a function that evaluates the items emitted by the - * source Observable, returning {@code true} if they pass - * the filter - * @return an Observable that emits only those items in the original - * Observable that the filter evaluates as {@code true} - * @see filter() - */ - public Observable filter(Func1 predicate) { - return create(OperationFilter.filter(this, predicate)); - } - - /** - * Returns an Observable that forwards all sequentially distinct items - * emitted from the source Observable. - *

    - * - * - * @return an Observable of sequentially distinct items - * @see distinctUntilChanged() - * @see MSDN: Observable.distinctUntilChanged - */ - public Observable distinctUntilChanged() { - return create(OperationDistinctUntilChanged.distinctUntilChanged(this)); - } - - /** - * Returns an Observable that forwards all items emitted from the source - * Observable that are sequentially distinct according to a key selector - * function. - *

    - * - * - * @param keySelector a function that projects an emitted item to a key - * value that is used for deciding whether an item is - * sequentially distinct from another one or not - * @return an Observable of sequentially distinct items - * @see distinctUntilChanged() - * @see MSDN: Observable.distinctUntilChanged - */ - public Observable distinctUntilChanged(Func1 keySelector) { - return create(OperationDistinctUntilChanged.distinctUntilChanged(this, keySelector)); - } - - /** - * Returns an Observable that emits all distinct items emitted from the - * source Observable. - *

    - * - * - * @return an Observable of distinct items - * @see distinct() - * @see MSDN: Observable.distinct - */ - public Observable distinct() { - return create(OperationDistinct.distinct(this)); - } - - /** - * Returns an Observable that emits all items emitted from the source - * Observable that are distinct according to a key selector function. - *

    - * - * - * @param keySelector a function that projects an emitted item to a key - * value that is used to decide whether an item is - * distinct from another one or not - * @return an Observable that emits distinct items - * @see distinct() - * @see MSDN: Observable.distinct - */ - public Observable distinct(Func1 keySelector) { - return create(OperationDistinct.distinct(this, keySelector)); - } - - /** - * Returns the item at a specified index in a sequence. - *

    - * - * - * @param index the zero-based index of the item to retrieve - * @return an Observable that emits the item at the specified position in - * the source sequence - * @throws IndexOutOfBoundsException if index is greater than - * or equal to the number of elements in - * the source sequence - * @throws IndexOutOfBoundsException if index is less than 0 - * @see elementAt() - */ - public Observable elementAt(int index) { - return create(OperationElementAt.elementAt(this, index)); - } - - /** - * Returns the item at a specified index in a sequence or the default item - * if the index is out of range. - *

    - * - * - * @param index the zero-based index of the item to retrieve - * @param defaultValue the default item - * @return an Observable that emits the item at the specified position in - * the source sequence, or the default item if the index is outside - * the bounds of the source sequence - * @throws IndexOutOfBoundsException if index is less than 0 - * @see elementAtOrDefault() - */ - public Observable elementAtOrDefault(int index, T defaultValue) { - return create(OperationElementAt.elementAtOrDefault(this, index, defaultValue)); - } - - /** - * Returns an {@link Observable} that emits true if any element - * of the source {@link Observable} satisfies the given condition, otherwise - * false. Note: always emits false if the source - * {@link Observable} is empty. - *

    - * In Rx.Net this is the any operator but renamed in RxJava to - * better match Java naming idioms. - *

    - * - * - * @param predicate the condition to test every element - * @return a subscription function for creating the target Observable - * @see exists() - * @see MSDN: Observable.Any Note: the description in this page is wrong. - */ - public Observable exists(Func1 predicate) { - return create(OperationAny.exists(this, predicate)); - } - - /** - * Determines whether an Observable sequence contains a specified item. - *

    - * - * - * @param element the item to search in the sequence - * @return an Observable that emits true if the item is in the - * source sequence - * @see contains() - * @see MSDN: Observable.Contains - */ - public Observable contains(final T element) { - return exists(new Func1() { - public Boolean call(T t1) { - return element == null ? t1 == null : element.equals(t1); - } - }); - } - - /** - * Registers an {@link Action0} to be called when this Observable invokes - * {@link Observer#onCompleted onCompleted} or - * {@link Observer#onError onError}. - *

    - * - * - * @param action an {@link Action0} to be invoked when the source - * Observable finishes - * @return an Observable that emits the same items as the source Observable, - * then invokes the {@link Action0} - * @see finallyDo() - * @see MSDN: Observable.Finally Method - */ - public Observable finallyDo(Action0 action) { - return create(OperationFinally.finallyDo(this, action)); - } - - /** - * Creates a new Observable by applying a function that you supply to each - * item emitted by the source Observable, where that function returns an - * Observable, and then merging those resulting Observables and emitting the - * results of this merger. - *

    - * - *

    - * Note: {@code mapMany} and {@code flatMap} are equivalent. - * - * @param func a function that, when applied to an item emitted by the - * source Observable, returns an Observable - * @return an Observable that emits the result of applying the - * transformation function to each item emitted by the source - * Observable and merging the results of the Observables obtained - * from this transformation. - * @see flatMap() - * @see #mapMany(Func1) - */ - public Observable flatMap(Func1> func) { - return mapMany(func); - } - - /** - * Filter items emitted by an Observable. - *

    - * - * - * @param predicate a function that evaluates an item emitted by the source - * Observable, returning {@code true} if it passes the - * filter - * @return an Observable that emits only those items emitted by the original - * Observable that the filter evaluates as {@code true} - * @see where() - * @see #filter(Func1) - */ - public Observable where(Func1 predicate) { - return filter(predicate); - } - - /** - * Returns an Observable that applies the given function to each item - * emitted by an Observable and emits the result. - *

    - * - * - * @param func a function to apply to each item emitted by the Observable - * @return an Observable that emits the items from the source Observable, - * transformed by the given function - * @see map() - * @see MSDN: Observable.Select - */ - public Observable map(Func1 func) { - return create(OperationMap.map(this, func)); - } - - /** - * Returns an Observable that applies the given function to each item - * emitted by an Observable and emits the result. - *

    - * - * - * @param func a function to apply to each item emitted by the Observable. - * The function takes the index of the emitted item as - * additional parameter. - * @return an Observable that emits the items from the source Observable, - * transformed by the given function - * @see mapWithIndex() - * @see MSDN: Observable.Select - */ - public Observable mapWithIndex(Func2 func) { - return create(OperationMap.mapWithIndex(this, func)); - } - - /** - * Creates a new Observable by applying a function that you supply to each - * item emitted by the source Observable, where that function returns an - * Observable, and then merging those resulting Observables and emitting - * the results of this merger. - *

    - * - *

    - * Note: mapMany and flatMap are equivalent. - * - * @param func a function that, when applied to an item emitted by the - * source Observable, returns an Observable - * @return an Observable that emits the result of applying the - * transformation function to each item emitted by the source - * Observable and merging the results of the Observables obtained - * from this transformation. - * @see mapMany() - * @see #flatMap(Func1) - */ - public Observable mapMany(Func1> func) { - return create(OperationMap.mapMany(this, func)); - } - - /** - * Turns all of the notifications from a source Observable into - * {@link Observer#onNext onNext} emissions, and marks them with their - * original notification types within {@link Notification} objects. - *

    - * - * - * @return an Observable whose items are the result of materializing the - * items and notifications of the source Observable - * @see materialize() - * @see MSDN: Observable.materialize - */ - public Observable> materialize() { - return create(OperationMaterialize.materialize(this)); - } - - /** - * Asynchronously subscribes and unsubscribes Observers on the specified - * {@link Scheduler}. - *

    - * - * - * @param scheduler the {@link Scheduler} to perform subscription and - * unsubscription actions on - * @return the source Observable modified so that its subscriptions and - * unsubscriptions happen on the specified {@link Scheduler} - * @see subscribeOn() - */ - public Observable subscribeOn(Scheduler scheduler) { - return create(OperationSubscribeOn.subscribeOn(this, scheduler)); - } - - /** - * Asynchronously notify {@link Observer}s on the specified - * {@link Scheduler}. - *

    - * - * - * @param scheduler the {@link Scheduler} to notify {@link Observer}s on - * @return the source Observable modified so that its {@link Observer}s are - * notified on the specified {@link Scheduler} - * @see observeOn() - */ - public Observable observeOn(Scheduler scheduler) { - return create(OperationObserveOn.observeOn(this, scheduler)); - } - - /** - * Returns an Observable that reverses the effect of - * {@link #materialize materialize} by transforming the {@link Notification} - * objects emitted by the source Observable into the items or notifications - * they represent. - *

    - * - * - * @return an Observable that emits the items and notifications embedded in - * the {@link Notification} objects emitted by the source Observable - * @throws Throwable if the source Observable is not of type - * {@code Observable>} - * @see dematerialize() - * @see MSDN: Observable.dematerialize - */ - @SuppressWarnings("unchecked") - public Observable dematerialize() { - return create(OperationDematerialize.dematerialize((Observable>) this)); - } - - /** - * Instruct an Observable to pass control to another Observable rather than - * invoking {@link Observer#onError onError} if it encounters an error. - *

    - * - *

    - * By default, when an Observable encounters an error that prevents it from - * emitting the expected item to its {@link Observer}, the Observable - * invokes its Observer's onError method, and then quits - * without invoking any more of its Observer's methods. The - * onErrorResumeNext method changes this behavior. If you pass - * a function that returns an Observable (resumeFunction) to - * onErrorResumeNext, if the original Observable encounters an - * error, instead of invoking its Observer's onError method, it - * will instead relinquish control to the Observable returned from - * resumeFunction, which will invoke the Observer's - * {@link Observer#onNext onNext} method if it is able to do so. In such a - * case, because no Observable necessarily invokes onError, the - * Observer may never know that an error happened. - *

    - * You can use this to prevent errors from propagating or to supply fallback - * data should errors be encountered. - * - * @param resumeFunction a function that returns an Observable that will - * take over if the source Observable encounters an - * error - * @return the original Observable, with appropriately modified behavior - * @see onErrorResumeNext() - */ - public Observable onErrorResumeNext(final Func1> resumeFunction) { - return create(OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction(this, resumeFunction)); - } - - /** - * Instruct an Observable to pass control to another Observable rather than - * invoking {@link Observer#onError onError} if it encounters an error. - *

    - * - *

    - * By default, when an Observable encounters an error that prevents it from - * emitting the expected item to its {@link Observer}, the Observable - * invokes its Observer's onError method, and then quits - * without invoking any more of its Observer's methods. The - * onErrorResumeNext method changes this behavior. If you pass - * another Observable (resumeSequence) to an Observable's - * onErrorResumeNext method, if the original Observable - * encounters an error, instead of invoking its Observer's - * onError method, it will instead relinquish control to - * resumeSequence which will invoke the Observer's - * {@link Observer#onNext onNext} method if it is able to do so. In such a - * case, because no Observable necessarily invokes onError, the - * Observer may never know that an error happened. - *

    - * You can use this to prevent errors from propagating or to supply fallback - * data should errors be encountered. - * - * @param resumeSequence a function that returns an Observable that will - * take over if the source Observable encounters an - * error - * @return the original Observable, with appropriately modified behavior - * @see onErrorResumeNext() - */ - public Observable onErrorResumeNext(final Observable resumeSequence) { - return create(OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable(this, resumeSequence)); - } - - /** - * Instruct an Observable to pass control to another Observable rather than - * invoking {@link Observer#onError onError} if it encounters an error of - * type {@link java.lang.Exception}. - *

    - * This differs from {@link #onErrorResumeNext} in that this one does not - * handle {@link java.lang.Throwable} or {@link java.lang.Error} but lets - * those continue through. - *

    - * - *

    - * By default, when an Observable encounters an error that prevents it from - * emitting the expected item to its {@link Observer}, the Observable - * invokes its Observer's onError method, and then quits - * without invoking any more of its Observer's methods. The - * onErrorResumeNext method changes this behavior. If you pass - * another Observable (resumeSequence) to an Observable's - * onErrorResumeNext method, if the original Observable - * encounters an error, instead of invoking its Observer's - * onError method, it will instead relinquish control to - * resumeSequence which will invoke the Observer's - * {@link Observer#onNext onNext} method if it is able to do so. In such a - * case, because no Observable necessarily invokes onError, - * the Observer may never know that an error happened. - *

    - * You can use this to prevent errors from propagating or to supply fallback - * data should errors be encountered. - * - * @param resumeSequence a function that returns an Observable that will - * take over if the source Observable encounters an - * error - * @return the original Observable, with appropriately modified behavior - * @see onExceptionResumeNextViaObservable() - */ - public Observable onExceptionResumeNext(final Observable resumeSequence) { - return create(OperationOnExceptionResumeNextViaObservable.onExceptionResumeNextViaObservable(this, resumeSequence)); - } - - /** - * Instruct an Observable to emit an item (returned by a specified function) - * rather than invoking {@link Observer#onError onError} if it encounters an - * error. - *

    - * - *

    - * By default, when an Observable encounters an error that prevents it from - * emitting the expected item to its {@link Observer}, the Observable - * invokes its Observer's onError method, and then quits - * without invoking any more of its Observer's methods. The - * onErrorReturn method changes this behavior. If you pass a - * function (resumeFunction) to an Observable's - * onErrorReturn method, if the original Observable encounters - * an error, instead of invoking its Observer's onError method, - * it will instead pass the return value of resumeFunction to - * the Observer's {@link Observer#onNext onNext} method. - *

    - * You can use this to prevent errors from propagating or to supply fallback - * data should errors be encountered. - * - * @param resumeFunction a function that returns an item that the new - * Observable will emit if the source Observable - * encounters an error - * @return the original Observable with appropriately modified behavior - * @see onErrorReturn() - */ - public Observable onErrorReturn(Func1 resumeFunction) { - return create(OperationOnErrorReturn.onErrorReturn(this, resumeFunction)); - } - - /** - * Returns an Observable that applies a function of your choosing to the - * first item emitted by a source Observable, then feeds the result of that - * function along with the second item emitted by the source Observable into - * the same function, and so on until all items have been emitted by the - * source Observable, and emits the final result from the final call to your - * function as its sole item. - *

    - * - *

    - * This technique, which is called "reduce" or "aggregate" here, is - * sometimes called "fold," "accumulate," "compress," or "inject" in other - * programming contexts. Groovy, for instance, has an inject - * method that does a similar operation on lists. - * - * @param accumulator an accumulator function to be invoked on each item - * emitted by the source Observable, whose result will - * be used in the next accumulator call - * @return an Observable that emits a single item that is the result of - * accumulating the output from the source Observable - * @throws IllegalArgumentException if the Observable sequence is empty - * @see reduce() - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) - */ - public Observable reduce(Func2 accumulator) { - /* - * Discussion and confirmation of implementation at https://github.com/Netflix/RxJava/issues/423#issuecomment-27642532 - * - * It should use last() not takeLast(1) since it needs to emit an error if the sequence is empty. - */ - return create(OperationScan.scan(this, accumulator)).last(); - } - - /** - * Returns an Observable that counts the total number of items in the - * source Observable. - *

    - * - * - * @return an Observable that emits the number of counted elements of the - * source Observable as its single item - * @see count() - * @see MSDN: Observable.Count - */ - public Observable count() { - return reduce(0, new Func2() { - @Override - public Integer call(Integer t1, T t2) { - return t1 + 1; - } - }); - } - - /** - * Returns an Observable that sums up the integers emitted by the source - * Observable. - *

    - * - * - * @param source source Observable to compute the sum of - * @return an Observable that emits the sum of all the items of the - * source Observable as its single item - * @see sum() - * @see MSDN: Observable.Sum - */ - public static Observable sum(Observable source) { - return OperationSum.sum(source); - } - - /** - * Returns an Observable that sums up the longs emitted by the source - * Observable. - *

    - * - * - * @param source source Observable to compute the sum of - * @return an Observable that emits the sum of all the items of the - * source Observable as its single item - * @see sumLongs() - * @see MSDN: Observable.Sum - */ - public static Observable sumLongs(Observable source) { - return OperationSum.sumLongs(source); - } - - /** - * Returns an Observable that sums up the floats emitted by the source - * Observable. - *

    - * - * - * @param source source Observable to compute the sum of - * @return an Observable that emits the sum of all the items of the - * source Observable as its single item - * @see sumFloats() - * @see MSDN: Observable.Sum - */ - public static Observable sumFloats(Observable source) { - return OperationSum.sumFloats(source); - } - - /** - * Returns an Observable that sums up the doubles emitted by the source - * Observable. - *

    - * - * - * @param source source Observable to compute the sum of - * @return an Observable that emits the sum of all the items of the - * source Observable as its single item - * @see sumDoubles() - * @see MSDN: Observable.Sum - */ - public static Observable sumDoubles(Observable source) { - return OperationSum.sumDoubles(source); - } - - /** - * Returns an Observable that computes the average of the integers emitted - * by the source Observable. - *

    - * - * - * @param source source observable to compute the average of - * @return an Observable that emits the average of all the items emitted by - * the source Observable as its single item - * @throws IllegalArgumentException if the Observable sequence is empty - * @see average() - * @see MSDN: Observable.Average - */ - public static Observable average(Observable source) { - return OperationAverage.average(source); - } - - /** - * Returns an Observable that computes the average of the longs emitted by - * the source Observable. - *

    - * - * - * @param source source observable to compute the average of - * @return an Observable that emits the average of all the items emitted by - * the source Observable as its single item - * @see averageLongs() - * @see MSDN: Observable.Average - */ - public static Observable averageLongs(Observable source) { - return OperationAverage.averageLongs(source); - } - - /** - * Returns an Observable that computes the average of the floats emitted by - * the source Observable. - *

    - * - * - * @param source source observable to compute the average of - * @return an Observable that emits the average of all the items emitted by - * the source Observable as its single item - * @see averageFloats() - * @see MSDN: Observable.Average - */ - public static Observable averageFloats(Observable source) { - return OperationAverage.averageFloats(source); - } - - /** - * Returns an Observable that computes the average of the doubles emitted - * by the source Observable. - *

    - * - * - * @param source source observable to compute the average of - * @return an Observable that emits the average of all the items emitted by - * the source Observable as its single item - * @see averageDoubles() - * @see MSDN: Observable.Average - */ - public static Observable averageDoubles(Observable source) { - return OperationAverage.averageDoubles(source); - } - - /** - * Returns the minimum item emitted by an Observable. If there are more than - * one minimum items, its returns the last one. - *

    - * - * - * @param source an Observable sequence to determine the minimum item of - * @return an Observable that emits the minimum item - * @throws IllegalArgumentException if the source is empty - * @see MSDN: Observable.Min - */ - public static > Observable min(Observable source) { - return OperationMinMax.min(source); - } - - /** - * Returns the minimum item emitted by an Observable according to a - * specified comparator. If there are more than one minimum items, it - * returns the last one. - *

    - * - * - * @param comparator the comparer used to compare elements - * @return an Observable that emits the minimum value according to the - * specified comparator - * @throws IllegalArgumentException if the source is empty - * @see min() - * @see MSDN: Observable.Min - */ - public Observable min(Comparator comparator) { - return OperationMinMax.min(this, comparator); - } - - /** - * Returns the items emitted by an Observable sequence with the minimum key - * value. For an empty source, returns an Observable that emits an empty - * List. - *

    - * - * - * @param selector the key selector function - * @return an Observable that emits a List of the items with the minimum key - * value - * @see minBy() - * @see MSDN: Observable.MinBy - */ - public > Observable> minBy(Func1 selector) { - return OperationMinMax.minBy(this, selector); - } - - /** - * Returns the elements emitted by an Observable with the minimum key value - * according to the specified comparator. For an empty source, it returns an - * Observable that emits an empty List. - *

    - * - * - * @param selector the key selector function - * @param comparator the comparator used to compare key values - * @return an Observable that emits a List of the elements with the minimum - * key value according to the specified comparator - * @see minBy() - * @see MSDN: Observable.MinBy - */ - public Observable> minBy(Func1 selector, Comparator comparator) { - return OperationMinMax.minBy(this, selector, comparator); - } - - /** - * Returns the maximum item emitted by an Observable. If there is more - * than one maximum item, it returns the last one. - *

    - * - * - * @param source an Observable to determine the maximum item of - * @return an Observable that emits the maximum element - * @throws IllegalArgumentException if the source is empty - * @see max() - * @see MSDN: Observable.Max - */ - public static > Observable max(Observable source) { - return OperationMinMax.max(source); - } - - /** - * Returns the maximum item emitted by an Observable according to the - * specified comparator. If there is more than one maximum item, it returns - * the last one. - *

    - * - * - * @param comparator the comparer used to compare items - * @return an Observable that emits the maximum item according to the - * specified comparator - * @throws IllegalArgumentException if the source is empty - * @see max() - * @see MSDN: Observable.Max - */ - public Observable max(Comparator comparator) { - return OperationMinMax.max(this, comparator); - } - - /** - * Returns the items emitted by an Observable with the maximum key value. - * For an empty source, it returns an Observable that emits an empty List. - *

    - * - * - * @param selector the key selector function - * @return an Observable that emits a List of the items with the maximum key - * value - * @see maxBy() - * @see MSDN: Observable.MaxBy - */ - public > Observable> maxBy(Func1 selector) { - return OperationMinMax.maxBy(this, selector); - } - - /** - * Returns the items emitted by an Observable with the maximum key value - * according to the specified comparator. For an empty source, it returns an - * Observable that emits an empty List. - *

    - * - * - * @param selector the key selector function - * @param comparator the comparator used to compare key values - * @return an Observable that emits a List of the elements with the maximum - * key value according to the specified comparator - * @see maxBy() - * @see MSDN: Observable.MaxBy - */ - public Observable> maxBy(Func1 selector, Comparator comparator) { - return OperationMinMax.maxBy(this, selector, comparator); - } - - /** - * Returns a {@link ConnectableObservable} that shares a single subscription - * to the underlying Observable that will replay all of its items and - * notifications to any future {@link Observer}. - *

    - * - * - * @return a {@link ConnectableObservable} that upon connection causes the - * source Observable to emit items to its {@link Observer}s - * @see replay() - */ - public ConnectableObservable replay() { - return OperationMulticast.multicast(this, ReplaySubject. create()); - } - - /** - * Retry subscription to origin Observable upto given retry count. - *

    - * - *

    - * If {@link Observer#onError} is invoked the source Observable will be - * re-subscribed to as many times as defined by retryCount. - *

    - * Any {@link Observer#onNext} calls received on each attempt will be - * emitted and concatenated together. - *

    - * For example, if an Observable fails on first time but emits [1, 2] then - * succeeds the second time and emits [1, 2, 3, 4, 5] then the complete - * output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. - * - * @param retryCount number of retry attempts before failing - * @return an Observable with retry logic - * @see retry() - */ - public Observable retry(int retryCount) { - return create(OperationRetry.retry(this, retryCount)); - } - - /** - * Retry subscription to origin Observable whenever onError is - * called (infinite retry count). - *

    - * - *

    - * If {@link Observer#onError} is invoked the source Observable will be - * re-subscribed to. - *

    - * Any {@link Observer#onNext} calls received on each attempt will be - * emitted and concatenated together. - *

    - * For example, if an Observable fails on first time but emits [1, 2] then - * succeeds the second time and emits [1, 2, 3, 4, 5] then the complete - * output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. - * - * @return an Observable with retry logic - * @see retry() - */ - public Observable retry() { - return create(OperationRetry.retry(this)); - } - - /** - * This method has similar behavior to {@link #replay} except that this - * auto-subscribes to the source Observable rather than returning a - * {@link ConnectableObservable}. - *

    - * - *

    - * This is useful when you want an Observable to cache responses and you - * can't control the subscribe/unsubscribe behavior of all the - * {@link Observer}s. - *

    - * Note: You sacrifice the ability to unsubscribe from the origin when you - * use the cache() operator so be careful not to use this - * operator on Observables that emit an infinite or very large number of - * items that will use up memory. - * - * @return an Observable that, when first subscribed to, caches all of its - * notifications for the benefit of subsequent subscribers. - * @see cache() - */ - public Observable cache() { - return create(OperationCache.cache(this)); - } - - /** - * Perform work in parallel by sharding an {@code Observable} on a - * {@link Schedulers#threadPoolForComputation()} {@link Scheduler} and - * return an {@code Observable} with the output. - *

    - * - * - * @param f a {@link Func1} that applies Observable operators to - * {@code Observable} in parallel and returns an - * {@code Observable} - * @return an Observable with the output of the {@link Func1} executed on a - * {@link Scheduler} - * @see parallel() - */ - public Observable parallel(Func1, Observable> f) { - return OperationParallel.parallel(this, f); - } - - /** - * Perform work in parallel by sharding an {@code Observable} on a - * {@link Scheduler} and return an {@code Observable} with the output. - *

    - * - * - * @param f a {@link Func1} that applies Observable operators to - * {@code Observable} in parallel and returns an - * {@code Observable} - * @param s a {@link Scheduler} to perform the work on - * @return an Observable with the output of the {@link Func1} executed on a - * {@link Scheduler} - * @see parallel() - */ - - public Observable parallel(final Func1, Observable> f, final Scheduler s) { - return OperationParallel.parallel(this, f, s); - } - - - /** - * Merges an Observable<Observable<T>> to - * Observable<Observable<T>> with the number of - * inner Observables defined by parallelObservables. - *

    - * For example, if the original - * Observable<Observable<T>> has 100 Observables to - * be emitted and parallelObservables is 8, the 100 will be - * grouped onto 8 output Observables. - *

    - * This is a mechanism for efficiently processing n number of - * Observables on a smaller m number of resources (typically CPU - * cores). - *

    - * - * - * @param parallelObservables the number of Observables to merge into - * @return an Observable of Observables constrained to number defined by - * parallelObservables - * @see parallelMerge() - */ - public static Observable> parallelMerge(Observable> source, int parallelObservables) { - return OperationParallelMerge.parallelMerge(source, parallelObservables); - } - - /** - * Merges an Observable<Observable<T>> to - * Observable<Observable<T>> with the number of - * inner Observables defined by parallelObservables and runs - * each Observable on the defined Scheduler. - *

    - * For example, if the original - * Observable<Observable<T>> has 100 Observables to - * be emitted and parallelObservables is 8, the 100 will be - * grouped onto 8 output Observables. - *

    - * This is a mechanism for efficiently processing n number of - * Observables on a smaller m number of resources (typically CPU - * cores). - *

    - * - * - * @param parallelObservables the number of Observables to merge into - * @return an Observable of Observables constrained to number defined by - * parallelObservables. - * @see parallelMerge() - */ - public static Observable> parallelMerge(Observable> source, int parallelObservables, Scheduler scheduler) { - return OperationParallelMerge.parallelMerge(source, parallelObservables, scheduler); - } - - /** - * Returns a {@link ConnectableObservable}, which waits until its - * {@link ConnectableObservable#connect connect} method is called before it - * begins emitting items to those {@link Observer}s that have subscribed to - * it. - *

    - * - * - * @return a {@link ConnectableObservable} that upon connection causes the - * source Observable to emit items to its {@link Observer}s - * @see publish() - */ - public ConnectableObservable publish() { - return OperationMulticast.multicast(this, PublishSubject. create()); - } - - /** - * Returns a {@link ConnectableObservable} that shares a single subscription - * that contains the last notification only. - *

    - * - * - * @return a {@link ConnectableObservable} - * @see publishLast() - */ - public ConnectableObservable publishLast() { - return OperationMulticast.multicast(this, AsyncSubject. create()); - } - - /** - * Synonymous with reduce(). - *

    - * - * - * @see aggregate() - * @see #reduce(Func2) - */ - public Observable aggregate(Func2 accumulator) { - return reduce(accumulator); - } - - /** - * Returns an Observable that applies a function of your choosing to the - * first item emitted by a source Observable, then feeds the result of that - * function along with the second item emitted by an Observable into the - * same function, and so on until all items have been emitted by the source - * Observable, emitting the final result from the final call to your - * function as its sole item. - *

    - * - *

    - * This technique, which is called "reduce" or "aggregate" here, is - * sometimes called "fold," "accumulate," "compress," or "inject" in other - * programming contexts. Groovy, for instance, has an inject - * method that does a similar operation on lists. - * - * @param initialValue the initial (seed) accumulator value - * @param accumulator an accumulator function to be invoked on each item - * emitted by the source Observable, the result of which - * will be used in the next accumulator call - * @return an Observable that emits a single item that is the result of - * accumulating the output from the items emitted by the source - * Observable - * @see reduce() - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) - */ - public Observable reduce(R initialValue, Func2 accumulator) { - return create(OperationScan.scan(this, initialValue, accumulator)).takeLast(1); - } - - /** - * Synonymous with reduce(). - *

    - * - * - * @see aggregate() - * @see #reduce(Object, Func2) - */ - public Observable aggregate(R initialValue, Func2 accumulator) { - return reduce(initialValue, accumulator); - } - - /** - * Returns an Observable that applies a function of your choosing to the - * first item emitted by a source Observable, then feeds the result of that - * function along with the second item emitted by an Observable into the - * same function, and so on until all items have been emitted by the source - * Observable, emitting the result of each of these iterations. - *

    - * - *

    - * This sort of function is sometimes called an accumulator. - *

    - * Note that when you pass a seed to scan() the resulting - * Observable will emit that seed as its first emitted item. - * - * @param accumulator an accumulator function to be invoked on each item - * emitted by the source Observable, whose result will be - * emitted to {@link Observer}s via - * {@link Observer#onNext onNext} and used in the next - * accumulator call - * @return an Observable that emits the results of each call to the - * accumulator function - * @see scan() - * @see MSDN: Observable.Scan - */ - public Observable scan(Func2 accumulator) { - return create(OperationScan.scan(this, accumulator)); - } - - /** - * Returns an Observable that emits the results of sampling the items - * emitted by the source Observable at a specified time interval. - *

    - * - * - * @param period the sampling rate - * @param unit the {@link TimeUnit} in which period is defined - * @return an Observable that emits the results of sampling the items - * emitted by the source Observable at the specified time interval - * @see sample() - */ - public Observable sample(long period, TimeUnit unit) { - return create(OperationSample.sample(this, period, unit)); - } - - /** - * Returns an Observable that emits the results of sampling the items - * emitted by the source Observable at a specified time interval. - *

    - * - * - * @param period the sampling rate - * @param unit the {@link TimeUnit} in which period is defined - * @param scheduler the {@link Scheduler} to use when sampling - * @return an Observable that emits the results of sampling the items - * emitted by the source Observable at the specified time interval - * @see sample() - */ - public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { - return create(OperationSample.sample(this, period, unit, scheduler)); - } - - /** - * Returns an Observable that applies a function of your choosing to the - * first item emitted by a source Observable, then feeds the result of that - * function along with the second item emitted by an Observable into the - * same function, and so on until all items have been emitted by the source - * Observable, emitting the result of each of these iterations. - *

    - * - *

    - * This sort of function is sometimes called an accumulator. - *

    - * Note that when you pass a seed to scan() the resulting - * Observable will emit that seed as its first emitted item. - * - * @param initialValue the initial (seed) accumulator value - * @param accumulator an accumulator function to be invoked on each item - * emitted by the source Observable, whose result will be - * emitted to {@link Observer}s via - * {@link Observer#onNext onNext} and used in the next - * accumulator call - * @return an Observable that emits the results of each call to the - * accumulator function - * @see scan() - * @see MSDN: Observable.Scan - */ - public Observable scan(R initialValue, Func2 accumulator) { - return create(OperationScan.scan(this, initialValue, accumulator)); - } - - /** - * Returns an Observable that emits a Boolean that indicates whether all of - * the items emitted by the source Observable satisfy a condition. - *

    - * - * - * @param predicate a function that evaluates an item and returns a Boolean - * @return an Observable that emits true if all items emitted - * by the source Observable satisfy the predicate; otherwise, - * false - * @see all() - */ - public Observable all(Func1 predicate) { - return create(OperationAll.all(this, predicate)); - } - - /** - * Returns an Observable that skips the first num items emitted - * by the source Observable and emits the remainder. - *

    - * - *

    - * You can ignore the first num items emitted by an Observable - * and attend only to those items that come after, by modifying the - * Observable with the skip method. - * - * @param num the number of items to skip - * @return an Observable that is identical to the source Observable except - * that it does not emit the first num items that the - * source emits - * @see skip() - */ - public Observable skip(int num) { - return create(OperationSkip.skip(this, num)); - } - - /** - * Returns an Observable that emits only the very first item emitted by the - * source Observable. - *

    - * - * - * @return an Observable that emits only the very first item from the - * source, or none if the source Observable completes without - * emitting a single item - * @see first() - * @see MSDN: Observable.First - */ - public Observable first() { - return take(1); - } - - /** - * Returns an Observable that emits only the very first item emitted by the - * source Observable that satisfies a given condition. - *

    - * - * - * @param predicate the condition any source emitted item has to satisfy - * @return an Observable that emits only the very first item satisfying the - * given condition from the source, or none if the source Observable - * completes without emitting a single matching item - * @see first() - * @see MSDN: Observable.First - */ - public Observable first(Func1 predicate) { - return skipWhile(not(predicate)).take(1); - } - - /** - * Returns an Observable that emits only the very first item emitted by the - * source Observable, or a default value. - *

    - * - * - * @param defaultValue the default value to emit if the source Observable - * doesn't emit anything - * @return an Observable that emits only the very first item from the - * source, or a default value if the source Observable completes - * without emitting a single item - * @see firstOrDefault() - * @see MSDN: Observable.FirstOrDefault - */ - public Observable firstOrDefault(T defaultValue) { - return create(OperationFirstOrDefault.firstOrDefault(this, defaultValue)); - } - - /** - * Returns an Observable that emits only the very first item emitted by the - * source Observable that satisfies a given condition, or a default value - * otherwise. - *

    - * - * - * @param predicate the condition any source emitted item has to satisfy - * @param defaultValue the default value to emit if the source Observable - * doesn't emit anything that satisfies the given condition - * @return an Observable that emits only the very first item from the source - * that satisfies the given condition, or a default value otherwise - * @see firstOrDefault() - * @see MSDN: Observable.FirstOrDefault - */ - public Observable firstOrDefault(Func1 predicate, T defaultValue) { - return create(OperationFirstOrDefault.firstOrDefault(this, predicate, defaultValue)); - } - - /** - * Returns the elements of the specified sequence or the specified default - * value in a singleton sequence if the sequence is empty. - *

    - * - * - * @param defaultValue the value to return if the sequence is empty - * @return an Observable that emits the specified default value if the - * source is empty; otherwise, the items emitted by the source - * @see defaultIfEmpty() - * @see MSDN: Observable.DefaultIfEmpty - */ - public Observable defaultIfEmpty(T defaultValue) { - return create(OperationDefaultIfEmpty.defaultIfEmpty(this, defaultValue)); - } - - /** - * Returns an Observable that emits only the first num items - * emitted by the source Observable. - *

    - * - *

    - * This method returns an Observable that will invoke a subscribing - * {@link Observer}'s {@link Observer#onNext onNext} function a maximum of - * num times before invoking - * {@link Observer#onCompleted onCompleted}. - * - * @param num the number of items to emit - * @return an Observable that emits only the first num items - * from the source Observable, or all of the items from the source - * Observable if that Observable emits fewer than num - * items - * @see take() - */ - public Observable take(final int num) { - return create(OperationTake.take(this, num)); - } - - /** - * Returns an Observable that emits items emitted by the source Observable - * so long as a specified condition is true. - *

    - * - * - * @param predicate a function that evaluates an item emitted by the source - * Observable and returns a Boolean - * @return an Observable that emits the items from the source Observable so - * long as each item satisfies the condition defined by - * predicate - * @see takeWhile() - */ - public Observable takeWhile(final Func1 predicate) { - return create(OperationTakeWhile.takeWhile(this, predicate)); - } - - /** - * Returns an Observable that emits the items emitted by a source Observable - * so long as a given predicate remains true, where the predicate can - * operate on both the item and its index relative to the complete sequence. - *

    - * - * - * @param predicate a function to test each item emitted by the source - * Observable for a condition; the second parameter of the - * function represents the index of the source item - * @return an Observable that emits items from the source Observable so long - * as the predicate continues to return true for each - * item, then completes - * @see takeWhileWithIndex() - */ - public Observable takeWhileWithIndex(final Func2 predicate) { - return create(OperationTakeWhile.takeWhileWithIndex(this, predicate)); - } - - /** - * Returns an Observable that emits only the very first item emitted by the - * source Observable. - *

    - * - * - * @return an Observable that emits only the very first item from the - * source, or none if the source Observable completes without - * emitting a single item - * @see first() - * @see MSDN: Observable.First - * @see #first() - */ - public Observable takeFirst() { - return first(); - } - - /** - * Returns an Observable that emits only the very first item emitted by the - * source Observable that satisfies a given condition. - *

    - * - * - * @param predicate the condition any source emitted item has to satisfy - * @return an Observable that emits only the very first item satisfying the - * given condition from the source, or none if the source Observable - * completes without emitting a single matching item - * @see first() - * @see MSDN: Observable.First - * @see #first(Func1) - */ - public Observable takeFirst(Func1 predicate) { - return first(predicate); - } - - /** - * Returns an Observable that emits only the last count items - * emitted by the source Observable. - *

    - * - * - * @param count the number of items to emit from the end of the sequence - * emitted by the source Observable - * @return an Observable that emits only the last count items - * emitted by the source Observable - * @see takeLast() - */ - public Observable takeLast(final int count) { - return create(OperationTakeLast.takeLast(this, count)); - } - - /** - * Returns an Observable that emits the items from the source Observable - * only until the other Observable emits an item. - *

    - * - * - * @param other the Observable whose first emitted item will cause - * takeUntil to stop emitting items from the - * source Observable - * @param the type of items emitted by other - * @return an Observable that emits the items of the source Observable until - * such time as other emits its first item - * @see takeUntil() - */ - public Observable takeUntil(Observable other) { - return OperationTakeUntil.takeUntil(this, other); - } - - /** - * Returns an Observable that bypasses all items from the source Observable - * as long as the specified condition holds true, but emits all further - * source items as soon as the condition becomes false. - *

    - * - * - * @param predicate a function to test each item emitted from the source - * Observable for a condition. It receives the emitted item - * as the first parameter and the index of the emitted item - * as a second parameter. - * @return an Observable that emits all items from the source Observable as - * soon as the condition becomes false - * @see skipWhileWithIndex() - * @see MSDN: Observable.SkipWhile - */ - public Observable skipWhileWithIndex(Func2 predicate) { - return create(OperationSkipWhile.skipWhileWithIndex(this, predicate)); - } - - /** - * Returns an Observable that bypasses all items from the source Observable - * as long as the specified condition holds true, but emits all further - * source items as soon as the condition becomes false. - *

    - * - * - * @param predicate a function to test each item emitted from the source - * Observable for a condition - * @return an Observable that emits all items from the source Observable as - * soon as the condition becomes false - * @see skipWhile() - * @see MSDN: Observable.SkipWhile - */ - public Observable skipWhile(Func1 predicate) { - return create(OperationSkipWhile.skipWhile(this, predicate)); - } - - /** - * Bypasses a specified number of items at the end of an Observable - * sequence. - *

    - * This operator accumulates a queue with a length enough to store the first - * count items. As more items are received, items are taken - * from the front of the queue and produced on the result sequence. This - * causes elements to be delayed. - *

    - * - * - * @param count number of elements to bypass at the end of the source - * sequence - * @return an Observable sequence emitting the source sequence items - * except for the bypassed ones at the end - * @throws IndexOutOfBoundsException if count is less than zero - * @see skipLast() - * @see MSDN: Observable.SkipLast - */ - public Observable skipLast(int count) { - return create(OperationSkipLast.skipLast(this, count)); - } - - /** - * Returns an Observable that emits a single item, a list composed of all - * the items emitted by the source Observable. - *

    - * - *

    - * Normally, an Observable that returns multiple items will do so by - * invoking its {@link Observer}'s {@link Observer#onNext onNext} method for - * each such item. You can change this behavior, instructing the Observable - * to compose a list of all of these items and then to invoke the Observer's - * onNext function once, passing it the entire list, by calling - * the Observable's toList method prior to calling its - * {@link #subscribe} method. - *

    - * Be careful not to use this operator on Observables that emit infinite or - * very large numbers of items, as you do not have the option to - * unsubscribe. - * - * @return an Observable that emits a single item: a List containing all of - * the items emitted by the source Observable. - * @see toList() - */ - public Observable> toList() { - return create(OperationToObservableList.toObservableList(this)); - } - - /** - * Return an Observable that emits the items emitted by the source - * Observable, in a sorted order (each item emitted by the Observable must - * implement {@link Comparable} with respect to all other items in the - * sequence). - *

    - * - * - * @throws ClassCastException if any item emitted by the Observable does not - * implement {@link Comparable} with respect to - * all other items emitted by the Observable - * @return an Observable that emits the items from the source Observable in - * sorted order - * @see toSortedList() - */ - public Observable> toSortedList() { - return create(OperationToObservableSortedList.toSortedList(this)); - } - - /** - * Return an Observable that emits the items emitted by the source - * Observable, in a sorted order based on a specified comparison function - *

    - * - * - * @param sortFunction a function that compares two items emitted by the - * source Observable and returns an Integer that - * indicates their sort order - * @return an Observable that emits the items from the source Observable in - * sorted order - * @see toSortedList() - */ - public Observable> toSortedList(Func2 sortFunction) { - return create(OperationToObservableSortedList.toSortedList(this, sortFunction)); - } - - /** - * Emit a specified set of items before beginning to emit items from the - * source Observable. - *

    - * - * - * @param values Iterable of the items you want the modified Observable to - * emit first - * @return an Observable that exhibits the modified behavior - * @see startWith() - */ - public Observable startWith(Iterable values) { - return concat(Observable. from(values), this); - } - - /** - * Emit a specified set of items with the specified scheduler before - * beginning to emit items from the source Observable. - *

    - * - * - * @param values iterable of the items you want the modified Observable to - * emit first - * @param scheduler the scheduler to emit the prepended values on - * @return an Observable that exhibits the modified behavior - * @see startWith() - * @see MSDN: Observable.StartWith - */ - public Observable startWith(Iterable values, Scheduler scheduler) { - return concat(from(values, scheduler), this); - } - - /** - * Emit a specified array of items with the specified scheduler before - * beginning to emit items from the source Observable. - *

    - * - * - * @param values the items you want the modified Observable to emit first - * @param scheduler the scheduler to emit the prepended values on - * @return an Observable that exhibits the modified behavior - * @see startWith() - * @see MSDN: Observable.StartWith - */ - public Observable startWith(T[] values, Scheduler scheduler) { - return startWith(Arrays.asList(values), scheduler); - } - - /** - * Emit a specified item before beginning to emit items from the source - * Observable. - *

    - * - * - * @param t1 item to emit - * @return an Observable that exhibits the modified behavior - * @see startWith() - */ - public Observable startWith(T t1) { - return concat(Observable. from(t1), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the - * source Observable. - *

    - * - * - * @param t1 first item to emit - * @param t2 second item to emit - * @return an Observable that exhibits the modified behavior - * @see startWith() - */ - public Observable startWith(T t1, T t2) { - return concat(Observable. from(t1, t2), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the - * source Observable. - *

    - * - * - * @param t1 first item to emit - * @param t2 second item to emit - * @param t3 third item to emit - * @return an Observable that exhibits the modified behavior - * @see startWith() - */ - public Observable startWith(T t1, T t2, T t3) { - return concat(Observable. from(t1, t2, t3), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the - * source Observable. - *

    - * - * - * @param t1 first item to emit - * @param t2 second item to emit - * @param t3 third item to emit - * @param t4 fourth item to emit - * @return an Observable that exhibits the modified behavior - * @see startWith() - */ - public Observable startWith(T t1, T t2, T t3, T t4) { - return concat(Observable. from(t1, t2, t3, t4), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the - * source Observable. - *

    - * - * - * @param t1 first item to emit - * @param t2 second item to emit - * @param t3 third item to emit - * @param t4 fourth item to emit - * @param t5 fifth item to emit - * @return an Observable that exhibits the modified behavior - * @see startWith() - */ - public Observable startWith(T t1, T t2, T t3, T t4, T t5) { - return concat(Observable. from(t1, t2, t3, t4, t5), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the - * source Observable. - *

    - * - * - * @param t1 first item to emit - * @param t2 second item to emit - * @param t3 third item to emit - * @param t4 fourth item to emit - * @param t5 fifth item to emit - * @param t6 sixth item to emit - * @return an Observable that exhibits the modified behavior - * @see startWith() - */ - public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { - return concat(Observable. from(t1, t2, t3, t4, t5, t6), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the - * source Observable. - *

    - * - * - * @param t1 first item to emit - * @param t2 second item to emit - * @param t3 third item to emit - * @param t4 fourth item to emit - * @param t5 fifth item to emit - * @param t6 sixth item to emit - * @param t7 seventh item to emit - * @return an Observable that exhibits the modified behavior - * @see startWith() - */ - public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { - return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the - * source Observable. - *

    - * - * - * @param t1 first item to emit - * @param t2 second item to emit - * @param t3 third item to emit - * @param t4 fourth item to emit - * @param t5 fifth item to emit - * @param t6 sixth item to emit - * @param t7 seventh item to emit - * @param t8 eighth item to emit - * @return an Observable that exhibits the modified behavior - * @see startWith() - */ - public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { - return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8), this); - } - - /** - * Emit a specified set of items before beginning to emit items from the - * source Observable. - *

    - * - * - * @param t1 first item to emit - * @param t2 second item to emit - * @param t3 third item to emit - * @param t4 fourth item to emit - * @param t5 fifth item to emit - * @param t6 sixth item to emit - * @param t7 seventh item to emit - * @param t8 eighth item to emit - * @param t9 ninth item to emit - * @return an Observable that exhibits the modified behavior - * @see startWith() - */ - public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { - return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8, t9), this); - } - - /** - * Groups the items emitted by an Observable according to a specified - * criterion, and emits these grouped items as {@link GroupedObservable}s, - * one GroupedObservable per group. - *

    - * - * - * @param keySelector a function that extracts the key from an item - * @param elementSelector a function to map a source item to an item in a - * {@link GroupedObservable} - * @param the key type - * @param the type of items emitted by the resulting - * {@link GroupedObservable}s - * @return an Observable that emits {@link GroupedObservable}s, each of - * which corresponds to a unique key value and emits items - * representing items from the source Observable that share that key - * value - * @see groupBy - */ - public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) { - return create(OperationGroupBy.groupBy(this, keySelector, elementSelector)); - } - - /** - * Groups the items emitted by an Observable according to a specified - * criterion, and emits these grouped items as {@link GroupedObservable}s, - * one GroupedObservable per group. - *

    - * - * - * @param keySelector a function that extracts the key for each item - * @param the key type - * @return an Observable that emits {@link GroupedObservable}s, each of - * which corresponds to a unique key value and emits items - * representing items from the source Observable that share that key - * value - * @see groupBy - */ - public Observable> groupBy(final Func1 keySelector) { - return create(OperationGroupBy.groupBy(this, keySelector)); - } - - /** - * Returns an {@link Observable} that emits true if the source - * {@link Observable} is empty, otherwise false. - *

    - * In Rx.Net this is negated as the any operator but renamed in - * RxJava to better match Java naming idioms. - *

    - * - * - * @return an Observable that emits a Boolean - * @see isEmpty() - * @see MSDN: Observable.Any - */ - public Observable isEmpty() { - return create(OperationAny.isEmpty(this)); - } - - /** - * Returns an {@link Observable} that emits the last item emitted by the - * source or an IllegalArgumentException if the source - * {@link Observable} is empty. - *

    - * - * - * @return - * @see last() - */ - public Observable last() { - return create(OperationLast.last(this)); - } - - /** - * Converts an Observable into a {@link BlockingObservable} (an Observable - * with blocking operators). - * - * @return - * @see Blocking Observable Operators - */ - public BlockingObservable toBlockingObservable() { - return BlockingObservable.from(this); - } - - /** - * Converts the items emitted by an Observable to the specified type. - *

    - * - * - * @param klass the target class type which the items will be converted to - * @return an Observable that emits each item from the source Observable - * converted to the specified type - * @see cast() - * @see MSDN: Observable.Cast - */ - public Observable cast(final Class klass) { - return create(OperationCast.cast(this, klass)); - } - - /** - * Filters the items emitted by an Observable based on the specified type. - *

    - * - * - * @param klass the class type to filter the items emitted by the source - * Observable - * @return an Observable that emits items from the source Observable of - * type klass. - * @see ofClass() - * @see MSDN: Observable.OfType - */ - public Observable ofType(final Class klass) { - return filter(new Func1() { - public Boolean call(T t) { - return klass.isInstance(t); - } - }).cast(klass); - } - - /** - * Ignores all items emitted by an Observable and only calls - * onCompleted or onError. - *

    - * - * - * @return an empty Observable that only calls onCompleted or - * onError - * @see ignoreElements() - * @see MSDN: Observable.IgnoreElements - */ - public Observable ignoreElements() { - return filter(alwaysFalse()); - } - - /** - * Applies a timeout policy for each element in the observable sequence, - * using the specified scheduler to run timeout timers. If the next element - * isn't received within the specified timeout duration starting from its - * predecessor, a TimeoutException is propagated to the observer. - *

    - * - * - * @param timeout maximum duration between values before a timeout occurs - * @param timeUnit the unit of time which applies to the - * timeout argument. - * @return the source Observable with a TimeoutException in - * case of a timeout - * @see timeout() - * @see MSDN: Observable.Timeout - */ - public Observable timeout(long timeout, TimeUnit timeUnit) { - return create(OperationTimeout.timeout(this, timeout, timeUnit)); - } - - /** - * Applies a timeout policy for each element in the observable sequence, - * using the specified scheduler to run timeout timers. If the next element - * isn't received within the specified timeout duration starting from its - * predecessor, the other observable sequence is used to produce future - * messages from that point on. - *

    - * - * - * @param timeout maximum duration between values before a timeout occurs - * @param timeUnit the unit of time which applies to the - * timeout argument - * @param other sequence to return in case of a timeout - * @return the source sequence switching to the other sequence in case of a - * timeout - * @see timeout() - * @see MSDN: Observable.Timeout - */ - public Observable timeout(long timeout, TimeUnit timeUnit, Observable other) { - return create(OperationTimeout.timeout(this, timeout, timeUnit, other)); - } - - /** - * Applies a timeout policy for each element in the observable sequence, - * using the specified scheduler to run timeout timers. If the next element - * isn't received within the specified timeout duration starting from its - * predecessor, a TimeoutException is propagated to the observer. - *

    - * - * - * @param timeout maximum duration between values before a timeout occurs - * @param timeUnit the unit of time which applies to the - * timeout argument - * @param scheduler Scheduler to run the timeout timers on - * @return the source sequence with a TimeoutException in case - * of a timeout - * @see timeout() - * @see MSDN: Observable.Timeout - */ - public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { - return create(OperationTimeout.timeout(this, timeout, timeUnit, scheduler)); - } - - /** - * Applies a timeout policy for each element in the observable sequence, - * using the specified scheduler to run timeout timers. If the next element - * isn't received within the specified timeout duration starting from its - * predecessor, the other observable sequence is used to produce future - * messages from that point on. - *

    - * - * - * @param timeout maximum duration between values before a timeout occurs - * @param timeUnit the unit of time which applies to the - * timeout argument - * @param other sequence to return in case of a timeout - * @param scheduler Scheduler to run the timeout timers on - * @return the source sequence switching to the other sequence in case of a - * timeout - * @see timeout() - * @see MSDN: Observable.Timeout - */ - public Observable timeout(long timeout, TimeUnit timeUnit, Observable other, Scheduler scheduler) { - return create(OperationTimeout.timeout(this, timeout, timeUnit, other, scheduler)); - } - - /** - * Records the time interval between consecutive items emitted by an - * Observable. - *

    - * - * - * @return an Observable that emits time interval information items - * @see timeInterval() - * @see MSDN: Observable.TimeInterval - */ - public Observable> timeInterval() { - return create(OperationTimeInterval.timeInterval(this)); - } - - /** - * Records the time interval between consecutive items emitted by an - * Observable, using the specified Scheduler to compute time intervals. - *

    - * - * - * @param scheduler Scheduler used to compute time intervals - * @return an Observable that emits time interval information items - * @see timeInterval() - * @see MSDN: Observable.TimeInterval - */ - public Observable> timeInterval(Scheduler scheduler) { - return create(OperationTimeInterval.timeInterval(this, scheduler)); - } - - /** - * Constructs an Observable that depends on a resource object. - *

    - * - * - * @param resourceFactory the factory function to obtain a resource object - * that depends on the Observable - * @param observableFactory the factory function to obtain an Observable - * @return the Observable whose lifetime controls the lifetime of the - * dependent resource object - * @see using() - * @see MSDN: Observable.Using - */ - public static Observable using(Func0 resourceFactory, Func1> observableFactory) { - return create(OperationUsing.using(resourceFactory, observableFactory)); - } - - /** - * Propagates the Observable sequence that reacts first. - *

    - * - * - * @param o1 an Observable competing to react first - * @param o2 an Observable competing to react first - * @return an Observable that reflects whichever of the given Observables - * reacted first - * @see amb() - * @see MSDN: Observable.Amb - */ - public static Observable amb(Observable o1, Observable o2) { - return create(OperationAmb.amb(o1, o2)); - } - - /** - * Propagates the Observable sequence that reacts first. - *

    - * - * - * @param o1 an Observable competing to react first - * @param o2 an Observable competing to react first - * @param o3 an Observable competing to react first - * @return an Observable that reflects whichever of the given Observables - * reacted first - * @see amb() - * @see MSDN: Observable.Amb - */ - public static Observable amb(Observable o1, Observable o2, Observable o3) { - return create(OperationAmb.amb(o1, o2, o3)); - } - - /** - * Propagates the observable sequence that reacts first. - *

    - * - * - * @param o1 an Observable competing to react first - * @param o2 an Observable competing to react first - * @param o3 an Observable competing to react first - * @param o4 an Observable competing to react first - * @return an Observable that reflects whichever of the given Observables - * reacted first - * @see amb() - * @see MSDN: Observable.Amb - */ - public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4) { - return create(OperationAmb.amb(o1, o2, o3, o4)); - } - - /** - * Propagates the Observable sequence that reacts first. - *

    - * - * - * @param o1 an Observable competing to react first - * @param o2 an Observable competing to react first - * @param o3 an Observable competing to react first - * @param o4 an Observable competing to react first - * @param o5 an Observable competing to react first - * @return an Observable that reflects whichever of the given Observables - * reacted first - * @see amb() - * @see MSDN: Observable.Amb - */ - public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5) { - return create(OperationAmb.amb(o1, o2, o3, o4, o5)); - } - - /** - * Propagates the observable sequence that reacts first. - *

    - * - * - * @param o1 an Observable competing to react first - * @param o2 an Observable competing to react first - * @param o3 an Observable competing to react first - * @param o4 an Observable competing to react first - * @param o5 an Observable competing to react first - * @param o6 an Observable competing to react first - * @return an Observable that reflects whichever of the given Observables - * reacted first - * @see amb() - * @see MSDN: Observable.Amb - */ - public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6) { - return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6)); - } - - /** - * Propagates the observable sequence that reacts first. - *

    - * - * - * @param o1 an Observable competing to react first - * @param o2 an Observable competing to react first - * @param o3 an Observable competing to react first - * @param o4 an Observable competing to react first - * @param o5 an Observable competing to react first - * @param o6 an Observable competing to react first - * @param o7 an Observable competing to react first - * @return an Observable that reflects whichever of the given Observables - * reacted first - * @see amb() - * @see MSDN: Observable.Amb - */ - public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7) { - return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7)); - } - - /** - * Propagates the observable sequence that reacts first. - *

    - * - * - * @param o1 an Observable competing to react first - * @param o2 an Observable competing to react first - * @param o3 an Observable competing to react first - * @param o4 an Observable competing to react first - * @param o5 an Observable competing to react first - * @param o6 an Observable competing to react first - * @param o7 an Observable competing to react first - * @param o8 an observable competing to react first - * @return an Observable that reflects whichever of the given Observables - * reacted first - * @see amb() - * @see MSDN: Observable.Amb - */ - public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8) { - return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8)); - } - - /** - * Propagates the observable sequence that reacts first. - *

    - * - * - * @param o1 an Observable competing to react first - * @param o2 an Observable competing to react first - * @param o3 an Observable competing to react first - * @param o4 an Observable competing to react first - * @param o5 an Observable competing to react first - * @param o6 an Observable competing to react first - * @param o7 an Observable competing to react first - * @param o8 an Observable competing to react first - * @param o9 an Observable competing to react first - * @return an Observable that reflects whichever of the given Observables - * reacted first - * @see amb() - * @see MSDN: Observable.Amb - */ - public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9) { - return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8, o9)); - } - - /** - * Propagates the observable sequence that reacts first. - *

    - * - * - * @param sources Observable sources competing to react first - * @return an Observable that reflects whichever of the given Observables - * reacted first - * @see amb() - * @see MSDN: Observable.Amb - */ - public static Observable amb(Iterable> sources) { - return create(OperationAmb.amb(sources)); - } - - /** - * Invokes an action for each item emitted by the Observable. - *

    - * - * - * @param observer the action to invoke for each item emitted in the source - * sequence - * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() - * @see MSDN: Observable.Do - */ - public Observable doOnEach(Observer observer) { - return create(OperationDoOnEach.doOnEach(this, observer)); - } - - /** - * Invokes an action for each item emitted by an Observable. - *

    - * - * - * @param onNext the action to invoke for each item in the source - * sequence - * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() - * @see MSDN: Observable.Do - */ - public Observable doOnEach(final Action1 onNext) { - Observer observer = new Observer() { - @Override - public void onCompleted() {} - - @Override - public void onError(Throwable e) {} - - @Override - public void onNext(T args) { - onNext.call(args); - } - - }; - - - return create(OperationDoOnEach.doOnEach(this, observer)); - } - - /** - * Invokes an action if onError is called from the Observable. - *

    - * - * - * @param onError the action to invoke if onError is invoked - * @return the source sequence with the side-effecting behavior applied - * @see doOnError() - * @see MSDN: Observable.Do - */ - public Observable doOnError(final Action1 onError) { - Observer observer = new Observer() { - @Override - public void onCompleted() {} - - @Override - public void onError(Throwable e) { - onError.call(e); - } - - @Override - public void onNext(T args) { } - - }; - - - return create(OperationDoOnEach.doOnEach(this, observer)); - } - - /** - * Invokes an action when onCompleted is called by the - * Observable. - *

    - * - * - * @param onCompleted the action to invoke when onCompleted is - * called - * @return the source sequence with the side-effecting behavior applied - * @see doOnCompleted() - * @see MSDN: Observable.Do - */ - public Observable doOnCompleted(final Action0 onCompleted) { - Observer observer = new Observer() { - @Override - public void onCompleted() { - onCompleted.call(); - } - - @Override - public void onError(Throwable e) { } - - @Override - public void onNext(T args) { } - - }; - - - return create(OperationDoOnEach.doOnEach(this, observer)); - } - - /** - * Invokes an action for each item emitted by an Observable. - * - * @param onNext the action to invoke for each item in the source sequence - * @param onError the action to invoke when the source Observable calls - * onError - * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() - * @see MSDN: Observable.Do - */ - public Observable doOnEach(final Action1 onNext, final Action1 onError) { - Observer observer = new Observer() { - @Override - public void onCompleted() {} - - @Override - public void onError(Throwable e) { - onError.call(e); - } - - @Override - public void onNext(T args) { - onNext.call(args); - } - - }; - - - return create(OperationDoOnEach.doOnEach(this, observer)); - } - - /** - * Invokes an action for each item emitted by an Observable. - * - * @param onNext the action to invoke for each item in the source sequence - * @param onError the action to invoke when the source Observable calls - * onError - * @param onCompleted the action to invoke when the source Observable calls - * onCompleted - * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() - * @see MSDN: Observable.Do - */ - public Observable doOnEach(final Action1 onNext, final Action1 onError, final Action0 onCompleted) { - Observer observer = new Observer() { - @Override - public void onCompleted() { - onCompleted.call(); - } - - @Override - public void onError(Throwable e) { - onError.call(e); - } - - @Override - public void onNext(T args) { - onNext.call(args); - } - - }; - - - return create(OperationDoOnEach.doOnEach(this, observer)); - } - - /** - * Whether a given {@link Function} is an internal implementation inside - * rx.* packages or not. - *

    - * For why this is being used see - * https://github.com/Netflix/RxJava/issues/216 for discussion on - * "Guideline 6.4: Protect calls to user code from within an operator" - * - * Note: If strong reasons for not depending on package names comes up then - * the implementation of this method can change to looking for a marker - * interface. - * - * @param o - * @return {@code true} if the given function is an internal implementation, - * and {@code false} otherwise. - */ - private boolean isInternalImplementation(Object o) { - if (o == null) { - return true; - } - // prevent double-wrapping (yeah it happens) - if (o instanceof SafeObserver) { - return true; - } - - Class clazz = o.getClass(); - if (internalClassMap.containsKey(clazz)) { - //don't need to do reflection - return internalClassMap.get(clazz); - } else { - // we treat the following package as "internal" and don't wrap it - Package p = o.getClass().getPackage(); // it can be null - Boolean isInternal = (p != null && p.getName().startsWith("rx.operators")); - internalClassMap.put(clazz, isInternal); - return isInternal; - } - } - - /** - * Creates a pattern that matches when both Observable sequences have an - * available item. - *

    - * - * - * @param right Observable sequence to match with the left sequence - * @return Pattern object that matches when both Observable sequences have - * an available item - * @throws NullPointerException if right is null - * @see and() - * @see MSDN: Observable.And - */ - public Pattern2 and(Observable right) { - return OperationJoinPatterns.and(this, right); - } - - /** - * Matches when the Observable sequence has an available item and - * projects the item by invoking the selector function. - *

    - * - * - * @param selector Selector that will be invoked for elements in the source - * sequence - * @return Plan that produces the projected results, to be fed (with other - * plans) to the When operator - * @throws NullPointerException if selector is null - * @see then() - * @see MSDN: Observable.Then - */ - public Plan0 then(Func1 selector) { - return OperationJoinPatterns.then(this, selector); - } - - /** - * Joins together the results from several patterns. - *

    - * - * - * @param plans a series of plans created by use of the Then operator on - * patterns - * @return an Observable sequence with the results from matching several - * patterns - * @throws NullPointerException if plans is null - * @see when() - * @see MSDN: Observable.When - */ - public static Observable when(Plan0... plans) { - return create(OperationJoinPatterns.when(plans)); - } - - /** - * Joins together the results from several patterns. - *

    - * - * - * @param plans a series of plans created by use of the Then operator on - * patterns - * @return an Observable sequence with the results from matching several - * patterns - * @throws NullPointerException if plans is null - * @see when() - * @see MSDN: Observable.When - */ - public static Observable when(Iterable> plans) { - if (plans == null) { - throw new NullPointerException("plans"); - } - return create(OperationJoinPatterns.when(plans)); - } - - /** - * Joins the results from a pattern. - *

    - * - * - * @param p1 the plan to join - * @return an Observable sequence with the results from matching a pattern - * @see when() - * @see MSDN: Observable.When - */ - @SuppressWarnings("unchecked") - public static Observable when(Plan0 p1) { - return create(OperationJoinPatterns.when(p1)); - } - - /** - * Joins together the results from several patterns. - *

    - * - * - * @param p1 a plan - * @param p2 a plan - * @return an Observable sequence with the results from matching several - * patterns - * @see when() - * @see MSDN: Observable.When - */ - @SuppressWarnings("unchecked") - public static Observable when(Plan0 p1, Plan0 p2) { - return create(OperationJoinPatterns.when(p1, p2)); - } - - /** - * Joins together the results from several patterns. - *

    - * - * - * @param p1 a plan - * @param p2 a plan - * @param p3 a plan - * @return an Observable sequence with the results from matching several - * patterns - * @see when() - * @see MSDN: Observable.When - */ - @SuppressWarnings("unchecked") - public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3) { - return create(OperationJoinPatterns.when(p1, p2, p3)); - } - - /** - * Joins together the results from several patterns. - *

    - * - * - * @param p1 a plan - * @param p2 a plan - * @param p3 a plan - * @param p4 a plan - * @return an Observable sequence with the results from matching several - * patterns - * @see when() - * @see MSDN: Observable.When - */ - @SuppressWarnings("unchecked") - public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4) { - return create(OperationJoinPatterns.when(p1, p2, p3, p4)); - } - - /** - * Joins together the results from several patterns. - *

    - * - * - * @param p1 a plan - * @param p2 a plan - * @param p3 a plan - * @param p4 a plan - * @param p5 a plan - * @return an Observable sequence with the results from matching several - * patterns - * @see when() - * @see MSDN: Observable.When - */ - @SuppressWarnings("unchecked") - public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5) { - return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5)); - } - - /** - * Joins together the results from several patterns. - *

    - * - * - * @param p1 a plan - * @param p2 a plan - * @param p3 a plan - * @param p4 a plan - * @param p5 a plan - * @param p6 a plan - * @return an Observable sequence with the results from matching several - * patterns - * @see when() - * @see MSDN: Observable.When - */ - @SuppressWarnings("unchecked") - public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6) { - return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6)); - } - - /** - * Joins together the results from several patterns. - *

    - * - * - * @param p1 a plan - * @param p2 a plan - * @param p3 a plan - * @param p4 a plan - * @param p5 a plan - * @param p6 a plan - * @param p7 a plan - * @return an Observable sequence with the results from matching several - * patterns - * @see when() - * @see MSDN: Observable.When - */ - @SuppressWarnings("unchecked") - public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7) { - return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7)); - } - - /** - * Joins together the results from several patterns. - *

    - * - * - * @param p1 a plan - * @param p2 a plan - * @param p3 a plan - * @param p4 a plan - * @param p5 a plan - * @param p6 a plan - * @param p7 a plan - * @param p8 a plan - * @return an Observable sequence with the results from matching several - * patterns - * @see when() - * @see MSDN: Observable.When - */ - @SuppressWarnings("unchecked") - public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8) { - return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8)); - } - - /** - * Joins together the results from several patterns. - *

    - * - * - * @param p1 a plan - * @param p2 a plan - * @param p3 a plan - * @param p4 a plan - * @param p5 a plan - * @param p6 a plan - * @param p7 a plan - * @param p8 a plan - * @param p9 a plan - * @return an Observable sequence with the results from matching several - * patterns - * @see when() - * @see MSDN: Observable.When - */ - @SuppressWarnings("unchecked") - public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8, Plan0 p9) { - return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8, p9)); - } -} - +/** + * Copyright 2013 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 rx.util.functions.Functions.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import rx.concurrency.Schedulers; +import rx.joins.Pattern2; +import rx.joins.Plan0; +import rx.observables.BlockingObservable; +import rx.observables.ConnectableObservable; +import rx.observables.GroupedObservable; +import rx.operators.OperationAll; +import rx.operators.OperationAmb; +import rx.operators.OperationAny; +import rx.operators.OperationAverage; +import rx.operators.OperationBuffer; +import rx.operators.OperationCache; +import rx.operators.OperationCast; +import rx.operators.OperationCombineLatest; +import rx.operators.OperationConcat; +import rx.operators.OperationDebounce; +import rx.operators.OperationDefaultIfEmpty; +import rx.operators.OperationDefer; +import rx.operators.OperationDematerialize; +import rx.operators.OperationDistinct; +import rx.operators.OperationDistinctUntilChanged; +import rx.operators.OperationDoOnEach; +import rx.operators.OperationElementAt; +import rx.operators.OperationFilter; +import rx.operators.OperationFinally; +import rx.operators.OperationFirstOrDefault; +import rx.operators.OperationGroupBy; +import rx.operators.OperationInterval; +import rx.operators.OperationJoin; +import rx.operators.OperationJoinPatterns; +import rx.operators.OperationLast; +import rx.operators.OperationMap; +import rx.operators.OperationMaterialize; +import rx.operators.OperationMerge; +import rx.operators.OperationMergeDelayError; +import rx.operators.OperationMinMax; +import rx.operators.OperationMulticast; +import rx.operators.OperationObserveOn; +import rx.operators.OperationOnErrorResumeNextViaFunction; +import rx.operators.OperationOnErrorResumeNextViaObservable; +import rx.operators.OperationOnErrorReturn; +import rx.operators.OperationOnExceptionResumeNextViaObservable; +import rx.operators.OperationParallel; +import rx.operators.OperationParallelMerge; +import rx.operators.OperationRetry; +import rx.operators.OperationSample; +import rx.operators.OperationScan; +import rx.operators.OperationSkip; +import rx.operators.OperationSkipLast; +import rx.operators.OperationSkipWhile; +import rx.operators.OperationSubscribeOn; +import rx.operators.OperationSum; +import rx.operators.OperationSwitch; +import rx.operators.OperationSynchronize; +import rx.operators.OperationTake; +import rx.operators.OperationTakeLast; +import rx.operators.OperationTakeUntil; +import rx.operators.OperationTakeWhile; +import rx.operators.OperationThrottleFirst; +import rx.operators.OperationTimeInterval; +import rx.operators.OperationTimeout; +import rx.operators.OperationTimestamp; +import rx.operators.OperationToObservableFuture; +import rx.operators.OperationToObservableIterable; +import rx.operators.OperationToObservableList; +import rx.operators.OperationToObservableSortedList; +import rx.operators.OperationUsing; +import rx.operators.OperationWindow; +import rx.operators.OperationZip; +import rx.operators.SafeObservableSubscription; +import rx.operators.SafeObserver; +import rx.plugins.RxJavaErrorHandler; +import rx.plugins.RxJavaObservableExecutionHook; +import rx.plugins.RxJavaPlugins; +import rx.subjects.AsyncSubject; +import rx.subjects.PublishSubject; +import rx.subjects.ReplaySubject; +import rx.subjects.Subject; +import rx.subscriptions.Subscriptions; +import rx.util.Closing; +import rx.util.OnErrorNotImplementedException; +import rx.util.Opening; +import rx.util.Range; +import rx.util.TimeInterval; +import rx.util.Timestamped; +import rx.util.functions.Action0; +import rx.util.functions.Action1; +import rx.util.functions.Func0; +import rx.util.functions.Func1; +import rx.util.functions.Func2; +import rx.util.functions.Func3; +import rx.util.functions.Func4; +import rx.util.functions.Func5; +import rx.util.functions.Func6; +import rx.util.functions.Func7; +import rx.util.functions.Func8; +import rx.util.functions.Func9; +import rx.util.functions.FuncN; +import rx.util.functions.Function; + +/** + * The Observable interface that implements the Reactive Pattern. + *

    + * This interface provides overloaded methods for subscribing as well as + * delegate methods to the various operators. + *

    + * The documentation for this interface makes use of marble diagrams. The + * following legend explains these diagrams: + *

    + * + *

    + * For more information see the + * RxJava Wiki + * + * @param the type of the item emitted by the Observable + */ +public class Observable { + + private final static ConcurrentHashMap internalClassMap = new ConcurrentHashMap(); + + /** + * Executed when 'subscribe' is invoked. + */ + private final OnSubscribeFunc onSubscribe; + + /** + * Function interface for work to be performed when an {@link Observable} + * is subscribed to via {@link Observable#subscribe(Observer)} + * + * @param + */ + public static interface OnSubscribeFunc extends Function { + + public Subscription onSubscribe(Observer t1); + + } + + /** + * Observable with Function to execute when subscribed to. + *

    + * NOTE: Use {@link #create(OnSubscribeFunc)} to create an Observable + * instead of this constructor unless you specifically have a need for + * inheritance. + * + * @param onSubscribe {@link OnSubscribeFunc} to be executed when + * {@link #subscribe(Observer)} is called + */ + protected Observable(OnSubscribeFunc onSubscribe) { + this.onSubscribe = onSubscribe; + } + + private final static RxJavaObservableExecutionHook hook = RxJavaPlugins.getInstance().getObservableExecutionHook(); + + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. + *

    + * A typical implementation of {@code subscribe} does the following: + *

      + *
    1. It stores a reference to the Observer in a collection object, such as + * a {@code List} object.
    2. + *
    3. It returns a reference to the {@link Subscription} interface. This + * enables Observers to unsubscribe, that is, to stop receiving items + * and notifications before the Observable stops sending them, which + * also invokes the Observer's {@link Observer#onCompleted onCompleted} + * method.
    4. + *

    + * An Observable<T> instance is responsible for accepting + * all subscriptions and notifying all Observers. Unless the documentation + * for a particular Observable<T> implementation + * indicates otherwise, Observers should make no assumptions about the order + * in which multiple Observers will receive their notifications. + *

    + * For more information see the + * RxJava Wiki + * + * @param observer the Observer + * @return a {@link Subscription} reference with which the {@link Observer} + * can stop receiving items before the Observable has finished + * sending them + * @throws IllegalArgumentException if the {@link Observer} provided as the + * argument to {@code subscribe()} is + * {@code null} + */ + public Subscription subscribe(Observer observer) { + // allow the hook to intercept and/or decorate + OnSubscribeFunc onSubscribeFunction = hook.onSubscribeStart(this, onSubscribe); + // validate and proceed + if (observer == null) { + throw new IllegalArgumentException("observer can not be null"); + } + if (onSubscribeFunction == null) { + throw new IllegalStateException("onSubscribe function can not be null."); + // the subscribe function can also be overridden but generally that's not the appropriate approach so I won't mention that in the exception + } + try { + /** + * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" + */ + if (isInternalImplementation(observer)) { + Subscription s = onSubscribeFunction.onSubscribe(observer); + if (s == null) { + // this generally shouldn't be the case on a 'trusted' onSubscribe but in case it happens + // we want to gracefully handle it the same as AtomicObservableSubscription does + return hook.onSubscribeReturn(this, Subscriptions.empty()); + } else { + return hook.onSubscribeReturn(this, s); + } + } else { + SafeObservableSubscription subscription = new SafeObservableSubscription(); + subscription.wrap(onSubscribeFunction.onSubscribe(new SafeObserver(subscription, observer))); + return hook.onSubscribeReturn(this, subscription); + } + } catch (OnErrorNotImplementedException e) { + // special handling when onError is not implemented ... we just rethrow + throw e; + } catch (Throwable e) { + // if an unhandled error occurs executing the onSubscribe we will propagate it + try { + observer.onError(hook.onSubscribeError(this, e)); + } catch (OnErrorNotImplementedException e2) { + // special handling when onError is not implemented ... we just rethrow + throw e2; + } catch (Throwable e2) { + // if this happens it means the onError itself failed (perhaps an invalid function implementation) + // so we are unable to propagate the error correctly and will just throw + RuntimeException r = new RuntimeException("Error occurred attempting to subscribe [" + e.getMessage() + "] and then again while trying to pass to onError.", e2); + hook.onSubscribeError(this, r); + throw r; + } + return Subscriptions.empty(); + } + } + + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. + *

    + * A typical implementation of {@code subscribe} does the following: + *

      + *
    1. It stores a reference to the Observer in a collection object, such as + * a {@code List} object.
    2. + *
    3. It returns a reference to the {@link Subscription} interface. This + * enables Observers to unsubscribe, that is, to stop receiving items + * and notifications before the Observable stops sending them, which + * also invokes the Observer's {@link Observer#onCompleted onCompleted} + * method.
    4. + *

    + * An {@code Observable} instance is responsible for accepting all + * subscriptions and notifying all Observers. Unless the documentation for a + * particular {@code Observable} implementation indicates otherwise, + * Observers should make no assumptions about the order in which multiple + * Observers will receive their notifications. + *

    + * For more information see the + * RxJava Wiki + * + * @param observer the Observer + * @param scheduler the {@link Scheduler} on which Observers subscribe to + * the Observable + * @return a {@link Subscription} reference with which Observers can stop + * receiving items and notifications before the Observable has + * finished sending them + * @throws IllegalArgumentException if an argument to {@code subscribe()} + * is {@code null} + */ + public Subscription subscribe(Observer observer, Scheduler scheduler) { + return subscribeOn(scheduler).subscribe(observer); + } + + /** + * Protects against errors being thrown from Observer implementations and + * ensures onNext/onError/onCompleted contract compliance. + *

    + * See https://github.com/Netflix/RxJava/issues/216 for a discussion on + * "Guideline 6.4: Protect calls to user code from within an operator" + */ + private Subscription protectivelyWrapAndSubscribe(Observer o) { + SafeObservableSubscription subscription = new SafeObservableSubscription(); + return subscription.wrap(subscribe(new SafeObserver(subscription, o))); + } + + /** + * Subscribe and ignore all events. + * + * @return + */ + public Subscription subscribe() { + return protectivelyWrapAndSubscribe(new Observer() { + + @Override + public void onCompleted() { + // do nothing + } + + @Override + public void onError(Throwable e) { + handleError(e); + throw new OnErrorNotImplementedException(e); + } + + @Override + public void onNext(T args) { + // do nothing + } + + }); + } + + /** + * An {@link Observer} must call an Observable's {@code subscribe} method + * in order to receive items and notifications from the Observable. + * + * @param onNext + * @return + */ + public Subscription subscribe(final Action1 onNext) { + if (onNext == null) { + throw new IllegalArgumentException("onNext can not be null"); + } + + /** + * Wrapping since raw functions provided by the user are being invoked. + * + * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" + */ + return protectivelyWrapAndSubscribe(new Observer() { + + @Override + public void onCompleted() { + // do nothing + } + + @Override + public void onError(Throwable e) { + handleError(e); + throw new OnErrorNotImplementedException(e); + } + + @Override + public void onNext(T args) { + onNext.call(args); + } + + }); + } + + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. + * + * @param onNext + * @param scheduler + * @return + */ + public Subscription subscribe(final Action1 onNext, Scheduler scheduler) { + return subscribeOn(scheduler).subscribe(onNext); + } + + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. + * + * @param onNext + * @param onError + * @return + */ + public Subscription subscribe(final Action1 onNext, final Action1 onError) { + if (onNext == null) { + throw new IllegalArgumentException("onNext can not be null"); + } + if (onError == null) { + throw new IllegalArgumentException("onError can not be null"); + } + + /** + * Wrapping since raw functions provided by the user are being invoked. + * + * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" + */ + return protectivelyWrapAndSubscribe(new Observer() { + + @Override + public void onCompleted() { + // do nothing + } + + @Override + public void onError(Throwable e) { + handleError(e); + onError.call(e); + } + + @Override + public void onNext(T args) { + onNext.call(args); + } + + }); + } + + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. + * + * @param onNext + * @param onError + * @param scheduler + * @return + */ + public Subscription subscribe(final Action1 onNext, final Action1 onError, Scheduler scheduler) { + return subscribeOn(scheduler).subscribe(onNext, onError); + } + + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. + * + * @param onNext + * @param onError + * @param onComplete + * @return + */ + public Subscription subscribe(final Action1 onNext, final Action1 onError, final Action0 onComplete) { + if (onNext == null) { + throw new IllegalArgumentException("onNext can not be null"); + } + if (onError == null) { + throw new IllegalArgumentException("onError can not be null"); + } + if (onComplete == null) { + throw new IllegalArgumentException("onComplete can not be null"); + } + + /** + * Wrapping since raw functions provided by the user are being invoked. + * + * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" + */ + return protectivelyWrapAndSubscribe(new Observer() { + + @Override + public void onCompleted() { + onComplete.call(); + } + + @Override + public void onError(Throwable e) { + handleError(e); + onError.call(e); + } + + @Override + public void onNext(T args) { + onNext.call(args); + } + + }); + } + + /** + * An {@link Observer} must call an Observable's {@code subscribe} method in + * order to receive items and notifications from the Observable. + * + * @param onNext + * @param onError + * @param onComplete + * @param scheduler + * @return + */ + public Subscription subscribe(final Action1 onNext, final Action1 onError, final Action0 onComplete, Scheduler scheduler) { + return subscribeOn(scheduler).subscribe(onNext, onError, onComplete); + } + + /** + * Returns a {@link ConnectableObservable} that upon connection causes the + * source Observable to push results into the specified subject. + * + * @param subject the {@link Subject} for the {@link ConnectableObservable} + * to push source items into + * @param result type + * @return a {@link ConnectableObservable} that upon connection causes the + * source Observable to push results into the specified + * {@link Subject} + * @see Observable.publish() and Observable.multicast() + */ + public ConnectableObservable multicast(Subject subject) { + return OperationMulticast.multicast(this, subject); + } + + /** + * Allow the {@link RxJavaErrorHandler} to receive the exception from + * onError. + * + * @param e + */ + private void handleError(Throwable e) { + // onError should be rare so we'll only fetch when needed + RxJavaPlugins.getInstance().getErrorHandler().handleError(e); + } + + /** + * An Observable that never sends any information to an {@link Observer}. + * + * This Observable is useful primarily for testing purposes. + * + * @param the type of item emitted by the Observable + */ + private static class NeverObservable extends Observable { + public NeverObservable() { + super(new OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(Observer t1) { + return Subscriptions.empty(); + } + + }); + } + } + + /** + * An Observable that invokes {@link Observer#onError onError} when the + * {@link Observer} subscribes to it. + * + * @param the type of item emitted by the Observable + */ + private static class ThrowObservable extends Observable { + + public ThrowObservable(final Throwable exception) { + super(new OnSubscribeFunc() { + + /** + * Accepts an {@link Observer} and calls its + * {@link Observer#onError onError} method. + * + * @param observer an {@link Observer} of this Observable + * @return a reference to the subscription + */ + @Override + public Subscription onSubscribe(Observer observer) { + observer.onError(exception); + return Subscriptions.empty(); + } + + }); + } + + } + + /** + * Creates an Observable that will execute the given function when an + * {@link Observer} subscribes to it. + *

    + * + *

    + * Write the function you pass to create so that it behaves as + * an Observable: It should invoke the Observer's + * {@link Observer#onNext onNext}, {@link Observer#onError onError}, and + * {@link Observer#onCompleted onCompleted} methods appropriately. + *

    + * A well-formed Observable must invoke either the Observer's + * onCompleted method exactly once or its onError + * method exactly once. + *

    + * See Rx Design + * Guidelines (PDF) for detailed information. + * + * @param the type of the items that this Observable emits + * @param func a function that accepts an {@code Observer}, invokes its + * {@code onNext}, {@code onError}, and {@code onCompleted} + * methods as appropriate, and returns a {@link Subscription} to + * allow the Observer to cancel the subscription + * @return an Observable that, when an {@link Observer} subscribes to it, + * will execute the given function + * @see create() + */ + public static Observable create(OnSubscribeFunc func) { + return new Observable(func); + } + + /** + * Returns an Observable that emits no data to the {@link Observer} and + * immediately invokes its {@link Observer#onCompleted onCompleted} method. + *

    + * + * + * @param the type of the items (ostensibly) emitted by the Observable + * @return an Observable that returns no data to the {@link Observer} and + * immediately invokes the {@link Observer}'s + * {@link Observer#onCompleted() onCompleted} method + * @see empty() + * @see MSDN: Observable.Empty Method + */ + public static Observable empty() { + return from(new ArrayList()); + } + + /** + * Returns an Observable that emits no data to the {@link Observer} and + * immediately invokes its {@link Observer#onCompleted onCompleted} method + * with the specified scheduler. + *

    + * + * + * @param scheduler the scheduler to call the + {@link Observer#onCompleted onCompleted} method + * @param the type of the items (ostensibly) emitted by the Observable + * @return an Observable that returns no data to the {@link Observer} and + * immediately invokes the {@link Observer}'s + * {@link Observer#onCompleted() onCompleted} method with the + * specified scheduler + * @see empty() + * @see MSDN: Observable.Empty Method (IScheduler) + */ + public static Observable empty(Scheduler scheduler) { + return Observable. empty().subscribeOn(scheduler); + } + + /** + * Returns an Observable that invokes an {@link Observer}'s + * {@link Observer#onError onError} method when the Observer subscribes to + * it. + *

    + * + * + * @param exception the particular error to report + * @param the type of the items (ostensibly) emitted by the Observable + * @return an Observable that invokes the {@link Observer}'s + * {@link Observer#onError onError} method when the Observer + * subscribes to it + * @see error() + * @see MSDN: Observable.Throw Method + */ + public static Observable error(Throwable exception) { + return new ThrowObservable(exception); + } + + /** + * Returns an Observable that invokes an {@link Observer}'s + * {@link Observer#onError onError} method with the specified scheduler. + *

    + * + * + * @param exception the particular error to report + * @param scheduler the scheduler to call the + * {@link Observer#onError onError} method + * @param the type of the items (ostensibly) emitted by the Observable + * @return an Observable that invokes the {@link Observer}'s + * {@link Observer#onError onError} method with the specified + * scheduler + * @see error() + * @see MSDN: Observable.Throw Method + */ + public static Observable error(Throwable exception, Scheduler scheduler) { + return Observable. error(exception).subscribeOn(scheduler); + } + + /** + * Converts an {@link Iterable} sequence into an Observable. + *

    + * + *

    + * Note: the entire iterable sequence is immediately emitted each time an + * {@link Observer} subscribes. Since this occurs before the + * {@link Subscription} is returned, it is not possible to unsubscribe from + * the sequence before it completes. + * + * @param iterable the source {@link Iterable} sequence + * @param the type of items in the {@link Iterable} sequence and the + * type of items to be emitted by the resulting Observable + * @return an Observable that emits each item in the source {@link Iterable} + * sequence + * @see from() + */ + public static Observable from(Iterable iterable) { + return create(OperationToObservableIterable.toObservableIterable(iterable)); + } + + /** + * Converts an {@link Iterable} sequence into an Observable with the specified scheduler. + *

    + * + * + * @param iterable the source {@link Iterable} sequence + * @param scheduler the scheduler to emit the items of the iterable + * @param the type of items in the {@link Iterable} sequence and the + * type of items to be emitted by the resulting Observable + * @return an Observable that emits each item in the source {@link Iterable} + * sequence with the specified scheduler + * @see from() + * @see MSDN: Observable.ToObservable + */ + public static Observable from(Iterable iterable, Scheduler scheduler) { + return from(iterable).observeOn(scheduler); + } + + /** + * Converts an Array into an Observable. + *

    + * + *

    + * Note: the entire array is immediately emitted each time an + * {@link Observer} subscribes. Since this occurs before the + * {@link Subscription} is returned, it is not possible to unsubscribe from + * the sequence before it completes. + * + * @param items the source sequence + * @param the type of items in the Array and the type of items to be + * emitted by the resulting Observable + * @return an Observable that emits each item in the source Array + * @see from() + */ + public static Observable from(T[] items) { + return create(OperationToObservableIterable.toObservableIterable(Arrays.asList(items))); + } + + /** + * Converts an item into an Observable that emits that item. + *

    + * + *

    + * Note: the item is immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 the item + * @param the type of the item, and the type of the item to be + * emitted by the resulting Observable + * @return an Observable that emits the item + * @see from() + */ + @SuppressWarnings("unchecked") + // suppress unchecked because we are using varargs inside the method + public static Observable from(T t1) { + return from(Arrays.asList(t1)); + } + + /** + * Converts a series of items into an Observable. + *

    + * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param the type of items, and the type of items to be emitted by the + * resulting Observable + * @return an Observable that emits each item + * @see from() + */ + @SuppressWarnings("unchecked") + // suppress unchecked because we are using varargs inside the method + public static Observable from(T t1, T t2) { + return from(Arrays.asList(t1, t2)); + } + + /** + * Converts a series of items into an Observable. + *

    + * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param the type of items, and the type of items to be emitted by the + * resulting Observable + * @return an Observable that emits each item + * @see from() + */ + @SuppressWarnings("unchecked") + // suppress unchecked because we are using varargs inside the method + public static Observable from(T t1, T t2, T t3) { + return from(Arrays.asList(t1, t2, t3)); + } + + /** + * Converts a series of items into an Observable. + *

    + * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param the type of items, and the type of items to be emitted by the + * resulting Observable + * @return an Observable that emits each item + * @see from() + */ + @SuppressWarnings("unchecked") + // suppress unchecked because we are using varargs inside the method + public static Observable from(T t1, T t2, T t3, T t4) { + return from(Arrays.asList(t1, t2, t3, t4)); + } + + /** + * Converts a series of items into an Observable. + *

    + * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param the type of items, and the type of items to be emitted by the + * resulting Observable + * @return an Observable that emits each item + * @see from() + */ + @SuppressWarnings("unchecked") + // suppress unchecked because we are using varargs inside the method + public static Observable from(T t1, T t2, T t3, T t4, T t5) { + return from(Arrays.asList(t1, t2, t3, t4, t5)); + } + + /** + * Converts a series of items into an Observable. + *

    + * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param t6 sixth item + * @param the type of items, and the type of items to be emitted by the + * resulting Observable + * @return an Observable that emits each item + * @see from() + */ + @SuppressWarnings("unchecked") + // suppress unchecked because we are using varargs inside the method + public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6) { + return from(Arrays.asList(t1, t2, t3, t4, t5, t6)); + } + + /** + * Converts a series of items into an Observable. + *

    + * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param t6 sixth item + * @param t7 seventh item + * @param the type of items, and the type of items to be emitted by the + * resulting Observable + * @return an Observable that emits each item + * @see from() + */ + @SuppressWarnings("unchecked") + // suppress unchecked because we are using varargs inside the method + public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { + return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7)); + } + + /** + * Converts a series of items into an Observable. + *

    + * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param t6 sixth item + * @param t7 seventh item + * @param t8 eighth item + * @param the type of items, and the type of items to be emitted by the + * resulting Observable + * @return an Observable that emits each item + * @see from() + */ + @SuppressWarnings("unchecked") + // suppress unchecked because we are using varargs inside the method + public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { + return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8)); + } + + /** + * Converts a series of items into an Observable. + *

    + * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param t6 sixth item + * @param t7 seventh item + * @param t8 eighth item + * @param t9 ninth item + * @param the type of items, and the type of items to be emitted by the + * resulting Observable + * @return an Observable that emits each item + * @see from() + */ + @SuppressWarnings("unchecked") + // suppress unchecked because we are using varargs inside the method + public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { + return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8, t9)); + } + + /** + * Converts a series of items into an Observable. + *

    + * + *

    + * Note: the items will be immediately emitted each time an {@link Observer} + * subscribes. Since this occurs before the {@link Subscription} is + * returned, it is not possible to unsubscribe from the sequence before it + * completes. + * + * @param t1 first item + * @param t2 second item + * @param t3 third item + * @param t4 fourth item + * @param t5 fifth item + * @param t6 sixth item + * @param t7 seventh item + * @param t8 eighth item + * @param t9 ninth item + * @param t10 tenth item + * @param the type of items, and the type of items to be emitted by the + * resulting Observable + * @return an Observable that emits each item + * @see from() + */ + @SuppressWarnings("unchecked") + // suppress unchecked because we are using varargs inside the method + public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9, T t10) { + return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)); + } + + /** + * Generates an Observable that emits a sequence of integers within a + * specified range. + *

    + * + *

    + * Note: the entire range is immediately emitted each time an + * {@link Observer} subscribes. Since this occurs before the + * {@link Subscription} is returned, it is not possible to unsubscribe from + * the sequence before it completes. + * + * @param start the value of the first integer in the sequence + * @param count the number of sequential integers to generate + * @return an Observable that emits a range of sequential integers + * @see range() + * @see Observable.Range Method (Int32, Int32) + */ + public static Observable range(int start, int count) { + return from(Range.createWithCount(start, count)); + } + + /** + * Generates an Observable that emits a sequence of integers within a + * specified range with the specified scheduler. + *

    + * + * @param start the value of the first integer in the sequence + * @param count the number of sequential integers to generate + * @param scheduler the scheduler to run the generator loop on + * @return an Observable that emits a range of sequential integers + * @see range() + * @see Observable.Range Method (Int32, Int32, IScheduler) + */ + public static Observable range(int start, int count, Scheduler scheduler) { + return range(start, count).observeOn(scheduler); + } + + /** + * Returns an Observable that calls an Observable factory to create its + * Observable for each new Observer that subscribes. That is, for each + * subscriber, the actuall Observable is determined by the factory function. + *

    + * + *

    + * The defer operator allows you to defer or delay emitting items from an + * Observable until such time as an Observer subscribes to the Observable. + * This allows an {@link Observer} to easily obtain updates or a refreshed + * version of the sequence. + * + * @param observableFactory the Observable factory function to invoke for + * each {@link Observer} that subscribes to the + * resulting Observable + * @param the type of the items emitted by the Observable + * @return an Observable whose {@link Observer}s trigger an invocation of + * the given Observable factory function + * @see defer() + */ + public static Observable defer(Func0> observableFactory) { + return create(OperationDefer.defer(observableFactory)); + } + + /** + * Returns an Observable that emits a single item and then completes. + *

    + * + *

    + * To convert any object into an Observable that emits that object, pass + * that object into the just method. + *

    + * This is similar to the {@link #from(java.lang.Object[])} method, except + * that from() will convert an {@link Iterable} object into an + * Observable that emits each of the items in the Iterable, one at a time, + * while the just() method converts an Iterable into an + * Observable that emits the entire Iterable as a single item. + * + * @param value the item to pass to the {@link Observer}'s + * {@link Observer#onNext onNext} method + * @param the type of that item + * @return an Observable that emits a single item and then completes + * @see just() + */ + public static Observable just(T value) { + List list = new ArrayList(); + list.add(value); + + return from(list); + } + + /** + * Returns an Observable that emits a single item and then completes on a + * specified scheduler. + *

    + * This is a scheduler version of {@link Observable#just(Object)}. + * + * @param value the item to pass to the {@link Observer}'s + * {@link Observer#onNext onNext} method + * @param the type of that item + * @param scheduler the scheduler to send the single element on + * @return an Observable that emits a single item and then completes on a + * specified scheduler + * @see just() + */ + public static Observable just(T value, Scheduler scheduler) { + return just(value).observeOn(scheduler); + } + + /** + * Flattens a sequence of Observables emitted by an Observable into one + * Observable, without any transformation. + *

    + * + *

    + * You can combine the items emitted by multiple Observables so that they + * act like a single Observable, by using the {@code merge} method. + * + * @param source an Observable that emits Observables + * @return an Observable that emits items that are the result of flattening + * the items emitted by the Observables emitted by the + * {@code source} Observable + * @see merge() + * @see MSDN: Observable.Merge Method + */ + public static Observable merge(Observable> source) { + return create(OperationMerge.merge(source)); + } + + /** + * Flattens a series of Observables into one Observable, without any + * transformation. + *

    + * + *

    + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see merge() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable merge(Observable t1, Observable t2) { + return create(OperationMerge.merge(t1, t2)); + } + + /** + * Flattens a series of Observables into one Observable, without any + * transformation. + *

    + * + *

    + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see merge() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable merge(Observable t1, Observable t2, Observable t3) { + return create(OperationMerge.merge(t1, t2, t3)); + } + + /** + * Flattens a series of Observables into one Observable, without any + * transformation. + *

    + * + *

    + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see merge() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4) { + return create(OperationMerge.merge(t1, t2, t3, t4)); + } + + /** + * Flattens a series of Observables into one Observable, without any + * transformation. + *

    + * + *

    + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see merge() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { + return create(OperationMerge.merge(t1, t2, t3, t4, t5)); + } + + /** + * Flattens a series of Observables into one Observable, without any + * transformation. + *

    + * + *

    + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see merge() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { + return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6)); + } + + /** + * Flattens a series of Observables into one Observable, without any + * transformation. + *

    + * + *

    + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see merge() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { + return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6, t7)); + } + + /** + * Flattens a series of Observables into one Observable, without any + * transformation. + *

    + * + *

    + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @param t8 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see merge() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { + return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6, t7, t8)); + } + + /** + * Flattens a series of Observables into one Observable, without any + * transformation. + *

    + * + *

    + * You can combine items emitted by multiple Observables so that they act + * like a single Observable, by using the {@code merge} method. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @param t8 an Observable to be merged + * @param t9 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see merge() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable merge(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8, Observable t9) { + return create(OperationMerge.merge(t1, t2, t3, t4, t5, t6, t7, t8, t9)); + } + + /** + * Returns an Observable that emits the items emitted by two or more + * Observables, one after the other. + *

    + * + * + * @param observables an Observable that emits Observables + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see concat() + * @see MSDN: Observable.Concat Method + */ + public static Observable concat(Observable> observables) { + return create(OperationConcat.concat(observables)); + } + + /** + * Returns an Observable that emits the items emitted by two Observables, + * one after the other. + *

    + * + * + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see concat() + * @see MSDN: Observable.Concat Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable concat(Observable t1, Observable t2) { + return create(OperationConcat.concat(t1, t2)); + } + + /** + * Returns an Observable that emits the items emitted by three Observables, + * one after the other. + *

    + * + * + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see concat() + * @see MSDN: Observable.Concat Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable concat(Observable t1, Observable t2, Observable t3) { + return create(OperationConcat.concat(t1, t2, t3)); + } + + /** + * Returns an Observable that emits the items emitted by four Observables, + * one after the other. + *

    + * + * + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see concat() + * @see MSDN: Observable.Concat Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4) { + return create(OperationConcat.concat(t1, t2, t3, t4)); + } + + /** + * Returns an Observable that emits the items emitted by five Observables, + * one after the other. + *

    + * + * + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @param t5 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see concat() + * @see MSDN: Observable.Concat Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { + return create(OperationConcat.concat(t1, t2, t3, t4, t5)); + } + + /** + * Returns an Observable that emits the items emitted by six Observables, + * one after the other. + *

    + * + * + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @param t5 an Observable to be concatenated + * @param t6 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see concat() + * @see MSDN: Observable.Concat Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { + return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6)); + } + + /** + * Returns an Observable that emits the items emitted by secven Observables, + * one after the other. + *

    + * + * + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @param t5 an Observable to be concatenated + * @param t6 an Observable to be concatenated + * @param t7 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see concat() + * @see MSDN: Observable.Concat Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { + return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6, t7)); + } + + /** + * Returns an Observable that emits the items emitted by eight Observables, + * one after the other. + *

    + * + * + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @param t5 an Observable to be concatenated + * @param t6 an Observable to be concatenated + * @param t7 an Observable to be concatenated + * @param t8 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see concat() + * @see MSDN: Observable.Concat Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { + return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6, t7, t8)); + } + + /** + * Returns an Observable that emits the items emitted by nine Observables, + * one after the other. + *

    + * + * + * @param t1 an Observable to be concatenated + * @param t2 an Observable to be concatenated + * @param t3 an Observable to be concatenated + * @param t4 an Observable to be concatenated + * @param t5 an Observable to be concatenated + * @param t6 an Observable to be concatenated + * @param t7 an Observable to be concatenated + * @param t8 an Observable to be concatenated + * @param t9 an Observable to be concatenated + * @return an Observable that emits items that are the result of combining + * the items emitted by the {@code source} Observables, one after + * the other + * @see concat() + * @see MSDN: Observable.Concat Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable concat(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8, Observable t9) { + return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6, t7, t8, t9)); + } + + /** + * This behaves like {@link #merge(Observable)} except that if any of the + * merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. + *

    + * + *

    + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param source an Observable that emits Observables + * @return an Observable that emits items that are the result of flattening + * the items emitted by the Observables emitted by the + * {@code source} Observable + * @see mergeDelayError() + * @see MSDN: Observable.Merge Method + */ + public static Observable mergeDelayError(Observable> source) { + return create(OperationMergeDelayError.mergeDelayError(source)); + } + + /** + * This behaves like {@link #merge(Observable, Observable)} except that if + * any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. + *

    + * + *

    + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see mergeDelayError() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable mergeDelayError(Observable t1, Observable t2) { + return create(OperationMergeDelayError.mergeDelayError(t1, t2)); + } + + /** + * This behaves like {@link #merge(Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. + *

    + * + *

    + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see mergeDelayError() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3) { + return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3)); + } + + /** + * This behaves like + * {@link #merge(Observable, Observable, Observable, Observable)} except + * that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. + *

    + * + *

    + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see mergeDelayError() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4) { + return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4)); + } + + /** + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. + *

    + * + *

    + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see mergeDelayError() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5) { + return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5)); + } + + /** + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. + *

    + * + *

    + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see mergeDelayError() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6) { + return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6)); + } + + /** + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. + *

    + * + *

    + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see mergeDelayError() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7) { + return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6, t7)); + } + + /** + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. + *

    + * + *

    + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @param t8 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see mergeDelayError() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8) { + return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6, t7, t8)); + } + + /** + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} + * except that if any of the merged Observables notify of an error via + * {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged + * Observables have finished emitting items. + *

    + * + *

    + * Even if multiple merged Observables send {@code onError} notifications, + * {@code mergeDelayError} will only invoke the {@code onError} method of + * its Observers once. + *

    + * This method allows an Observer to receive all successfully emitted items + * from all of the source Observables without being interrupted by an error + * notification from one of them. + * + * @param t1 an Observable to be merged + * @param t2 an Observable to be merged + * @param t3 an Observable to be merged + * @param t4 an Observable to be merged + * @param t5 an Observable to be merged + * @param t6 an Observable to be merged + * @param t7 an Observable to be merged + * @param t8 an Observable to be merged + * @param t9 an Observable to be merged + * @return an Observable that emits items that are the result of flattening + * the items emitted by the {@code source} Observables + * @see mergeDelayError() + * @see MSDN: Observable.Merge Method + */ + @SuppressWarnings("unchecked") + // suppress because the types are checked by the method signature before using a vararg + public static Observable mergeDelayError(Observable t1, Observable t2, Observable t3, Observable t4, Observable t5, Observable t6, Observable t7, Observable t8, Observable t9) { + return create(OperationMergeDelayError.mergeDelayError(t1, t2, t3, t4, t5, t6, t7, t8, t9)); + } + + /** + * Returns an Observable that never sends any items or notifications to an + * {@link Observer}. + *

    + * + *

    + * This Observable is useful primarily for testing purposes. + * + * @param the type of items (not) emitted by the Observable + * @return an Observable that never sends any items or notifications to an + * {@link Observer} + * @see never() + */ + public static Observable never() { + return new NeverObservable(); + } + + /** + * Given an Observable that emits Observables, creates a single Observable + * that emits the items emitted by the most recently published of those + * Observables. + *

    + * + * + * @param sequenceOfSequences the source Observable that emits Observables + * @return an Observable that emits only the items emitted by the most + * recently published Observable + * @see switchOnNext() + * @deprecated use {@link #switchOnNext} + */ + @Deprecated + public static Observable switchDo(Observable> sequenceOfSequences) { + return create(OperationSwitch.switchDo(sequenceOfSequences)); + } + + /** + * Given an Observable that emits Observables, creates a single Observable + * that emits the items emitted by the most recently published of those + * Observables. + *

    + * + * + * @param sequenceOfSequences the source Observable that emits Observables + * @return an Observable that emits only the items emitted by the most + * recently published Observable + * @see switchOnNext() + */ + public static Observable switchOnNext(Observable> sequenceOfSequences) { + return create(OperationSwitch.switchDo(sequenceOfSequences)); + } + + /** + * Accepts an Observable and wraps it in another Observable that ensures + * that the resulting Observable is chronologically well-behaved. + *

    + * + *

    + * A well-behaved Observable does not interleave its invocations of the + * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, + * and {@link Observer#onError onError} methods of its {@link Observer}s; it + * invokes {@code onCompleted} or {@code onError} only once; and it never + * invokes {@code onNext} after invoking either {@code onCompleted} or + * {@code onError}. {@code synchronize} enforces this, and the Observable it + * returns invokes {@code onNext} and {@code onCompleted} or {@code onError} + * synchronously. + * + * @return an Observable that is a chronologically well-behaved version of + * the source Observable, and that synchronously notifies its + * {@link Observer}s + * @see synchronize() + */ + public Observable synchronize() { + return create(OperationSynchronize.synchronize(this)); + } + + /** + * Accepts an Observable and wraps it in another Observable that ensures + * that the resulting Observable is chronologically well-behaved. This is + * accomplished by acquiring a mutual-exclusion lock for the object + * provided as the lock parameter. + *

    + * + *

    + * A well-behaved Observable does not interleave its invocations of the + * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, + * and {@link Observer#onError onError} methods of its {@link Observer}s; it + * invokes {@code onCompleted} or {@code onError} only once; and it never + * invokes {@code onNext} after invoking either {@code onCompleted} or + * {@code onError}. {@code synchronize} enforces this, and the Observable it + * returns invokes {@code onNext} and {@code onCompleted} or {@code onError} + * synchronously. + * + * @param lock the lock object to synchronize each observer call on + * @return an Observable that is a chronologically well-behaved version of + * the source Observable, and that synchronously notifies its + * {@link Observer}s + * @see synchronize() + */ + public Observable synchronize(Object lock) { + return create(OperationSynchronize.synchronize(this, lock)); + } + + /** + * @deprecated use {@link #synchronize()} or {@link #synchronize(Object)} + */ + @Deprecated + public static Observable synchronize(Observable source) { + return create(OperationSynchronize.synchronize(source)); + } + + /** + * Emits an item each time interval (containing a sequential number). + *

    + * + * + * @param interval interval size in time units (see below) + * @param unit time units to use for the interval size + * @return an Observable that emits an item each time interval + * @see interval() + * @see MSDN: Observable.Interval + */ + public static Observable interval(long interval, TimeUnit unit) { + return create(OperationInterval.interval(interval, unit)); + } + + /** + * Emits an item each time interval (containing a sequential number). + *

    + * + * + * @param interval interval size in time units (see below) + * @param unit time units to use for the interval size + * @param scheduler the scheduler to use for scheduling the items + * @return an Observable that emits an item each time interval + * @see interval() + * @see MSDN: Observable.Interval + */ + public static Observable interval(long interval, TimeUnit unit, Scheduler scheduler) { + return create(OperationInterval.interval(interval, unit, scheduler)); + } + + /** + * Drops items emitted by an Observable that are followed by newer items + * before a timeout value expires. The timer resets on each emission. + *

    + * Note: If events keep firing faster than the timeout then no data will be + * emitted. + *

    + * + *

    + * Information on debounce vs throttle: + *

    + *

    + * + * @param timeout the time each value has to be "the most recent" of the + * {@link Observable} to ensure that it's not dropped + * @param unit the {@link TimeUnit} for the timeout + * @return an {@link Observable} that filters out items that are too + * quickly followed by newer items + * @see debounce() + * @see #throttleWithTimeout(long, TimeUnit) + */ + public Observable debounce(long timeout, TimeUnit unit) { + return create(OperationDebounce.debounce(this, timeout, unit)); + } + + /** + * Drops items emitted by an Observable that are followed by newer items + * before a timeout value expires. The timer resets on each emission. + *

    + * Note: If events keep firing faster than the timeout then no data will be + * emitted. + *

    + * + *

    + * Information on debounce vs throttle: + *

    + *

    + * + * @param timeout the time each value has to be "the most recent" of the + * {@link Observable} to ensure that it's not dropped + * @param unit the unit of time for the specified timeout + * @param scheduler the {@link Scheduler} to use internally to manage the + * timers that handle the timeout for each event + * @return an {@link Observable} that filters out items that are too + * quickly followed by newer items + * @see debounce() + * @see #throttleWithTimeout(long, TimeUnit, Scheduler) + */ + public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) { + return create(OperationDebounce.debounce(this, timeout, unit, scheduler)); + } + + /** + * Drops items emitted by an Observable that are followed by newer items + * before a timeout value expires. The timer resets on each emission. + *

    + * Note: If events keep firing faster than the timeout then no data will be + * emitted. + *

    + * + *

    + * Information on debounce vs throttle: + *

    + *

    + * + * @param timeout the time each value has to be "the most recent" of the + * {@link Observable} to ensure that it's not dropped + * @param unit the {@link TimeUnit} for the timeout + * @return an {@link Observable} that filters out items that are too + * quickly followed by newer items + * @see throttleWithTimeout() + * @see #debounce(long, TimeUnit) + */ + public Observable throttleWithTimeout(long timeout, TimeUnit unit) { + return create(OperationDebounce.debounce(this, timeout, unit)); + } + + /** + * Drops items emitted by an Observable that are followed by newer items + * before a timeout value expires. The timer resets on each emission. + *

    + * Note: If events keep firing faster than the timeout then no data will be + * emitted. + *

    + * + *

    + * Information on debounce vs throttle: + *

    + *

    + * + * @param timeout the time each value has to be "the most recent" of the + * {@link Observable} to ensure that it's not dropped + * @param unit the {@link TimeUnit} for the timeout + * @param scheduler the {@link Scheduler} to use internally to manage the + * timers that handle the timeout for each event + * @return an {@link Observable} that filters out items that are too + * quickly followed by newer items + * @see throttleWithTimeout() + * @see #debounce(long, TimeUnit, Scheduler) + */ + public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler scheduler) { + return create(OperationDebounce.debounce(this, timeout, unit, scheduler)); + } + + /** + * Throttles by skipping items until "skipDuration" passes and then emits + * the next received item. + *

    + * This differs from {@link #throttleLast} in that this only tracks passage + * of time whereas {@link #throttleLast} ticks at scheduled intervals. + *

    + * + * + * @param windowDuration time to wait before sending another item after + * emitting the last item + * @param unit the unit of time for the specified timeout + * @return an Observable that performs the throttle operation + * @see throttleFirst() + */ + public Observable throttleFirst(long windowDuration, TimeUnit unit) { + return create(OperationThrottleFirst.throttleFirst(this, windowDuration, unit)); + } + + /** + * Throttles by skipping items until "skipDuration" passes and then emits + * the next received item. + *

    + * This differs from {@link #throttleLast} in that this only tracks passage + * of time whereas {@link #throttleLast} ticks at scheduled intervals. + *

    + * + * + * @param skipDuration time to wait before sending another item after + * emitting the last item + * @param unit the unit of time for the specified timeout + * @param scheduler the {@link Scheduler} to use internally to manage the + * timers that handle timeout for each event + * @return an Observable that performs the throttle operation + * @see throttleFirst() + */ + public Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler scheduler) { + return create(OperationThrottleFirst.throttleFirst(this, skipDuration, unit, scheduler)); + } + + /** + * Throttles by emitting the last item in each interval defined by + * intervalDuration. + *

    + * This differs from {@link #throttleFirst} in that this ticks along at a + * scheduled interval whereas {@link #throttleFirst} does not tick, it just + * tracks passage of time. + *

    + * + * + * @param intervalDuration duration of windows within which the last item + * will be emitted + * @param unit the unit of time for the specified interval + * @return an Observable that performs the throttle operation + * @see throttleLast() + * @see #sample(long, TimeUnit) + */ + public Observable throttleLast(long intervalDuration, TimeUnit unit) { + return sample(intervalDuration, unit); + } + + /** + * Throttles by emitting the last item in each interval defined by + * intervalDuration. + *

    + * This differs from {@link #throttleFirst} in that this ticks along at a + * scheduled interval whereas {@link #throttleFirst} does not tick, it just + * tracks passage of time. + *

    + * + * + * @param intervalDuration duration of windows within which the last item + * will be emitted + * @param unit the unit of time for the specified interval + * @return an Observable that performs the throttle operation + * @see throttleLast() + * @see #sample(long, TimeUnit, Scheduler) + */ + public Observable throttleLast(long intervalDuration, TimeUnit unit, Scheduler scheduler) { + return sample(intervalDuration, unit, scheduler); + } + + /** + * Wraps each item emitted by a source Observable in a {@link Timestamped} + * object. + *

    + * + * + * @return an Observable that emits timestamped items from the source + * Observable + * @see timestamp() + */ + public Observable> timestamp() { + return create(OperationTimestamp.timestamp(this)); + } + + /** + * Converts a {@link Future} into an Observable. + *

    + * + *

    + * You can convert any object that supports the {@link Future} interface + * into an Observable that emits the return value of the {@link Future#get} + * method of that object, by passing the object into the {@code from} + * method. + *

    + * Important note: This Observable is blocking; you cannot + * unsubscribe from it. + * + * @param future the source {@link Future} + * @param the type of object that the {@link Future} returns, and also + * the type of item to be emitted by the resulting Observable + * @return an Observable that emits the item from the source Future + * @see from() + */ + public static Observable from(Future future) { + return create(OperationToObservableFuture.toObservableFuture(future)); + } + + /** + * Converts a {@link Future} into an Observable. + *

    + * + *

    + * You can convert any object that supports the {@link Future} interface + * into an Observable that emits the return value of the {@link Future#get} + * method of that object, by passing the object into the {@code from} + * method. + *

    + * + * @param future the source {@link Future} + * @param scheduler the {@link Scheduler} to wait for the Future on. Use a + * Scheduler such as {@link Schedulers#threadPoolForIO()} + * that can block and wait on the future. + * @param the type of object that the {@link Future} returns, and also + * the type of item to be emitted by the resulting Observable + * @return an Observable that emits the item from the source Future + * @see from() + */ + public static Observable from(Future future, Scheduler scheduler) { + return create(OperationToObservableFuture.toObservableFuture(future)).subscribeOn(scheduler); + } + + /** + * Converts a {@link Future} into an Observable with timeout. + *

    + * + *

    + * You can convert any object that supports the {@link Future} interface + * into an Observable that emits the return value of the {link Future#get} + * method of that object, by passing the object into the {@code from} + * method. + *

    + * Important note: This Observable is blocking; you cannot + * unsubscribe from it. + * + * @param future the source {@link Future} + * @param timeout the maximum time to wait before calling get() + * @param unit the {@link TimeUnit} of the timeout argument + * @param the type of object that the {@link Future} returns, and also + * the type of item to be emitted by the resulting Observable + * @return an Observable that emits the item from the source {@link Future} + * @see from() + */ + public static Observable from(Future future, long timeout, TimeUnit unit) { + return create(OperationToObservableFuture.toObservableFuture(future, timeout, unit)); + } + + /** + * Returns an Observable that emits Boolean values that indicate whether the + * pairs of items emitted by two source Observables are equal. + *

    + * + * + * @param first the first Observable to compare + * @param second the second Observable to compare + * @param the type of items emitted by each Observable + * @return an Observable that emits Booleans that indicate whether the + * corresponding items emitted by the source Observables are equal + * @see sequenceEqual() + */ + public static Observable sequenceEqual(Observable first, Observable second) { + return sequenceEqual(first, second, new Func2() { + @Override + public Boolean call(T first, T second) { + return first.equals(second); + } + }); + } + + /** + * Returns an Observable that emits Boolean values that indicate whether the + * pairs of items emitted by two source Observables are equal based on the + * results of a specified equality function. + *

    + * + * + * @param first the first Observable to compare + * @param second the second Observable to compare + * @param equality a function used to compare items emitted by both + * Observables + * @param the type of items emitted by each Observable + * @return an Observable that emits Booleans that indicate whether the + * corresponding items emitted by the source Observables are equal + * @see sequenceEqual() + */ + public static Observable sequenceEqual(Observable first, Observable second, Func2 equality) { + return zip(first, second, equality); + } + + /** + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of two items emitted, in sequence, by + * two other Observables. + *

    + * + *

    + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by {@code o1} and the first item emitted by + * {@code o2}; the second item emitted by the new Observable will be the + * result of the function applied to the second item emitted by {@code o1} + * and the second item emitted by {@code o2}; and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 another source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item that will + * be emitted by the resulting Observable + * @return an Observable that emits the zipped results + * @see zip() + */ + public static Observable zip(Observable o1, Observable o2, Func2 zipFunction) { + return create(OperationZip.zip(o1, o2, zipFunction)); + } + + /** + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of three items emitted, in sequence, by + * three other Observables. + *

    + * + *

    + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by {@code o1}, the first item emitted by + * {@code o2}, and the first item emitted by {@code o3}; the second item + * emitted by the new Observable will be the result of the function applied + * to the second item emitted by {@code o1}, the second item emitted by + * {@code o2}, and the second item emitted by {@code o3}; and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable + * @return an Observable that emits the zipped results + * @see zip() + */ + public static Observable zip(Observable o1, Observable o2, Observable o3, Func3 zipFunction) { + return create(OperationZip.zip(o1, o2, o3, zipFunction)); + } + + /** + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of four items emitted, in sequence, by + * four other Observables. + *

    + * + *

    + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by {@code o1}, the first item emitted by + * {@code o2}, the first item emitted by {@code o3}, and the first item + * emitted by {@code 04}; the second item emitted by the new Observable will + * be the result of the function applied to the second item emitted by each + * of those Observables; and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 one source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item that will + * be emitted by the resulting Observable + * @return an Observable that emits the zipped results + * @see zip() + */ + public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Func4 zipFunction) { + return create(OperationZip.zip(o1, o2, o3, o4, zipFunction)); + } + + /** + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of five items emitted, in sequence, by + * five other Observables. + *

    + * + *

    + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by {@code o1}, the first item emitted by + * {@code o2}, the first item emitted by {@code o3}, the first item emitted + * by {@code o4}, and the first item emitted by {@code o5}; the second item + * emitted by the new Observable will be the result of the function applied + * to the second item emitted by each of those Observables; and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param o5 a fifth source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable + * @return an Observable that emits the zipped results + * @see zip() + */ + public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 zipFunction) { + return create(OperationZip.zip(o1, o2, o3, o4, o5, zipFunction)); + } + + /** + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of six items emitted, in sequence, by + * six other Observables. + *

    + * + *

    + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted each source Observable, the second item emitted + * by the new Observable will be the result of the function applied to the + * second item emitted by each of those Observables, and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param o5 a fifth source Observable + * @param o6 a sixth source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable + * @return an Observable that emits the zipped results + * @see zip() + */ + public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, + Func6 zipFunction) { + return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, zipFunction)); + } + + /** + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of seven items emitted, in sequence, by + * seven other Observables. + *

    + * + *

    + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted each source Observable, the second item emitted + * by the new Observable will be the result of the function applied to the + * second item emitted by each of those Observables, and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param o5 a fifth source Observable + * @param o6 a sixth source Observable + * @param o7 a seventh source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable + * @return an Observable that emits the zipped results + * @see zip() + */ + public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, + Func7 zipFunction) { + return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, o7, zipFunction)); + } + + /** + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of eight items emitted, in sequence, by + * eight other Observables. + *

    + * + *

    + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted each source Observable, the second item emitted + * by the new Observable will be the result of the function applied to the + * second item emitted by each of those Observables, and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param o5 a fifth source Observable + * @param o6 a sixth source Observable + * @param o7 a seventh source Observable + * @param o8 an eighth source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable + * @return an Observable that emits the zipped results + * @see zip() + */ + public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, + Func8 zipFunction) { + return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, o7, o8, zipFunction)); + } + + /** + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of nine items emitted, in sequence, by + * nine other Observables. + *

    + * + *

    + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted each source Observable, the second item emitted + * by the new Observable will be the result of the function applied to the + * second item emitted by each of those Observables, and so forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@link Observer#onNext onNext} as many times as the number of + * {@code onNext} invocations of the source Observable that emits the fewest + * items. + * + * @param o1 the first source Observable + * @param o2 a second source Observable + * @param o3 a third source Observable + * @param o4 a fourth source Observable + * @param o5 a fifth source Observable + * @param o6 a sixth source Observable + * @param o7 a seventh source Observable + * @param o8 an eighth source Observable + * @param o9 a ninth source Observable + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable + * @return an Observable that emits the zipped results + * @see zip() + */ + public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, + Observable o9, Func9 zipFunction) { + return create(OperationZip.zip(o1, o2, o3, o4, o5, o6, o7, o8, o9, zipFunction)); + } + + /** + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function + * @see combineLatest() + */ + public static Observable combineLatest(Observable o1, Observable o2, Func2 combineFunction) { + return create(OperationCombineLatest.combineLatest(o1, o2, combineFunction)); + } + + /** + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function + * @see combineLatest() + */ + public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Func3 combineFunction) { + return create(OperationCombineLatest.combineLatest(o1, o2, o3, combineFunction)); + } + + /** + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function + * @see combineLatest() + */ + public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, + Func4 combineFunction) { + return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, combineFunction)); + } + + /** + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param o5 the fifth source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function + * @see combineLatest() + */ + public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, + Func5 combineFunction) { + return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, combineFunction)); + } + + /** + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param o5 the fifth source Observable + * @param o6 the sixth source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function + * @see combineLatest() + */ + public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, + Func6 combineFunction) { + return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, combineFunction)); + } + + /** + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param o5 the fifth source Observable + * @param o6 the sixth source Observable + * @param o7 the seventh source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function + * @see combineLatest() + */ + public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, + Func7 combineFunction) { + return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, o7, combineFunction)); + } + + /** + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param o5 the fifth source Observable + * @param o6 the sixth source Observable + * @param o7 the seventh source Observable + * @param o8 the eighth source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function + * @see combineLatest() + */ + public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, + Func8 combineFunction) { + return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, o7, o8, combineFunction)); + } + + /** + * Combines the given Observables, emitting an event containing an + * aggregation of the latest values of each of the source observables each + * time an event is received from one of the source observables, where the + * aggregation is defined by the given function. + *

    + * + * + * @param o1 the first source Observable + * @param o2 the second source Observable + * @param o3 the third source Observable + * @param o4 the fourth source Observable + * @param o5 the fifth source Observable + * @param o6 the sixth source Observable + * @param o7 the seventh source Observable + * @param o8 the eighth source Observable + * @param o9 the ninth source Observable + * @param combineFunction the aggregation function used to combine the + * source observable values + * @return an Observable that combines the source Observables with the + * given combine function + * @see combineLatest() + */ + public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9, + Func9 combineFunction) { + return create(OperationCombineLatest.combineLatest(o1, o2, o3, o4, o5, o6, o7, o8, o9, combineFunction)); + } + + /** + * Creates an Observable that produces buffers of collected items. + *

    + * + *

    + * This Observable produces connected, non-overlapping buffers. The current + * buffer is emitted and replaced with a new buffer when the Observable + * produced by the specified bufferClosingSelector produces a + * {@link rx.util.Closing} object. The bufferClosingSelector + * will then be used to create a new Observable to listen for the end of + * the next buffer. + * + * @param bufferClosingSelector the {@link Func0} which is used to produce + * an {@link Observable} for every buffer + * created. When this {@link Observable} + * produces a {@link rx.util.Closing} object, + * the associated buffer is emitted and + * replaced with a new one. + * @return an {@link Observable} which produces connected, non-overlapping + * buffers, which are emitted when the current {@link Observable} + * created with the {@link Func0} argument produces a + * {@link rx.util.Closing} object + * @see buffer() + */ + public Observable> buffer(Func0> bufferClosingSelector) { + return create(OperationBuffer.buffer(this, bufferClosingSelector)); + } + + /** + * Creates an Observable which produces buffers of collected values. + *

    + * + *

    + * This Observable produces buffers. Buffers are created when the specified + * bufferOpenings Observable produces a {@link rx.util.Opening} + * object. Additionally the bufferClosingSelector argument is + * used to create an Observable which produces {@link rx.util.Closing} + * objects. When this Observable produces such an object, the associated + * buffer is emitted. + * + * @param bufferOpenings the {@link Observable} that, when it produces a + * {@link rx.util.Opening} object, will cause another + * buffer to be created + * @param bufferClosingSelector the {@link Func1} that is used to produce + * an {@link Observable} for every buffer + * created. When this {@link Observable} + * produces a {@link rx.util.Closing} object, + * the associated buffer is emitted. + * @return an {@link Observable} that produces buffers that are created and + * emitted when the specified {@link Observable}s publish certain + * objects + * @see buffer() + */ + public Observable> buffer(Observable bufferOpenings, Func1> bufferClosingSelector) { + return create(OperationBuffer.buffer(this, bufferOpenings, bufferClosingSelector)); + } + + /** + * Creates an Observable that produces buffers of collected items. + *

    + * + *

    + * This Observable produces connected, non-overlapping buffers, each + * containing count items. When the source Observable completes + * or encounters an error, the current buffer is emitted, and the event is + * propagated. + * + * @param count the maximum size of each buffer before it should be emitted + * @return an {@link Observable} that produces connected, non-overlapping + * buffers containing at most "count" items + * @see buffer() + */ + public Observable> buffer(int count) { + return create(OperationBuffer.buffer(this, count)); + } + + /** + * Creates an Observable which produces buffers of collected items. + *

    + * + *

    + * This Observable produces buffers every skip items, each + * containing count items. When the source Observable + * completes or encounters an error, the current buffer is emitted, and the + * event is propagated. + * + * @param count the maximum size of each buffer before it should be emitted + * @param skip how many produced items need to be skipped before starting a + * new buffer. Note that when skip and + * count are equal, this is the same operation as + * {@link Observable#buffer(int)}. + * @return an {@link Observable} that produces buffers every + * skip item containing at most count + * items + * @see buffer() + */ + public Observable> buffer(int count, int skip) { + return create(OperationBuffer.buffer(this, count, skip)); + } + + /** + * Creates an Observable that produces buffers of collected values. + *

    + * + *

    + * This Observable produces connected, non-overlapping buffers, each of a + * fixed duration specified by the timespan argument. When the + * source Observable completes or encounters an error, the current buffer is + * emitted and the event is propagated. + * + * @param timespan the period of time each buffer collects values before it + * should be emitted and replaced with a new buffer + * @param unit the unit of time which applies to the timespan + * argument + * @return an {@link Observable} that produces connected, non-overlapping + * buffers with a fixed duration + * @see buffer() + */ + public Observable> buffer(long timespan, TimeUnit unit) { + return create(OperationBuffer.buffer(this, timespan, unit)); + } + + /** + * Creates an Observable that produces buffers of collected values. + *

    + * + *

    + * This Observable produces connected, non-overlapping buffers, each of a + * fixed duration specified by the timespan argument. When the + * source Observable completes or encounters an error, the current buffer is + * emitted and the event is propagated. + * + * @param timespan the period of time each buffer collects values before it + * should be emitted and replaced with a new buffer + * @param unit the unit of time which applies to the timespan + * argument + * @param scheduler the {@link Scheduler} to use when determining the end + * and start of a buffer + * @return an {@link Observable} that produces connected, non-overlapping + * buffers with a fixed duration + * @see buffer() + */ + public Observable> buffer(long timespan, TimeUnit unit, Scheduler scheduler) { + return create(OperationBuffer.buffer(this, timespan, unit, scheduler)); + } + + /** + * Creates an Observable that produces buffers of collected items. This + * Observable produces connected, non-overlapping buffers, each of a fixed + * duration specified by the timespan argument or a maximum + * size specified by the count argument (whichever is reached + * first). When the source Observable completes or encounters an error, the + * current buffer is emitted and the event is propagated. + *

    + * + * + * @param timespan the period of time each buffer collects values before it + * should be emitted and replaced with a new buffer + * @param unit the unit of time which applies to the timespan + * argument + * @param count the maximum size of each buffer before it should be emitted + * @return an {@link Observable} that produces connected, non-overlapping + * buffers that are emitted after a fixed duration or when the + * buffer reaches maximum capacity (whichever occurs first) + * @see buffer() + */ + public Observable> buffer(long timespan, TimeUnit unit, int count) { + return create(OperationBuffer.buffer(this, timespan, unit, count)); + } + + /** + * Creates an Observable that produces buffers of collected items. This + * Observable produces connected, non-overlapping buffers, each of a fixed + * duration specified by the timespan argument or a maximum + * size specified by the count argument (whichever is reached + * first). When the source Observable completes or encounters an error, the + * current buffer is emitted and the event is propagated. + *

    + * + * + * @param timespan the period of time each buffer collects values before it + * should be emitted and replaced with a new buffer + * @param unit the unit of time which applies to the timespan + * argument + * @param count the maximum size of each buffer before it should be emitted + * @param scheduler the {@link Scheduler} to use when determining the end + and start of a buffer + * @return an {@link Observable} that produces connected, non-overlapping + * buffers that are emitted after a fixed duration or when the + * buffer has reached maximum capacity (whichever occurs first) + * @see buffer() + */ + public Observable> buffer(long timespan, TimeUnit unit, int count, Scheduler scheduler) { + return create(OperationBuffer.buffer(this, timespan, unit, count, scheduler)); + } + + /** + * Creates an Observable that produces buffers of collected items. This + * Observable starts a new buffer periodically, as determined by the + * timeshift argument. Each buffer is emitted after a fixed + * timespan, specified by the timespan argument. When the + * source Observable completes or encounters an error, the current buffer is + * emitted and the event is propagated. + *

    + * + * + * @param timespan the period of time each buffer collects values before it + * should be emitted + * @param timeshift the period of time after which a new buffer will be + * created + * @param unit the unit of time that applies to the timespan + * and timeshift arguments + * @return an {@link Observable} that produces new buffers periodically and + * emits these after a fixed timespan has elapsed. + * @see buffer() + */ + public Observable> buffer(long timespan, long timeshift, TimeUnit unit) { + return create(OperationBuffer.buffer(this, timespan, timeshift, unit)); + } + + /** + * Creates an Observable that produces buffers of collected items. This + * Observable starts a new buffer periodically, as determined by the + * timeshift argument. Each buffer is emitted after a fixed + * timespan, specified by the timespan argument. When the + * source Observable completes or encounters an error, the current buffer is + * emitted and the event is propagated. + *

    + * + * + * @param timespan the period of time each buffer collects values before it + * should be emitted + * @param timeshift the period of time after which a new buffer will be + * created + * @param unit the unit of time that applies to the timespan + * and timeshift arguments + * @param scheduler the {@link Scheduler} to use when determining the end + * and start of a buffer + * @return an {@link Observable} that produces new buffers periodically and + * emits these after a fixed timespan has elapsed + * @see buffer() + */ + public Observable> buffer(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { + return create(OperationBuffer.buffer(this, timespan, timeshift, unit, scheduler)); + } + + /** + * Creates an Observable that produces windows of collected items. This + * Observable produces connected, non-overlapping windows. The current + * window is emitted and replaced with a new window when the Observable + * produced by the specified closingSelector produces a + * {@link rx.util.Closing} object. The closingSelector will + * then be used to create a new Observable to listen for the end of the next + * window. + *

    + * + * + * @param closingSelector the {@link Func0} used to produce an + * {@link Observable} for every window created. When this + * {@link Observable} emits a {@link rx.util.Closing} object, the + * associated window is emitted and replaced with a new one. + * @return an {@link Observable} that produces connected, non-overlapping + * windows, which are emitted when the current {@link Observable} + * created with the closingSelector argument emits a + * {@link rx.util.Closing} object. + * @see window() + */ + public Observable> window(Func0> closingSelector) { + return create(OperationWindow.window(this, closingSelector)); + } + + /** + * Creates an Observable that produces windows of collected items. This + * Observable produces windows. Chunks are created when the + * windowOpenings Observable produces a {@link rx.util.Opening} + * object. Additionally the closingSelector argument creates an + * Observable that produces {@link rx.util.Closing} objects. When this + * Observable produces such an object, the associated window is emitted. + *

    + * + * + * @param windowOpenings the {@link Observable} that, when it produces a + * {@link rx.util.Opening} object, causes another + * window to be created + * @param closingSelector the {@link Func1} that produces an + * {@link Observable} for every window created. When + * this {@link Observable} produces a + * {@link rx.util.Closing} object, the associated + * window is emitted. + * @return an {@link Observable} that produces windows that are created and + * emitted when the specified {@link Observable}s publish certain + * objects + * @see window() + */ + public Observable> window(Observable windowOpenings, Func1> closingSelector) { + return create(OperationWindow.window(this, windowOpenings, closingSelector)); + } + + /** + * Creates an Observable that produces windows of collected items. This + * Observable produces connected, non-overlapping windows, each containing + * count elements. When the source Observable completes or + * encounters an error, the current window is emitted, and the event is + * propagated. + *

    + * + * + * @param count the maximum size of each window before it should be emitted + * @return an {@link Observable} that produces connected, non-overlapping + * windows containing at most count items + * @see window() + */ + public Observable> window(int count) { + return create(OperationWindow.window(this, count)); + } + + /** + * Creates an Observable that produces windows of collected items. This + * Observable produces windows every skip items, each + * containing count elements. When the source Observable + * completes or encounters an error, the current window is emitted and the + * event is propagated. + *

    + * + * + * @param count the maximum size of each window before it should be emitted + * @param skip how many items need to be skipped before starting a new + * window. Note that if skip and count + * are equal this is the same operation as {@link #window(int)}. + * @return an {@link Observable} that produces windows every "skipped" + * items containing at most count items + * @see window() + */ + public Observable> window(int count, int skip) { + return create(OperationWindow.window(this, count, skip)); + } + + /** + * Creates an Observable that produces windows of collected items. This + * Observable produces connected, non-overlapping windows, each of a fixed + * duration specified by the timespan argument. When the source + * Observable completes or encounters an error, the current window is + * emitted and the event is propagated. + *

    + * + * + * @param timespan the period of time each window collects items before it + * should be emitted and replaced with a new window + * @param unit the unit of time that applies to the timespan + * argument + * @return an {@link Observable} that produces connected, non-overlapping + * windows with a fixed duration + * @see window() + */ + public Observable> window(long timespan, TimeUnit unit) { + return create(OperationWindow.window(this, timespan, unit)); + } + + /** + * Creates an Observable that produces windows of collected items. This + * Observable produces connected, non-overlapping windows, each of a fixed + * duration as specified by the timespan argument. When the + * source Observable completes or encounters an error, the current window is + * emitted and the event is propagated. + *

    + * + * + * @param timespan the period of time each window collects items before it + * should be emitted and replaced with a new window + * @param unit the unit of time which applies to the timespan + * argument + * @param scheduler the {@link Scheduler} to use when determining the end + * and start of a window + * @return an {@link Observable} that produces connected, non-overlapping + * windows with a fixed duration + * @see window() + */ + public Observable> window(long timespan, TimeUnit unit, Scheduler scheduler) { + return create(OperationWindow.window(this, timespan, unit, scheduler)); + } + + /** + * Creates an Observable that produces windows of collected items. This + * Observable produces connected non-overlapping windows, each of a fixed + * duration as specified by the timespan argument or a maximum + * size as specified by the count argument (whichever is + * reached first). When the source Observable completes or encounters an + * error, the current window is emitted and the event is propagated. + *

    + * + * + * @param timespan the period of time each window collects values before it + * should be emitted and replaced with a new window + * @param unit the unit of time that applies to the timespan + * argument + * @param count the maximum size of each window before it should be emitted + * @return an {@link Observable} that produces connected, non-overlapping + * windows that are emitted after a fixed duration or when the + * window has reached maximum capacity (whichever occurs first) + * @see window() + */ + public Observable> window(long timespan, TimeUnit unit, int count) { + return create(OperationWindow.window(this, timespan, unit, count)); + } + + /** + * Creates an Observable that produces windows of collected items. This + * Observable produces connected, non-overlapping windows, each of a fixed + * duration specified by the timespan argument or a maximum + * size specified by the count argument (whichever is reached + * first). When the source Observable completes or encounters an error, the + * current window is emitted and the event is propagated. + *

    + * + * + * @param timespan the period of time each window collects values before it + * should be emitted and replaced with a new window + * @param unit the unit of time which applies to the timespan + * argument + * @param count the maximum size of each window before it should be emitted + * @param scheduler the {@link Scheduler} to use when determining the end + * and start of a window. + * @return an {@link Observable} that produces connected non-overlapping + * windows that are emitted after a fixed duration or when the + * window has reached maximum capacity (whichever occurs first). + * @see window() + */ + public Observable> window(long timespan, TimeUnit unit, int count, Scheduler scheduler) { + return create(OperationWindow.window(this, timespan, unit, count, scheduler)); + } + + /** + * Creates an Observable that produces windows of collected items. This + * Observable starts a new window periodically, as determined by the + * timeshift argument. Each window is emitted after a fixed + * timespan, specified by the timespan argument. When the + * source Observable completes or encounters an error, the current window is + * emitted and the event is propagated. + *

    + * + * + * @param timespan the period of time each window collects values before it + * should be emitted + * @param timeshift the period of time after which a new window will be + * created + * @param unit the unit of time that applies to the timespan + * and timeshift arguments + * @return an {@link Observable} that produces new windows periodically and + * emits these after a fixed timespan has elapsed + * @see window() + */ + public Observable> window(long timespan, long timeshift, TimeUnit unit) { + return create(OperationWindow.window(this, timespan, timeshift, unit)); + } + + /** + * Creates an Observable that produces windows of collected items. This + * Observable starts a new window periodically, as determined by the + * timeshift argument. Each window is emitted after a fixed + * timespan, specified by the timespan argument. When the + * source Observable completes or encounters an error, the current window is + * emitted and the event is propagated. + *

    + * + * + * @param timespan the period of time each window collects values before it + * should be emitted + * @param timeshift the period of time after which a new window will be + * created + * @param unit the unit of time that applies to the timespan + * and timeshift arguments + * @param scheduler the {@link Scheduler} to use when determining the end + * and start of a window + * @return an {@link Observable} that produces new windows periodically and + * emits these after a fixed timespan has elapsed + * @see window() + */ + public Observable> window(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { + return create(OperationWindow.window(this, timespan, timeshift, unit, scheduler)); + } + + /** + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations of n items emitted, in sequence, + * by n other Observables as provided by an Iterable. + *

    + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by all of the source Observables; the second + * item emitted by the new Observable will be the result of the function + * applied to the second item emitted by each of those Observables; and so + * forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will + * invoke {@code onNext} as many times as the number of {@code onNext} + * invokations of the source Observable that emits the fewest items. + *

    + * + * + * @param ws an Observable of source Observables + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable + * @return an Observable that emits the zipped results + * @see zip() + */ + public static Observable zip(Observable> ws, final FuncN zipFunction) { + return ws.toList().mapMany(new Func1>, Observable>() { + @Override + public Observable call(List> wsList) { + return create(OperationZip.zip(wsList, zipFunction)); + } + }); + } + + /** + * Returns an Observable that emits the results of a function of your + * choosing applied to combinations items emitted, in sequence, by a + * collection of other Observables. + *

    + * {@code zip} applies this function in strict sequence, so the first item + * emitted by the new Observable will be the result of the function applied + * to the first item emitted by all of the source Observables; the second + * item emitted by the new Observable will be the result of the function + * applied to the second item emitted by each of those Observables; and so + * forth. + *

    + * The resulting {@code Observable} returned from {@code zip} will invoke + * {@code onNext} as many times as the number of {@code onNext} invokations + * of the source Observable that emits the fewest items. + *

    + * + * + * @param ws a collection of source Observables + * @param zipFunction a function that, when applied to an item emitted by + * each of the source Observables, results in an item + * that will be emitted by the resulting Observable + * @return an Observable that emits the zipped results + * @see zip() + */ + public static Observable zip(Iterable> ws, FuncN zipFunction) { + return create(OperationZip.zip(ws, zipFunction)); + } + + /** + * Filter items emitted by an Observable. + *

    + * + * + * @param predicate a function that evaluates the items emitted by the + * source Observable, returning {@code true} if they pass + * the filter + * @return an Observable that emits only those items in the original + * Observable that the filter evaluates as {@code true} + * @see filter() + */ + public Observable filter(Func1 predicate) { + return create(OperationFilter.filter(this, predicate)); + } + + /** + * Returns an Observable that forwards all sequentially distinct items + * emitted from the source Observable. + *

    + * + * + * @return an Observable of sequentially distinct items + * @see distinctUntilChanged() + * @see MSDN: Observable.distinctUntilChanged + */ + public Observable distinctUntilChanged() { + return create(OperationDistinctUntilChanged.distinctUntilChanged(this)); + } + + /** + * Returns an Observable that forwards all items emitted from the source + * Observable that are sequentially distinct according to a key selector + * function. + *

    + * + * + * @param keySelector a function that projects an emitted item to a key + * value that is used for deciding whether an item is + * sequentially distinct from another one or not + * @return an Observable of sequentially distinct items + * @see distinctUntilChanged() + * @see MSDN: Observable.distinctUntilChanged + */ + public Observable distinctUntilChanged(Func1 keySelector) { + return create(OperationDistinctUntilChanged.distinctUntilChanged(this, keySelector)); + } + + /** + * Returns an Observable that emits all distinct items emitted from the + * source Observable. + *

    + * + * + * @return an Observable of distinct items + * @see distinct() + * @see MSDN: Observable.distinct + */ + public Observable distinct() { + return create(OperationDistinct.distinct(this)); + } + + /** + * Returns an Observable that emits all items emitted from the source + * Observable that are distinct according to a key selector function. + *

    + * + * + * @param keySelector a function that projects an emitted item to a key + * value that is used to decide whether an item is + * distinct from another one or not + * @return an Observable that emits distinct items + * @see distinct() + * @see MSDN: Observable.distinct + */ + public Observable distinct(Func1 keySelector) { + return create(OperationDistinct.distinct(this, keySelector)); + } + + /** + * Returns the item at a specified index in a sequence. + *

    + * + * + * @param index the zero-based index of the item to retrieve + * @return an Observable that emits the item at the specified position in + * the source sequence + * @throws IndexOutOfBoundsException if index is greater than + * or equal to the number of elements in + * the source sequence + * @throws IndexOutOfBoundsException if index is less than 0 + * @see elementAt() + */ + public Observable elementAt(int index) { + return create(OperationElementAt.elementAt(this, index)); + } + + /** + * Returns the item at a specified index in a sequence or the default item + * if the index is out of range. + *

    + * + * + * @param index the zero-based index of the item to retrieve + * @param defaultValue the default item + * @return an Observable that emits the item at the specified position in + * the source sequence, or the default item if the index is outside + * the bounds of the source sequence + * @throws IndexOutOfBoundsException if index is less than 0 + * @see elementAtOrDefault() + */ + public Observable elementAtOrDefault(int index, T defaultValue) { + return create(OperationElementAt.elementAtOrDefault(this, index, defaultValue)); + } + + /** + * Returns an {@link Observable} that emits true if any element + * of the source {@link Observable} satisfies the given condition, otherwise + * false. Note: always emits false if the source + * {@link Observable} is empty. + *

    + * In Rx.Net this is the any operator but renamed in RxJava to + * better match Java naming idioms. + *

    + * + * + * @param predicate the condition to test every element + * @return a subscription function for creating the target Observable + * @see exists() + * @see MSDN: Observable.Any Note: the description in this page is wrong. + */ + public Observable exists(Func1 predicate) { + return create(OperationAny.exists(this, predicate)); + } + + /** + * Determines whether an Observable sequence contains a specified item. + *

    + * + * + * @param element the item to search in the sequence + * @return an Observable that emits true if the item is in the + * source sequence + * @see contains() + * @see MSDN: Observable.Contains + */ + public Observable contains(final T element) { + return exists(new Func1() { + public Boolean call(T t1) { + return element == null ? t1 == null : element.equals(t1); + } + }); + } + + /** + * Registers an {@link Action0} to be called when this Observable invokes + * {@link Observer#onCompleted onCompleted} or + * {@link Observer#onError onError}. + *

    + * + * + * @param action an {@link Action0} to be invoked when the source + * Observable finishes + * @return an Observable that emits the same items as the source Observable, + * then invokes the {@link Action0} + * @see finallyDo() + * @see MSDN: Observable.Finally Method + */ + public Observable finallyDo(Action0 action) { + return create(OperationFinally.finallyDo(this, action)); + } + + /** + * Creates a new Observable by applying a function that you supply to each + * item emitted by the source Observable, where that function returns an + * Observable, and then merging those resulting Observables and emitting the + * results of this merger. + *

    + * + *

    + * Note: {@code mapMany} and {@code flatMap} are equivalent. + * + * @param func a function that, when applied to an item emitted by the + * source Observable, returns an Observable + * @return an Observable that emits the result of applying the + * transformation function to each item emitted by the source + * Observable and merging the results of the Observables obtained + * from this transformation. + * @see flatMap() + * @see #mapMany(Func1) + */ + public Observable flatMap(Func1> func) { + return mapMany(func); + } + + /** + * Filter items emitted by an Observable. + *

    + * + * + * @param predicate a function that evaluates an item emitted by the source + * Observable, returning {@code true} if it passes the + * filter + * @return an Observable that emits only those items emitted by the original + * Observable that the filter evaluates as {@code true} + * @see where() + * @see #filter(Func1) + */ + public Observable where(Func1 predicate) { + return filter(predicate); + } + + /** + * Returns an Observable that applies the given function to each item + * emitted by an Observable and emits the result. + *

    + * + * + * @param func a function to apply to each item emitted by the Observable + * @return an Observable that emits the items from the source Observable, + * transformed by the given function + * @see map() + * @see MSDN: Observable.Select + */ + public Observable map(Func1 func) { + return create(OperationMap.map(this, func)); + } + + /** + * Returns an Observable that applies the given function to each item + * emitted by an Observable and emits the result. + *

    + * + * + * @param func a function to apply to each item emitted by the Observable. + * The function takes the index of the emitted item as + * additional parameter. + * @return an Observable that emits the items from the source Observable, + * transformed by the given function + * @see mapWithIndex() + * @see MSDN: Observable.Select + */ + public Observable mapWithIndex(Func2 func) { + return create(OperationMap.mapWithIndex(this, func)); + } + + /** + * Creates a new Observable by applying a function that you supply to each + * item emitted by the source Observable, where that function returns an + * Observable, and then merging those resulting Observables and emitting + * the results of this merger. + *

    + * + *

    + * Note: mapMany and flatMap are equivalent. + * + * @param func a function that, when applied to an item emitted by the + * source Observable, returns an Observable + * @return an Observable that emits the result of applying the + * transformation function to each item emitted by the source + * Observable and merging the results of the Observables obtained + * from this transformation. + * @see mapMany() + * @see #flatMap(Func1) + */ + public Observable mapMany(Func1> func) { + return create(OperationMap.mapMany(this, func)); + } + + /** + * Turns all of the notifications from a source Observable into + * {@link Observer#onNext onNext} emissions, and marks them with their + * original notification types within {@link Notification} objects. + *

    + * + * + * @return an Observable whose items are the result of materializing the + * items and notifications of the source Observable + * @see materialize() + * @see MSDN: Observable.materialize + */ + public Observable> materialize() { + return create(OperationMaterialize.materialize(this)); + } + + /** + * Asynchronously subscribes and unsubscribes Observers on the specified + * {@link Scheduler}. + *

    + * + * + * @param scheduler the {@link Scheduler} to perform subscription and + * unsubscription actions on + * @return the source Observable modified so that its subscriptions and + * unsubscriptions happen on the specified {@link Scheduler} + * @see subscribeOn() + */ + public Observable subscribeOn(Scheduler scheduler) { + return create(OperationSubscribeOn.subscribeOn(this, scheduler)); + } + + /** + * Asynchronously notify {@link Observer}s on the specified + * {@link Scheduler}. + *

    + * + * + * @param scheduler the {@link Scheduler} to notify {@link Observer}s on + * @return the source Observable modified so that its {@link Observer}s are + * notified on the specified {@link Scheduler} + * @see observeOn() + */ + public Observable observeOn(Scheduler scheduler) { + return create(OperationObserveOn.observeOn(this, scheduler)); + } + + /** + * Returns an Observable that reverses the effect of + * {@link #materialize materialize} by transforming the {@link Notification} + * objects emitted by the source Observable into the items or notifications + * they represent. + *

    + * + * + * @return an Observable that emits the items and notifications embedded in + * the {@link Notification} objects emitted by the source Observable + * @throws Throwable if the source Observable is not of type + * {@code Observable>} + * @see dematerialize() + * @see MSDN: Observable.dematerialize + */ + @SuppressWarnings("unchecked") + public Observable dematerialize() { + return create(OperationDematerialize.dematerialize((Observable>) this)); + } + + /** + * Instruct an Observable to pass control to another Observable rather than + * invoking {@link Observer#onError onError} if it encounters an error. + *

    + * + *

    + * By default, when an Observable encounters an error that prevents it from + * emitting the expected item to its {@link Observer}, the Observable + * invokes its Observer's onError method, and then quits + * without invoking any more of its Observer's methods. The + * onErrorResumeNext method changes this behavior. If you pass + * a function that returns an Observable (resumeFunction) to + * onErrorResumeNext, if the original Observable encounters an + * error, instead of invoking its Observer's onError method, it + * will instead relinquish control to the Observable returned from + * resumeFunction, which will invoke the Observer's + * {@link Observer#onNext onNext} method if it is able to do so. In such a + * case, because no Observable necessarily invokes onError, the + * Observer may never know that an error happened. + *

    + * You can use this to prevent errors from propagating or to supply fallback + * data should errors be encountered. + * + * @param resumeFunction a function that returns an Observable that will + * take over if the source Observable encounters an + * error + * @return the original Observable, with appropriately modified behavior + * @see onErrorResumeNext() + */ + public Observable onErrorResumeNext(final Func1> resumeFunction) { + return create(OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction(this, resumeFunction)); + } + + /** + * Instruct an Observable to pass control to another Observable rather than + * invoking {@link Observer#onError onError} if it encounters an error. + *

    + * + *

    + * By default, when an Observable encounters an error that prevents it from + * emitting the expected item to its {@link Observer}, the Observable + * invokes its Observer's onError method, and then quits + * without invoking any more of its Observer's methods. The + * onErrorResumeNext method changes this behavior. If you pass + * another Observable (resumeSequence) to an Observable's + * onErrorResumeNext method, if the original Observable + * encounters an error, instead of invoking its Observer's + * onError method, it will instead relinquish control to + * resumeSequence which will invoke the Observer's + * {@link Observer#onNext onNext} method if it is able to do so. In such a + * case, because no Observable necessarily invokes onError, the + * Observer may never know that an error happened. + *

    + * You can use this to prevent errors from propagating or to supply fallback + * data should errors be encountered. + * + * @param resumeSequence a function that returns an Observable that will + * take over if the source Observable encounters an + * error + * @return the original Observable, with appropriately modified behavior + * @see onErrorResumeNext() + */ + public Observable onErrorResumeNext(final Observable resumeSequence) { + return create(OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable(this, resumeSequence)); + } + + /** + * Instruct an Observable to pass control to another Observable rather than + * invoking {@link Observer#onError onError} if it encounters an error of + * type {@link java.lang.Exception}. + *

    + * This differs from {@link #onErrorResumeNext} in that this one does not + * handle {@link java.lang.Throwable} or {@link java.lang.Error} but lets + * those continue through. + *

    + * + *

    + * By default, when an Observable encounters an error that prevents it from + * emitting the expected item to its {@link Observer}, the Observable + * invokes its Observer's onError method, and then quits + * without invoking any more of its Observer's methods. The + * onErrorResumeNext method changes this behavior. If you pass + * another Observable (resumeSequence) to an Observable's + * onErrorResumeNext method, if the original Observable + * encounters an error, instead of invoking its Observer's + * onError method, it will instead relinquish control to + * resumeSequence which will invoke the Observer's + * {@link Observer#onNext onNext} method if it is able to do so. In such a + * case, because no Observable necessarily invokes onError, + * the Observer may never know that an error happened. + *

    + * You can use this to prevent errors from propagating or to supply fallback + * data should errors be encountered. + * + * @param resumeSequence a function that returns an Observable that will + * take over if the source Observable encounters an + * error + * @return the original Observable, with appropriately modified behavior + * @see onExceptionResumeNextViaObservable() + */ + public Observable onExceptionResumeNext(final Observable resumeSequence) { + return create(OperationOnExceptionResumeNextViaObservable.onExceptionResumeNextViaObservable(this, resumeSequence)); + } + + /** + * Instruct an Observable to emit an item (returned by a specified function) + * rather than invoking {@link Observer#onError onError} if it encounters an + * error. + *

    + * + *

    + * By default, when an Observable encounters an error that prevents it from + * emitting the expected item to its {@link Observer}, the Observable + * invokes its Observer's onError method, and then quits + * without invoking any more of its Observer's methods. The + * onErrorReturn method changes this behavior. If you pass a + * function (resumeFunction) to an Observable's + * onErrorReturn method, if the original Observable encounters + * an error, instead of invoking its Observer's onError method, + * it will instead pass the return value of resumeFunction to + * the Observer's {@link Observer#onNext onNext} method. + *

    + * You can use this to prevent errors from propagating or to supply fallback + * data should errors be encountered. + * + * @param resumeFunction a function that returns an item that the new + * Observable will emit if the source Observable + * encounters an error + * @return the original Observable with appropriately modified behavior + * @see onErrorReturn() + */ + public Observable onErrorReturn(Func1 resumeFunction) { + return create(OperationOnErrorReturn.onErrorReturn(this, resumeFunction)); + } + + /** + * Returns an Observable that applies a function of your choosing to the + * first item emitted by a source Observable, then feeds the result of that + * function along with the second item emitted by the source Observable into + * the same function, and so on until all items have been emitted by the + * source Observable, and emits the final result from the final call to your + * function as its sole item. + *

    + * + *

    + * This technique, which is called "reduce" or "aggregate" here, is + * sometimes called "fold," "accumulate," "compress," or "inject" in other + * programming contexts. Groovy, for instance, has an inject + * method that does a similar operation on lists. + * + * @param accumulator an accumulator function to be invoked on each item + * emitted by the source Observable, whose result will + * be used in the next accumulator call + * @return an Observable that emits a single item that is the result of + * accumulating the output from the source Observable + * @throws IllegalArgumentException if the Observable sequence is empty + * @see reduce() + * @see MSDN: Observable.Aggregate + * @see Wikipedia: Fold (higher-order function) + */ + public Observable reduce(Func2 accumulator) { + /* + * Discussion and confirmation of implementation at https://github.com/Netflix/RxJava/issues/423#issuecomment-27642532 + * + * It should use last() not takeLast(1) since it needs to emit an error if the sequence is empty. + */ + return create(OperationScan.scan(this, accumulator)).last(); + } + + /** + * Returns an Observable that counts the total number of items in the + * source Observable. + *

    + * + * + * @return an Observable that emits the number of counted elements of the + * source Observable as its single item + * @see count() + * @see MSDN: Observable.Count + */ + public Observable count() { + return reduce(0, new Func2() { + @Override + public Integer call(Integer t1, T t2) { + return t1 + 1; + } + }); + } + + /** + * Returns an Observable that sums up the integers emitted by the source + * Observable. + *

    + * + * + * @param source source Observable to compute the sum of + * @return an Observable that emits the sum of all the items of the + * source Observable as its single item + * @see sum() + * @see MSDN: Observable.Sum + */ + public static Observable sum(Observable source) { + return OperationSum.sum(source); + } + + /** + * Returns an Observable that sums up the longs emitted by the source + * Observable. + *

    + * + * + * @param source source Observable to compute the sum of + * @return an Observable that emits the sum of all the items of the + * source Observable as its single item + * @see sumLongs() + * @see MSDN: Observable.Sum + */ + public static Observable sumLongs(Observable source) { + return OperationSum.sumLongs(source); + } + + /** + * Returns an Observable that sums up the floats emitted by the source + * Observable. + *

    + * + * + * @param source source Observable to compute the sum of + * @return an Observable that emits the sum of all the items of the + * source Observable as its single item + * @see sumFloats() + * @see MSDN: Observable.Sum + */ + public static Observable sumFloats(Observable source) { + return OperationSum.sumFloats(source); + } + + /** + * Returns an Observable that sums up the doubles emitted by the source + * Observable. + *

    + * + * + * @param source source Observable to compute the sum of + * @return an Observable that emits the sum of all the items of the + * source Observable as its single item + * @see sumDoubles() + * @see MSDN: Observable.Sum + */ + public static Observable sumDoubles(Observable source) { + return OperationSum.sumDoubles(source); + } + + /** + * Returns an Observable that computes the average of the integers emitted + * by the source Observable. + *

    + * + * + * @param source source observable to compute the average of + * @return an Observable that emits the average of all the items emitted by + * the source Observable as its single item + * @throws IllegalArgumentException if the Observable sequence is empty + * @see average() + * @see MSDN: Observable.Average + */ + public static Observable average(Observable source) { + return OperationAverage.average(source); + } + + /** + * Returns an Observable that computes the average of the longs emitted by + * the source Observable. + *

    + * + * + * @param source source observable to compute the average of + * @return an Observable that emits the average of all the items emitted by + * the source Observable as its single item + * @see averageLongs() + * @see MSDN: Observable.Average + */ + public static Observable averageLongs(Observable source) { + return OperationAverage.averageLongs(source); + } + + /** + * Returns an Observable that computes the average of the floats emitted by + * the source Observable. + *

    + * + * + * @param source source observable to compute the average of + * @return an Observable that emits the average of all the items emitted by + * the source Observable as its single item + * @see averageFloats() + * @see MSDN: Observable.Average + */ + public static Observable averageFloats(Observable source) { + return OperationAverage.averageFloats(source); + } + + /** + * Returns an Observable that computes the average of the doubles emitted + * by the source Observable. + *

    + * + * + * @param source source observable to compute the average of + * @return an Observable that emits the average of all the items emitted by + * the source Observable as its single item + * @see averageDoubles() + * @see MSDN: Observable.Average + */ + public static Observable averageDoubles(Observable source) { + return OperationAverage.averageDoubles(source); + } + + /** + * Returns the minimum item emitted by an Observable. If there are more than + * one minimum items, its returns the last one. + *

    + * + * + * @param source an Observable sequence to determine the minimum item of + * @return an Observable that emits the minimum item + * @throws IllegalArgumentException if the source is empty + * @see MSDN: Observable.Min + */ + public static > Observable min(Observable source) { + return OperationMinMax.min(source); + } + + /** + * Returns the minimum item emitted by an Observable according to a + * specified comparator. If there are more than one minimum items, it + * returns the last one. + *

    + * + * + * @param comparator the comparer used to compare elements + * @return an Observable that emits the minimum value according to the + * specified comparator + * @throws IllegalArgumentException if the source is empty + * @see min() + * @see MSDN: Observable.Min + */ + public Observable min(Comparator comparator) { + return OperationMinMax.min(this, comparator); + } + + /** + * Returns the items emitted by an Observable sequence with the minimum key + * value. For an empty source, returns an Observable that emits an empty + * List. + *

    + * + * + * @param selector the key selector function + * @return an Observable that emits a List of the items with the minimum key + * value + * @see minBy() + * @see MSDN: Observable.MinBy + */ + public > Observable> minBy(Func1 selector) { + return OperationMinMax.minBy(this, selector); + } + + /** + * Returns the elements emitted by an Observable with the minimum key value + * according to the specified comparator. For an empty source, it returns an + * Observable that emits an empty List. + *

    + * + * + * @param selector the key selector function + * @param comparator the comparator used to compare key values + * @return an Observable that emits a List of the elements with the minimum + * key value according to the specified comparator + * @see minBy() + * @see MSDN: Observable.MinBy + */ + public Observable> minBy(Func1 selector, Comparator comparator) { + return OperationMinMax.minBy(this, selector, comparator); + } + + /** + * Returns the maximum item emitted by an Observable. If there is more + * than one maximum item, it returns the last one. + *

    + * + * + * @param source an Observable to determine the maximum item of + * @return an Observable that emits the maximum element + * @throws IllegalArgumentException if the source is empty + * @see max() + * @see MSDN: Observable.Max + */ + public static > Observable max(Observable source) { + return OperationMinMax.max(source); + } + + /** + * Returns the maximum item emitted by an Observable according to the + * specified comparator. If there is more than one maximum item, it returns + * the last one. + *

    + * + * + * @param comparator the comparer used to compare items + * @return an Observable that emits the maximum item according to the + * specified comparator + * @throws IllegalArgumentException if the source is empty + * @see max() + * @see MSDN: Observable.Max + */ + public Observable max(Comparator comparator) { + return OperationMinMax.max(this, comparator); + } + + /** + * Returns the items emitted by an Observable with the maximum key value. + * For an empty source, it returns an Observable that emits an empty List. + *

    + * + * + * @param selector the key selector function + * @return an Observable that emits a List of the items with the maximum key + * value + * @see maxBy() + * @see MSDN: Observable.MaxBy + */ + public > Observable> maxBy(Func1 selector) { + return OperationMinMax.maxBy(this, selector); + } + + /** + * Returns the items emitted by an Observable with the maximum key value + * according to the specified comparator. For an empty source, it returns an + * Observable that emits an empty List. + *

    + * + * + * @param selector the key selector function + * @param comparator the comparator used to compare key values + * @return an Observable that emits a List of the elements with the maximum + * key value according to the specified comparator + * @see maxBy() + * @see MSDN: Observable.MaxBy + */ + public Observable> maxBy(Func1 selector, Comparator comparator) { + return OperationMinMax.maxBy(this, selector, comparator); + } + + /** + * Returns a {@link ConnectableObservable} that shares a single subscription + * to the underlying Observable that will replay all of its items and + * notifications to any future {@link Observer}. + *

    + * + * + * @return a {@link ConnectableObservable} that upon connection causes the + * source Observable to emit items to its {@link Observer}s + * @see replay() + */ + public ConnectableObservable replay() { + return OperationMulticast.multicast(this, ReplaySubject. create()); + } + + /** + * Retry subscription to origin Observable upto given retry count. + *

    + * + *

    + * If {@link Observer#onError} is invoked the source Observable will be + * re-subscribed to as many times as defined by retryCount. + *

    + * Any {@link Observer#onNext} calls received on each attempt will be + * emitted and concatenated together. + *

    + * For example, if an Observable fails on first time but emits [1, 2] then + * succeeds the second time and emits [1, 2, 3, 4, 5] then the complete + * output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. + * + * @param retryCount number of retry attempts before failing + * @return an Observable with retry logic + * @see retry() + */ + public Observable retry(int retryCount) { + return create(OperationRetry.retry(this, retryCount)); + } + + /** + * Retry subscription to origin Observable whenever onError is + * called (infinite retry count). + *

    + * + *

    + * If {@link Observer#onError} is invoked the source Observable will be + * re-subscribed to. + *

    + * Any {@link Observer#onNext} calls received on each attempt will be + * emitted and concatenated together. + *

    + * For example, if an Observable fails on first time but emits [1, 2] then + * succeeds the second time and emits [1, 2, 3, 4, 5] then the complete + * output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. + * + * @return an Observable with retry logic + * @see retry() + */ + public Observable retry() { + return create(OperationRetry.retry(this)); + } + + /** + * This method has similar behavior to {@link #replay} except that this + * auto-subscribes to the source Observable rather than returning a + * {@link ConnectableObservable}. + *

    + * + *

    + * This is useful when you want an Observable to cache responses and you + * can't control the subscribe/unsubscribe behavior of all the + * {@link Observer}s. + *

    + * Note: You sacrifice the ability to unsubscribe from the origin when you + * use the cache() operator so be careful not to use this + * operator on Observables that emit an infinite or very large number of + * items that will use up memory. + * + * @return an Observable that, when first subscribed to, caches all of its + * notifications for the benefit of subsequent subscribers. + * @see cache() + */ + public Observable cache() { + return create(OperationCache.cache(this)); + } + + /** + * Perform work in parallel by sharding an {@code Observable} on a + * {@link Schedulers#threadPoolForComputation()} {@link Scheduler} and + * return an {@code Observable} with the output. + *

    + * + * + * @param f a {@link Func1} that applies Observable operators to + * {@code Observable} in parallel and returns an + * {@code Observable} + * @return an Observable with the output of the {@link Func1} executed on a + * {@link Scheduler} + * @see parallel() + */ + public Observable parallel(Func1, Observable> f) { + return OperationParallel.parallel(this, f); + } + + /** + * Perform work in parallel by sharding an {@code Observable} on a + * {@link Scheduler} and return an {@code Observable} with the output. + *

    + * + * + * @param f a {@link Func1} that applies Observable operators to + * {@code Observable} in parallel and returns an + * {@code Observable} + * @param s a {@link Scheduler} to perform the work on + * @return an Observable with the output of the {@link Func1} executed on a + * {@link Scheduler} + * @see parallel() + */ + + public Observable parallel(final Func1, Observable> f, final Scheduler s) { + return OperationParallel.parallel(this, f, s); + } + + + /** + * Merges an Observable<Observable<T>> to + * Observable<Observable<T>> with the number of + * inner Observables defined by parallelObservables. + *

    + * For example, if the original + * Observable<Observable<T>> has 100 Observables to + * be emitted and parallelObservables is 8, the 100 will be + * grouped onto 8 output Observables. + *

    + * This is a mechanism for efficiently processing n number of + * Observables on a smaller m number of resources (typically CPU + * cores). + *

    + * + * + * @param parallelObservables the number of Observables to merge into + * @return an Observable of Observables constrained to number defined by + * parallelObservables + * @see parallelMerge() + */ + public static Observable> parallelMerge(Observable> source, int parallelObservables) { + return OperationParallelMerge.parallelMerge(source, parallelObservables); + } + + /** + * Merges an Observable<Observable<T>> to + * Observable<Observable<T>> with the number of + * inner Observables defined by parallelObservables and runs + * each Observable on the defined Scheduler. + *

    + * For example, if the original + * Observable<Observable<T>> has 100 Observables to + * be emitted and parallelObservables is 8, the 100 will be + * grouped onto 8 output Observables. + *

    + * This is a mechanism for efficiently processing n number of + * Observables on a smaller m number of resources (typically CPU + * cores). + *

    + * + * + * @param parallelObservables the number of Observables to merge into + * @return an Observable of Observables constrained to number defined by + * parallelObservables. + * @see parallelMerge() + */ + public static Observable> parallelMerge(Observable> source, int parallelObservables, Scheduler scheduler) { + return OperationParallelMerge.parallelMerge(source, parallelObservables, scheduler); + } + + /** + * Returns a {@link ConnectableObservable}, which waits until its + * {@link ConnectableObservable#connect connect} method is called before it + * begins emitting items to those {@link Observer}s that have subscribed to + * it. + *

    + * + * + * @return a {@link ConnectableObservable} that upon connection causes the + * source Observable to emit items to its {@link Observer}s + * @see publish() + */ + public ConnectableObservable publish() { + return OperationMulticast.multicast(this, PublishSubject. create()); + } + + /** + * Returns a {@link ConnectableObservable} that shares a single subscription + * that contains the last notification only. + *

    + * + * + * @return a {@link ConnectableObservable} + * @see publishLast() + */ + public ConnectableObservable publishLast() { + return OperationMulticast.multicast(this, AsyncSubject. create()); + } + + /** + * Synonymous with reduce(). + *

    + * + * + * @see aggregate() + * @see #reduce(Func2) + */ + public Observable aggregate(Func2 accumulator) { + return reduce(accumulator); + } + + /** + * Returns an Observable that applies a function of your choosing to the + * first item emitted by a source Observable, then feeds the result of that + * function along with the second item emitted by an Observable into the + * same function, and so on until all items have been emitted by the source + * Observable, emitting the final result from the final call to your + * function as its sole item. + *

    + * + *

    + * This technique, which is called "reduce" or "aggregate" here, is + * sometimes called "fold," "accumulate," "compress," or "inject" in other + * programming contexts. Groovy, for instance, has an inject + * method that does a similar operation on lists. + * + * @param initialValue the initial (seed) accumulator value + * @param accumulator an accumulator function to be invoked on each item + * emitted by the source Observable, the result of which + * will be used in the next accumulator call + * @return an Observable that emits a single item that is the result of + * accumulating the output from the items emitted by the source + * Observable + * @see reduce() + * @see MSDN: Observable.Aggregate + * @see Wikipedia: Fold (higher-order function) + */ + public Observable reduce(R initialValue, Func2 accumulator) { + return create(OperationScan.scan(this, initialValue, accumulator)).takeLast(1); + } + + /** + * Synonymous with reduce(). + *

    + * + * + * @see aggregate() + * @see #reduce(Object, Func2) + */ + public Observable aggregate(R initialValue, Func2 accumulator) { + return reduce(initialValue, accumulator); + } + + /** + * Returns an Observable that applies a function of your choosing to the + * first item emitted by a source Observable, then feeds the result of that + * function along with the second item emitted by an Observable into the + * same function, and so on until all items have been emitted by the source + * Observable, emitting the result of each of these iterations. + *

    + * + *

    + * This sort of function is sometimes called an accumulator. + *

    + * Note that when you pass a seed to scan() the resulting + * Observable will emit that seed as its first emitted item. + * + * @param accumulator an accumulator function to be invoked on each item + * emitted by the source Observable, whose result will be + * emitted to {@link Observer}s via + * {@link Observer#onNext onNext} and used in the next + * accumulator call + * @return an Observable that emits the results of each call to the + * accumulator function + * @see scan() + * @see MSDN: Observable.Scan + */ + public Observable scan(Func2 accumulator) { + return create(OperationScan.scan(this, accumulator)); + } + + /** + * Returns an Observable that emits the results of sampling the items + * emitted by the source Observable at a specified time interval. + *

    + * + * + * @param period the sampling rate + * @param unit the {@link TimeUnit} in which period is defined + * @return an Observable that emits the results of sampling the items + * emitted by the source Observable at the specified time interval + * @see sample() + */ + public Observable sample(long period, TimeUnit unit) { + return create(OperationSample.sample(this, period, unit)); + } + + /** + * Returns an Observable that emits the results of sampling the items + * emitted by the source Observable at a specified time interval. + *

    + * + * + * @param period the sampling rate + * @param unit the {@link TimeUnit} in which period is defined + * @param scheduler the {@link Scheduler} to use when sampling + * @return an Observable that emits the results of sampling the items + * emitted by the source Observable at the specified time interval + * @see sample() + */ + public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { + return create(OperationSample.sample(this, period, unit, scheduler)); + } + + /** + * Returns an Observable that applies a function of your choosing to the + * first item emitted by a source Observable, then feeds the result of that + * function along with the second item emitted by an Observable into the + * same function, and so on until all items have been emitted by the source + * Observable, emitting the result of each of these iterations. + *

    + * + *

    + * This sort of function is sometimes called an accumulator. + *

    + * Note that when you pass a seed to scan() the resulting + * Observable will emit that seed as its first emitted item. + * + * @param initialValue the initial (seed) accumulator value + * @param accumulator an accumulator function to be invoked on each item + * emitted by the source Observable, whose result will be + * emitted to {@link Observer}s via + * {@link Observer#onNext onNext} and used in the next + * accumulator call + * @return an Observable that emits the results of each call to the + * accumulator function + * @see scan() + * @see MSDN: Observable.Scan + */ + public Observable scan(R initialValue, Func2 accumulator) { + return create(OperationScan.scan(this, initialValue, accumulator)); + } + + /** + * Returns an Observable that emits a Boolean that indicates whether all of + * the items emitted by the source Observable satisfy a condition. + *

    + * + * + * @param predicate a function that evaluates an item and returns a Boolean + * @return an Observable that emits true if all items emitted + * by the source Observable satisfy the predicate; otherwise, + * false + * @see all() + */ + public Observable all(Func1 predicate) { + return create(OperationAll.all(this, predicate)); + } + + /** + * Returns an Observable that skips the first num items emitted + * by the source Observable and emits the remainder. + *

    + * + *

    + * You can ignore the first num items emitted by an Observable + * and attend only to those items that come after, by modifying the + * Observable with the skip method. + * + * @param num the number of items to skip + * @return an Observable that is identical to the source Observable except + * that it does not emit the first num items that the + * source emits + * @see skip() + */ + public Observable skip(int num) { + return create(OperationSkip.skip(this, num)); + } + + /** + * Returns an Observable that emits only the very first item emitted by the + * source Observable. + *

    + * + * + * @return an Observable that emits only the very first item from the + * source, or none if the source Observable completes without + * emitting a single item + * @see first() + * @see MSDN: Observable.First + */ + public Observable first() { + return take(1); + } + + /** + * Returns an Observable that emits only the very first item emitted by the + * source Observable that satisfies a given condition. + *

    + * + * + * @param predicate the condition any source emitted item has to satisfy + * @return an Observable that emits only the very first item satisfying the + * given condition from the source, or none if the source Observable + * completes without emitting a single matching item + * @see first() + * @see MSDN: Observable.First + */ + public Observable first(Func1 predicate) { + return skipWhile(not(predicate)).take(1); + } + + /** + * Returns an Observable that emits only the very first item emitted by the + * source Observable, or a default value. + *

    + * + * + * @param defaultValue the default value to emit if the source Observable + * doesn't emit anything + * @return an Observable that emits only the very first item from the + * source, or a default value if the source Observable completes + * without emitting a single item + * @see firstOrDefault() + * @see MSDN: Observable.FirstOrDefault + */ + public Observable firstOrDefault(T defaultValue) { + return create(OperationFirstOrDefault.firstOrDefault(this, defaultValue)); + } + + /** + * Returns an Observable that emits only the very first item emitted by the + * source Observable that satisfies a given condition, or a default value + * otherwise. + *

    + * + * + * @param predicate the condition any source emitted item has to satisfy + * @param defaultValue the default value to emit if the source Observable + * doesn't emit anything that satisfies the given condition + * @return an Observable that emits only the very first item from the source + * that satisfies the given condition, or a default value otherwise + * @see firstOrDefault() + * @see MSDN: Observable.FirstOrDefault + */ + public Observable firstOrDefault(Func1 predicate, T defaultValue) { + return create(OperationFirstOrDefault.firstOrDefault(this, predicate, defaultValue)); + } + + /** + * Returns the elements of the specified sequence or the specified default + * value in a singleton sequence if the sequence is empty. + *

    + * + * + * @param defaultValue the value to return if the sequence is empty + * @return an Observable that emits the specified default value if the + * source is empty; otherwise, the items emitted by the source + * @see defaultIfEmpty() + * @see MSDN: Observable.DefaultIfEmpty + */ + public Observable defaultIfEmpty(T defaultValue) { + return create(OperationDefaultIfEmpty.defaultIfEmpty(this, defaultValue)); + } + + /** + * Returns an Observable that emits only the first num items + * emitted by the source Observable. + *

    + * + *

    + * This method returns an Observable that will invoke a subscribing + * {@link Observer}'s {@link Observer#onNext onNext} function a maximum of + * num times before invoking + * {@link Observer#onCompleted onCompleted}. + * + * @param num the number of items to emit + * @return an Observable that emits only the first num items + * from the source Observable, or all of the items from the source + * Observable if that Observable emits fewer than num + * items + * @see take() + */ + public Observable take(final int num) { + return create(OperationTake.take(this, num)); + } + + /** + * Returns an Observable that emits items emitted by the source Observable + * so long as a specified condition is true. + *

    + * + * + * @param predicate a function that evaluates an item emitted by the source + * Observable and returns a Boolean + * @return an Observable that emits the items from the source Observable so + * long as each item satisfies the condition defined by + * predicate + * @see takeWhile() + */ + public Observable takeWhile(final Func1 predicate) { + return create(OperationTakeWhile.takeWhile(this, predicate)); + } + + /** + * Returns an Observable that emits the items emitted by a source Observable + * so long as a given predicate remains true, where the predicate can + * operate on both the item and its index relative to the complete sequence. + *

    + * + * + * @param predicate a function to test each item emitted by the source + * Observable for a condition; the second parameter of the + * function represents the index of the source item + * @return an Observable that emits items from the source Observable so long + * as the predicate continues to return true for each + * item, then completes + * @see takeWhileWithIndex() + */ + public Observable takeWhileWithIndex(final Func2 predicate) { + return create(OperationTakeWhile.takeWhileWithIndex(this, predicate)); + } + + /** + * Returns an Observable that emits only the very first item emitted by the + * source Observable. + *

    + * + * + * @return an Observable that emits only the very first item from the + * source, or none if the source Observable completes without + * emitting a single item + * @see first() + * @see MSDN: Observable.First + * @see #first() + */ + public Observable takeFirst() { + return first(); + } + + /** + * Returns an Observable that emits only the very first item emitted by the + * source Observable that satisfies a given condition. + *

    + * + * + * @param predicate the condition any source emitted item has to satisfy + * @return an Observable that emits only the very first item satisfying the + * given condition from the source, or none if the source Observable + * completes without emitting a single matching item + * @see first() + * @see MSDN: Observable.First + * @see #first(Func1) + */ + public Observable takeFirst(Func1 predicate) { + return first(predicate); + } + + /** + * Returns an Observable that emits only the last count items + * emitted by the source Observable. + *

    + * + * + * @param count the number of items to emit from the end of the sequence + * emitted by the source Observable + * @return an Observable that emits only the last count items + * emitted by the source Observable + * @see takeLast() + */ + public Observable takeLast(final int count) { + return create(OperationTakeLast.takeLast(this, count)); + } + + /** + * Returns an Observable that emits the items from the source Observable + * only until the other Observable emits an item. + *

    + * + * + * @param other the Observable whose first emitted item will cause + * takeUntil to stop emitting items from the + * source Observable + * @param the type of items emitted by other + * @return an Observable that emits the items of the source Observable until + * such time as other emits its first item + * @see takeUntil() + */ + public Observable takeUntil(Observable other) { + return OperationTakeUntil.takeUntil(this, other); + } + + /** + * Returns an Observable that bypasses all items from the source Observable + * as long as the specified condition holds true, but emits all further + * source items as soon as the condition becomes false. + *

    + * + * + * @param predicate a function to test each item emitted from the source + * Observable for a condition. It receives the emitted item + * as the first parameter and the index of the emitted item + * as a second parameter. + * @return an Observable that emits all items from the source Observable as + * soon as the condition becomes false + * @see skipWhileWithIndex() + * @see MSDN: Observable.SkipWhile + */ + public Observable skipWhileWithIndex(Func2 predicate) { + return create(OperationSkipWhile.skipWhileWithIndex(this, predicate)); + } + + /** + * Returns an Observable that bypasses all items from the source Observable + * as long as the specified condition holds true, but emits all further + * source items as soon as the condition becomes false. + *

    + * + * + * @param predicate a function to test each item emitted from the source + * Observable for a condition + * @return an Observable that emits all items from the source Observable as + * soon as the condition becomes false + * @see skipWhile() + * @see MSDN: Observable.SkipWhile + */ + public Observable skipWhile(Func1 predicate) { + return create(OperationSkipWhile.skipWhile(this, predicate)); + } + + /** + * Bypasses a specified number of items at the end of an Observable + * sequence. + *

    + * This operator accumulates a queue with a length enough to store the first + * count items. As more items are received, items are taken + * from the front of the queue and produced on the result sequence. This + * causes elements to be delayed. + *

    + * + * + * @param count number of elements to bypass at the end of the source + * sequence + * @return an Observable sequence emitting the source sequence items + * except for the bypassed ones at the end + * @throws IndexOutOfBoundsException if count is less than zero + * @see skipLast() + * @see MSDN: Observable.SkipLast + */ + public Observable skipLast(int count) { + return create(OperationSkipLast.skipLast(this, count)); + } + + /** + * Returns an Observable that emits a single item, a list composed of all + * the items emitted by the source Observable. + *

    + * + *

    + * Normally, an Observable that returns multiple items will do so by + * invoking its {@link Observer}'s {@link Observer#onNext onNext} method for + * each such item. You can change this behavior, instructing the Observable + * to compose a list of all of these items and then to invoke the Observer's + * onNext function once, passing it the entire list, by calling + * the Observable's toList method prior to calling its + * {@link #subscribe} method. + *

    + * Be careful not to use this operator on Observables that emit infinite or + * very large numbers of items, as you do not have the option to + * unsubscribe. + * + * @return an Observable that emits a single item: a List containing all of + * the items emitted by the source Observable. + * @see toList() + */ + public Observable> toList() { + return create(OperationToObservableList.toObservableList(this)); + } + + /** + * Return an Observable that emits the items emitted by the source + * Observable, in a sorted order (each item emitted by the Observable must + * implement {@link Comparable} with respect to all other items in the + * sequence). + *

    + * + * + * @throws ClassCastException if any item emitted by the Observable does not + * implement {@link Comparable} with respect to + * all other items emitted by the Observable + * @return an Observable that emits the items from the source Observable in + * sorted order + * @see toSortedList() + */ + public Observable> toSortedList() { + return create(OperationToObservableSortedList.toSortedList(this)); + } + + /** + * Return an Observable that emits the items emitted by the source + * Observable, in a sorted order based on a specified comparison function + *

    + * + * + * @param sortFunction a function that compares two items emitted by the + * source Observable and returns an Integer that + * indicates their sort order + * @return an Observable that emits the items from the source Observable in + * sorted order + * @see toSortedList() + */ + public Observable> toSortedList(Func2 sortFunction) { + return create(OperationToObservableSortedList.toSortedList(this, sortFunction)); + } + + /** + * Emit a specified set of items before beginning to emit items from the + * source Observable. + *

    + * + * + * @param values Iterable of the items you want the modified Observable to + * emit first + * @return an Observable that exhibits the modified behavior + * @see startWith() + */ + public Observable startWith(Iterable values) { + return concat(Observable. from(values), this); + } + + /** + * Emit a specified set of items with the specified scheduler before + * beginning to emit items from the source Observable. + *

    + * + * + * @param values iterable of the items you want the modified Observable to + * emit first + * @param scheduler the scheduler to emit the prepended values on + * @return an Observable that exhibits the modified behavior + * @see startWith() + * @see MSDN: Observable.StartWith + */ + public Observable startWith(Iterable values, Scheduler scheduler) { + return concat(from(values, scheduler), this); + } + + /** + * Emit a specified array of items with the specified scheduler before + * beginning to emit items from the source Observable. + *

    + * + * + * @param values the items you want the modified Observable to emit first + * @param scheduler the scheduler to emit the prepended values on + * @return an Observable that exhibits the modified behavior + * @see startWith() + * @see MSDN: Observable.StartWith + */ + public Observable startWith(T[] values, Scheduler scheduler) { + return startWith(Arrays.asList(values), scheduler); + } + + /** + * Emit a specified item before beginning to emit items from the source + * Observable. + *

    + * + * + * @param t1 item to emit + * @return an Observable that exhibits the modified behavior + * @see startWith() + */ + public Observable startWith(T t1) { + return concat(Observable. from(t1), this); + } + + /** + * Emit a specified set of items before beginning to emit items from the + * source Observable. + *

    + * + * + * @param t1 first item to emit + * @param t2 second item to emit + * @return an Observable that exhibits the modified behavior + * @see startWith() + */ + public Observable startWith(T t1, T t2) { + return concat(Observable. from(t1, t2), this); + } + + /** + * Emit a specified set of items before beginning to emit items from the + * source Observable. + *

    + * + * + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @return an Observable that exhibits the modified behavior + * @see startWith() + */ + public Observable startWith(T t1, T t2, T t3) { + return concat(Observable. from(t1, t2, t3), this); + } + + /** + * Emit a specified set of items before beginning to emit items from the + * source Observable. + *

    + * + * + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit + * @return an Observable that exhibits the modified behavior + * @see startWith() + */ + public Observable startWith(T t1, T t2, T t3, T t4) { + return concat(Observable. from(t1, t2, t3, t4), this); + } + + /** + * Emit a specified set of items before beginning to emit items from the + * source Observable. + *

    + * + * + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit + * @param t5 fifth item to emit + * @return an Observable that exhibits the modified behavior + * @see startWith() + */ + public Observable startWith(T t1, T t2, T t3, T t4, T t5) { + return concat(Observable. from(t1, t2, t3, t4, t5), this); + } + + /** + * Emit a specified set of items before beginning to emit items from the + * source Observable. + *

    + * + * + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit + * @param t5 fifth item to emit + * @param t6 sixth item to emit + * @return an Observable that exhibits the modified behavior + * @see startWith() + */ + public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { + return concat(Observable. from(t1, t2, t3, t4, t5, t6), this); + } + + /** + * Emit a specified set of items before beginning to emit items from the + * source Observable. + *

    + * + * + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit + * @param t5 fifth item to emit + * @param t6 sixth item to emit + * @param t7 seventh item to emit + * @return an Observable that exhibits the modified behavior + * @see startWith() + */ + public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { + return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7), this); + } + + /** + * Emit a specified set of items before beginning to emit items from the + * source Observable. + *

    + * + * + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit + * @param t5 fifth item to emit + * @param t6 sixth item to emit + * @param t7 seventh item to emit + * @param t8 eighth item to emit + * @return an Observable that exhibits the modified behavior + * @see startWith() + */ + public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { + return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8), this); + } + + /** + * Emit a specified set of items before beginning to emit items from the + * source Observable. + *

    + * + * + * @param t1 first item to emit + * @param t2 second item to emit + * @param t3 third item to emit + * @param t4 fourth item to emit + * @param t5 fifth item to emit + * @param t6 sixth item to emit + * @param t7 seventh item to emit + * @param t8 eighth item to emit + * @param t9 ninth item to emit + * @return an Observable that exhibits the modified behavior + * @see startWith() + */ + public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { + return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8, t9), this); + } + + /** + * Groups the items emitted by an Observable according to a specified + * criterion, and emits these grouped items as {@link GroupedObservable}s, + * one GroupedObservable per group. + *

    + * + * + * @param keySelector a function that extracts the key from an item + * @param elementSelector a function to map a source item to an item in a + * {@link GroupedObservable} + * @param the key type + * @param the type of items emitted by the resulting + * {@link GroupedObservable}s + * @return an Observable that emits {@link GroupedObservable}s, each of + * which corresponds to a unique key value and emits items + * representing items from the source Observable that share that key + * value + * @see groupBy + */ + public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) { + return create(OperationGroupBy.groupBy(this, keySelector, elementSelector)); + } + + /** + * Groups the items emitted by an Observable according to a specified + * criterion, and emits these grouped items as {@link GroupedObservable}s, + * one GroupedObservable per group. + *

    + * + * + * @param keySelector a function that extracts the key for each item + * @param the key type + * @return an Observable that emits {@link GroupedObservable}s, each of + * which corresponds to a unique key value and emits items + * representing items from the source Observable that share that key + * value + * @see groupBy + */ + public Observable> groupBy(final Func1 keySelector) { + return create(OperationGroupBy.groupBy(this, keySelector)); + } + + /** + * Returns an {@link Observable} that emits true if the source + * {@link Observable} is empty, otherwise false. + *

    + * In Rx.Net this is negated as the any operator but renamed in + * RxJava to better match Java naming idioms. + *

    + * + * + * @return an Observable that emits a Boolean + * @see isEmpty() + * @see MSDN: Observable.Any + */ + public Observable isEmpty() { + return create(OperationAny.isEmpty(this)); + } + + /** + * Returns an {@link Observable} that emits the last item emitted by the + * source or an IllegalArgumentException if the source + * {@link Observable} is empty. + *

    + * + * + * @return + * @see last() + */ + public Observable last() { + return create(OperationLast.last(this)); + } + + /** + * Converts an Observable into a {@link BlockingObservable} (an Observable + * with blocking operators). + * + * @return + * @see Blocking Observable Operators + */ + public BlockingObservable toBlockingObservable() { + return BlockingObservable.from(this); + } + + /** + * Converts the items emitted by an Observable to the specified type. + *

    + * + * + * @param klass the target class type which the items will be converted to + * @return an Observable that emits each item from the source Observable + * converted to the specified type + * @see cast() + * @see MSDN: Observable.Cast + */ + public Observable cast(final Class klass) { + return create(OperationCast.cast(this, klass)); + } + + /** + * Filters the items emitted by an Observable based on the specified type. + *

    + * + * + * @param klass the class type to filter the items emitted by the source + * Observable + * @return an Observable that emits items from the source Observable of + * type klass. + * @see ofClass() + * @see MSDN: Observable.OfType + */ + public Observable ofType(final Class klass) { + return filter(new Func1() { + public Boolean call(T t) { + return klass.isInstance(t); + } + }).cast(klass); + } + + /** + * Ignores all items emitted by an Observable and only calls + * onCompleted or onError. + *

    + * + * + * @return an empty Observable that only calls onCompleted or + * onError + * @see ignoreElements() + * @see MSDN: Observable.IgnoreElements + */ + public Observable ignoreElements() { + return filter(alwaysFalse()); + } + + /** + * Applies a timeout policy for each element in the observable sequence, + * using the specified scheduler to run timeout timers. If the next element + * isn't received within the specified timeout duration starting from its + * predecessor, a TimeoutException is propagated to the observer. + *

    + * + * + * @param timeout maximum duration between values before a timeout occurs + * @param timeUnit the unit of time which applies to the + * timeout argument. + * @return the source Observable with a TimeoutException in + * case of a timeout + * @see timeout() + * @see MSDN: Observable.Timeout + */ + public Observable timeout(long timeout, TimeUnit timeUnit) { + return create(OperationTimeout.timeout(this, timeout, timeUnit)); + } + + /** + * Applies a timeout policy for each element in the observable sequence, + * using the specified scheduler to run timeout timers. If the next element + * isn't received within the specified timeout duration starting from its + * predecessor, the other observable sequence is used to produce future + * messages from that point on. + *

    + * + * + * @param timeout maximum duration between values before a timeout occurs + * @param timeUnit the unit of time which applies to the + * timeout argument + * @param other sequence to return in case of a timeout + * @return the source sequence switching to the other sequence in case of a + * timeout + * @see timeout() + * @see MSDN: Observable.Timeout + */ + public Observable timeout(long timeout, TimeUnit timeUnit, Observable other) { + return create(OperationTimeout.timeout(this, timeout, timeUnit, other)); + } + + /** + * Applies a timeout policy for each element in the observable sequence, + * using the specified scheduler to run timeout timers. If the next element + * isn't received within the specified timeout duration starting from its + * predecessor, a TimeoutException is propagated to the observer. + *

    + * + * + * @param timeout maximum duration between values before a timeout occurs + * @param timeUnit the unit of time which applies to the + * timeout argument + * @param scheduler Scheduler to run the timeout timers on + * @return the source sequence with a TimeoutException in case + * of a timeout + * @see timeout() + * @see MSDN: Observable.Timeout + */ + public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { + return create(OperationTimeout.timeout(this, timeout, timeUnit, scheduler)); + } + + /** + * Applies a timeout policy for each element in the observable sequence, + * using the specified scheduler to run timeout timers. If the next element + * isn't received within the specified timeout duration starting from its + * predecessor, the other observable sequence is used to produce future + * messages from that point on. + *

    + * + * + * @param timeout maximum duration between values before a timeout occurs + * @param timeUnit the unit of time which applies to the + * timeout argument + * @param other sequence to return in case of a timeout + * @param scheduler Scheduler to run the timeout timers on + * @return the source sequence switching to the other sequence in case of a + * timeout + * @see timeout() + * @see MSDN: Observable.Timeout + */ + public Observable timeout(long timeout, TimeUnit timeUnit, Observable other, Scheduler scheduler) { + return create(OperationTimeout.timeout(this, timeout, timeUnit, other, scheduler)); + } + + /** + * Records the time interval between consecutive items emitted by an + * Observable. + *

    + * + * + * @return an Observable that emits time interval information items + * @see timeInterval() + * @see MSDN: Observable.TimeInterval + */ + public Observable> timeInterval() { + return create(OperationTimeInterval.timeInterval(this)); + } + + /** + * Records the time interval between consecutive items emitted by an + * Observable, using the specified Scheduler to compute time intervals. + *

    + * + * + * @param scheduler Scheduler used to compute time intervals + * @return an Observable that emits time interval information items + * @see timeInterval() + * @see MSDN: Observable.TimeInterval + */ + public Observable> timeInterval(Scheduler scheduler) { + return create(OperationTimeInterval.timeInterval(this, scheduler)); + } + + /** + * Constructs an Observable that depends on a resource object. + *

    + * + * + * @param resourceFactory the factory function to obtain a resource object + * that depends on the Observable + * @param observableFactory the factory function to obtain an Observable + * @return the Observable whose lifetime controls the lifetime of the + * dependent resource object + * @see using() + * @see MSDN: Observable.Using + */ + public static Observable using(Func0 resourceFactory, Func1> observableFactory) { + return create(OperationUsing.using(resourceFactory, observableFactory)); + } + + /** + * Propagates the Observable sequence that reacts first. + *

    + * + * + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see amb() + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2) { + return create(OperationAmb.amb(o1, o2)); + } + + /** + * Propagates the Observable sequence that reacts first. + *

    + * + * + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see amb() + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3) { + return create(OperationAmb.amb(o1, o2, o3)); + } + + /** + * Propagates the observable sequence that reacts first. + *

    + * + * + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see amb() + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4) { + return create(OperationAmb.amb(o1, o2, o3, o4)); + } + + /** + * Propagates the Observable sequence that reacts first. + *

    + * + * + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @param o5 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see amb() + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5) { + return create(OperationAmb.amb(o1, o2, o3, o4, o5)); + } + + /** + * Propagates the observable sequence that reacts first. + *

    + * + * + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @param o5 an Observable competing to react first + * @param o6 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see amb() + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6) { + return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6)); + } + + /** + * Propagates the observable sequence that reacts first. + *

    + * + * + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @param o5 an Observable competing to react first + * @param o6 an Observable competing to react first + * @param o7 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see amb() + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7) { + return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7)); + } + + /** + * Propagates the observable sequence that reacts first. + *

    + * + * + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @param o5 an Observable competing to react first + * @param o6 an Observable competing to react first + * @param o7 an Observable competing to react first + * @param o8 an observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see amb() + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8) { + return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8)); + } + + /** + * Propagates the observable sequence that reacts first. + *

    + * + * + * @param o1 an Observable competing to react first + * @param o2 an Observable competing to react first + * @param o3 an Observable competing to react first + * @param o4 an Observable competing to react first + * @param o5 an Observable competing to react first + * @param o6 an Observable competing to react first + * @param o7 an Observable competing to react first + * @param o8 an Observable competing to react first + * @param o9 an Observable competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see amb() + * @see MSDN: Observable.Amb + */ + public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9) { + return create(OperationAmb.amb(o1, o2, o3, o4, o5, o6, o7, o8, o9)); + } + + /** + * Propagates the observable sequence that reacts first. + *

    + * + * + * @param sources Observable sources competing to react first + * @return an Observable that reflects whichever of the given Observables + * reacted first + * @see amb() + * @see MSDN: Observable.Amb + */ + public static Observable amb(Iterable> sources) { + return create(OperationAmb.amb(sources)); + } + + /** + * Invokes an action for each item emitted by the Observable. + *

    + * + * + * @param observer the action to invoke for each item emitted in the source + * sequence + * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() + * @see MSDN: Observable.Do + */ + public Observable doOnEach(Observer observer) { + return create(OperationDoOnEach.doOnEach(this, observer)); + } + + /** + * Invokes an action for each item emitted by an Observable. + *

    + * + * + * @param onNext the action to invoke for each item in the source + * sequence + * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() + * @see MSDN: Observable.Do + */ + public Observable doOnEach(final Action1 onNext) { + Observer observer = new Observer() { + @Override + public void onCompleted() {} + + @Override + public void onError(Throwable e) {} + + @Override + public void onNext(T args) { + onNext.call(args); + } + + }; + + + return create(OperationDoOnEach.doOnEach(this, observer)); + } + + /** + * Invokes an action if onError is called from the Observable. + *

    + * + * + * @param onError the action to invoke if onError is invoked + * @return the source sequence with the side-effecting behavior applied + * @see doOnError() + * @see MSDN: Observable.Do + */ + public Observable doOnError(final Action1 onError) { + Observer observer = new Observer() { + @Override + public void onCompleted() {} + + @Override + public void onError(Throwable e) { + onError.call(e); + } + + @Override + public void onNext(T args) { } + + }; + + + return create(OperationDoOnEach.doOnEach(this, observer)); + } + + /** + * Invokes an action when onCompleted is called by the + * Observable. + *

    + * + * + * @param onCompleted the action to invoke when onCompleted is + * called + * @return the source sequence with the side-effecting behavior applied + * @see doOnCompleted() + * @see MSDN: Observable.Do + */ + public Observable doOnCompleted(final Action0 onCompleted) { + Observer observer = new Observer() { + @Override + public void onCompleted() { + onCompleted.call(); + } + + @Override + public void onError(Throwable e) { } + + @Override + public void onNext(T args) { } + + }; + + + return create(OperationDoOnEach.doOnEach(this, observer)); + } + + /** + * Invokes an action for each item emitted by an Observable. + * + * @param onNext the action to invoke for each item in the source sequence + * @param onError the action to invoke when the source Observable calls + * onError + * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() + * @see MSDN: Observable.Do + */ + public Observable doOnEach(final Action1 onNext, final Action1 onError) { + Observer observer = new Observer() { + @Override + public void onCompleted() {} + + @Override + public void onError(Throwable e) { + onError.call(e); + } + + @Override + public void onNext(T args) { + onNext.call(args); + } + + }; + + + return create(OperationDoOnEach.doOnEach(this, observer)); + } + + /** + * Invokes an action for each item emitted by an Observable. + * + * @param onNext the action to invoke for each item in the source sequence + * @param onError the action to invoke when the source Observable calls + * onError + * @param onCompleted the action to invoke when the source Observable calls + * onCompleted + * @return the source sequence with the side-effecting behavior applied + * @see doOnEach() + * @see MSDN: Observable.Do + */ + public Observable doOnEach(final Action1 onNext, final Action1 onError, final Action0 onCompleted) { + Observer observer = new Observer() { + @Override + public void onCompleted() { + onCompleted.call(); + } + + @Override + public void onError(Throwable e) { + onError.call(e); + } + + @Override + public void onNext(T args) { + onNext.call(args); + } + + }; + + + return create(OperationDoOnEach.doOnEach(this, observer)); + } + + /** + * Whether a given {@link Function} is an internal implementation inside + * rx.* packages or not. + *

    + * For why this is being used see + * https://github.com/Netflix/RxJava/issues/216 for discussion on + * "Guideline 6.4: Protect calls to user code from within an operator" + * + * Note: If strong reasons for not depending on package names comes up then + * the implementation of this method can change to looking for a marker + * interface. + * + * @param o + * @return {@code true} if the given function is an internal implementation, + * and {@code false} otherwise. + */ + private boolean isInternalImplementation(Object o) { + if (o == null) { + return true; + } + // prevent double-wrapping (yeah it happens) + if (o instanceof SafeObserver) { + return true; + } + + Class clazz = o.getClass(); + if (internalClassMap.containsKey(clazz)) { + //don't need to do reflection + return internalClassMap.get(clazz); + } else { + // we treat the following package as "internal" and don't wrap it + Package p = o.getClass().getPackage(); // it can be null + Boolean isInternal = (p != null && p.getName().startsWith("rx.operators")); + internalClassMap.put(clazz, isInternal); + return isInternal; + } + } + + /** + * Creates a pattern that matches when both Observable sequences have an + * available item. + *

    + * + * + * @param right Observable sequence to match with the left sequence + * @return Pattern object that matches when both Observable sequences have + * an available item + * @throws NullPointerException if right is null + * @see and() + * @see MSDN: Observable.And + */ + public Pattern2 and(Observable right) { + return OperationJoinPatterns.and(this, right); + } + + /** + * Matches when the Observable sequence has an available item and + * projects the item by invoking the selector function. + *

    + * + * + * @param selector Selector that will be invoked for elements in the source + * sequence + * @return Plan that produces the projected results, to be fed (with other + * plans) to the When operator + * @throws NullPointerException if selector is null + * @see then() + * @see MSDN: Observable.Then + */ + public Plan0 then(Func1 selector) { + return OperationJoinPatterns.then(this, selector); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param plans a series of plans created by use of the Then operator on + * patterns + * @return an Observable sequence with the results from matching several + * patterns + * @throws NullPointerException if plans is null + * @see when() + * @see MSDN: Observable.When + */ + public static Observable when(Plan0... plans) { + return create(OperationJoinPatterns.when(plans)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param plans a series of plans created by use of the Then operator on + * patterns + * @return an Observable sequence with the results from matching several + * patterns + * @throws NullPointerException if plans is null + * @see when() + * @see MSDN: Observable.When + */ + public static Observable when(Iterable> plans) { + if (plans == null) { + throw new NullPointerException("plans"); + } + return create(OperationJoinPatterns.when(plans)); + } + + /** + * Joins the results from a pattern. + *

    + * + * + * @param p1 the plan to join + * @return an Observable sequence with the results from matching a pattern + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1) { + return create(OperationJoinPatterns.when(p1)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2) { + return create(OperationJoinPatterns.when(p1, p2)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3) { + return create(OperationJoinPatterns.when(p1, p2, p3)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @param p7 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @param p7 a plan + * @param p8 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8)); + } + + /** + * Joins together the results from several patterns. + *

    + * + * + * @param p1 a plan + * @param p2 a plan + * @param p3 a plan + * @param p4 a plan + * @param p5 a plan + * @param p6 a plan + * @param p7 a plan + * @param p8 a plan + * @param p9 a plan + * @return an Observable sequence with the results from matching several + * patterns + * @see when() + * @see MSDN: Observable.When + */ + @SuppressWarnings("unchecked") + public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8, Plan0 p9) { + return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8, p9)); + } + /** + * Correlates the elements of two sequences based on overlapping durations. + * @param right The right observable sequence to join elements for. + * @param leftDurationSelector A function to select the duration of each + * element of this observable sequence, used to + * determine overlap. + * @param rightDurationSelector A function to select the duration of each + * element of the right observable sequence, + * used to determine overlap. + * @param resultSelector A function invoked to compute a result element + * for any two overlapping elements of the left and + * right observable sequences. + * @return An observable sequence that contains result elements computed + * from source elements that have an overlapping duration. + * @see MSDN: Observable.Join + */ + public Observable join(Observable right, Func1> leftDurationSelector, + Func1> rightDurationSelector, + Func2 resultSelector) { + return create(new OperationJoin(this, right, leftDurationSelector, rightDurationSelector, resultSelector)); + } +} + From 63d2400e7e7ce0af9d99158fa7032346514b42db Mon Sep 17 00:00:00 2001 From: zsxwing Date: Mon, 25 Nov 2013 19:21:02 +0800 Subject: [PATCH 311/333] Fixed the issue #521 --- .../src/main/java/rx/operators/OperationInterval.java | 1 - .../test/java/rx/operators/OperationIntervalTest.java | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationInterval.java b/rxjava-core/src/main/java/rx/operators/OperationInterval.java index 7e1be94991..72d35d37de 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationInterval.java +++ b/rxjava-core/src/main/java/rx/operators/OperationInterval.java @@ -78,7 +78,6 @@ public void call() { @Override public void call() { wrapped.unsubscribe(); - observer.onCompleted(); } }); } diff --git a/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java b/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java index 5f64452e3b..293ff236c9 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java @@ -66,7 +66,7 @@ public void testInterval() { sub.unsubscribe(); scheduler.advanceTimeTo(4, TimeUnit.SECONDS); verify(observer, never()).onNext(2L); - verify(observer, times(1)).onCompleted(); + verify(observer, never()).onCompleted(); verify(observer, never()).onError(any(Throwable.class)); } @@ -101,11 +101,11 @@ public void testWithMultipleSubscribersStartingAtSameTime() { scheduler.advanceTimeTo(4, TimeUnit.SECONDS); verify(observer, never()).onNext(2L); - verify(observer, times(1)).onCompleted(); + verify(observer, never()).onCompleted(); verify(observer, never()).onError(any(Throwable.class)); verify(observer2, never()).onNext(2L); - verify(observer2, times(1)).onCompleted(); + verify(observer2, never()).onCompleted(); verify(observer2, never()).onError(any(Throwable.class)); } @@ -141,11 +141,11 @@ public void testWithMultipleStaggeredSubscribers() { sub2.unsubscribe(); inOrder1.verify(observer, never()).onNext(anyLong()); - inOrder1.verify(observer, times(1)).onCompleted(); + inOrder1.verify(observer, never()).onCompleted(); verify(observer, never()).onError(any(Throwable.class)); inOrder2.verify(observer2, never()).onNext(anyLong()); - inOrder2.verify(observer2, times(1)).onCompleted(); + inOrder2.verify(observer2, never()).onCompleted(); verify(observer2, never()).onError(any(Throwable.class)); } From d09964c45971eea631499b539228101bdd8c172a Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 25 Nov 2013 16:53:34 -0800 Subject: [PATCH 312/333] UnitTests demonstrating bugs --- .../java/rx/subjects/BehaviorSubjectTest.java | 46 +++++++++++++++++ .../java/rx/subjects/PublishSubjectTest.java | 49 +++++++++++++++++-- 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java index ac543b7a18..b4ce9d8ace 100644 --- a/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java @@ -19,9 +19,11 @@ import static org.mockito.Mockito.*; import org.junit.Test; +import org.mockito.InOrder; import org.mockito.Mockito; import rx.Observer; +import rx.Subscription; import rx.util.functions.Action1; import rx.util.functions.Func0; @@ -91,6 +93,50 @@ public void testCompleted() { assertCompletedObserver(aObserver); } + + @Test + public void testCompletedStopsEmittingData() { + BehaviorSubject channel = BehaviorSubject.createWithDefaultValue(2013); + @SuppressWarnings("unchecked") + Observer observerA = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer observerB = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer observerC = mock(Observer.class); + + InOrder inOrder = inOrder(observerA, observerB, observerC); + + Subscription a = channel.subscribe(observerA); + Subscription b = channel.subscribe(observerB); + + inOrder.verify(observerA).onNext(2013); + inOrder.verify(observerB).onNext(2013); + + channel.onNext(42); + + inOrder.verify(observerA).onNext(42); + inOrder.verify(observerB).onNext(42); + + a.unsubscribe(); + + channel.onNext(4711); + + inOrder.verify(observerA, never()).onNext(any()); + inOrder.verify(observerB).onNext(4711); + + channel.onCompleted(); + + inOrder.verify(observerA, never()).onCompleted(); + inOrder.verify(observerB).onCompleted(); + + Subscription c = channel.subscribe(observerC); + + inOrder.verify(observerC).onCompleted(); + + channel.onNext(13); + + inOrder.verifyNoMoreInteractions(); + } private void assertCompletedObserver(Observer aObserver) { verify(aObserver, times(1)).onNext("default"); diff --git a/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java index 9e93c89e01..3de6d15d54 100644 --- a/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java +++ b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java @@ -1,12 +1,12 @@ /** * Copyright 2013 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 - * + * + * 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. @@ -117,6 +117,47 @@ public void testCompleted() { // todo bug? assertNeverObserver(anotherObserver); } + @Test + public void testCompletedStopsEmittingData() { + PublishSubject channel = PublishSubject.create(); + @SuppressWarnings("unchecked") + Observer observerA = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer observerB = mock(Observer.class); + @SuppressWarnings("unchecked") + Observer observerC = mock(Observer.class); + + InOrder inOrder = inOrder(observerA, observerB, observerC); + + Subscription a = channel.subscribe(observerA); + Subscription b = channel.subscribe(observerB); + + channel.onNext(42); + + inOrder.verify(observerA).onNext(42); + inOrder.verify(observerB).onNext(42); + + a.unsubscribe(); + + channel.onNext(4711); + + inOrder.verify(observerA, never()).onNext(any()); + inOrder.verify(observerB).onNext(4711); + + channel.onCompleted(); + + inOrder.verify(observerA, never()).onCompleted(); + inOrder.verify(observerB).onCompleted(); + + Subscription c = channel.subscribe(observerC); + + inOrder.verify(observerC).onCompleted(); + + channel.onNext(13); + + inOrder.verifyNoMoreInteractions(); + } + private void assertCompletedObserver(Observer aObserver) { verify(aObserver, times(1)).onNext("one"); verify(aObserver, times(1)).onNext("two"); From 56d1ff69635b65c8dba24f4d0a0e8586af0bde21 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 25 Nov 2013 17:20:04 -0800 Subject: [PATCH 313/333] Make unsubscribe idempotent. --- .../src/main/scala/rx/lang/scala/Subscription.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala index 3de9b8262b..6826b03e94 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala @@ -64,7 +64,7 @@ object Subscription { def isUnsubscribed = unsubscribed.get() val asJavaSubscription = new rx.Subscription { - def unsubscribe() { u; unsubscribed.set(true) } + def unsubscribe() { if(unsubscribed.get()) { u ; unsubscribed.set(true) }} } } } From 18e49bb2724649fc431d33f266bb4f9fefc4dd4a Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 25 Nov 2013 17:21:27 -0800 Subject: [PATCH 314/333] Make unsubscribe idempotent. --- .../rx/lang/scala/subscriptions/BooleanSubscription.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala index 246c68db21..c65049a12d 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala @@ -17,8 +17,10 @@ object BooleanSubscription { def apply(u: => Unit): BooleanSubscription = { new BooleanSubscription(new rx.subscriptions.BooleanSubscription { override def unsubscribe(): Unit = { - u - super.unsubscribe() + if(!super.isUnsubscribed()) { + u + super.unsubscribe() + } } }) } From 564fba0fbac16bb50e998cca30e2576e448705de Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 25 Nov 2013 21:41:07 -0800 Subject: [PATCH 315/333] BugFix: Handling of Terminal State for Behavior/Publish Subjects - They were not correctly emitting onCompleted when new Observers subscribed after the Subject was terminated. - Added same logic that already existed on AsyncSubject --- .../java/rx/subjects/AbstractSubject.java | 152 ++++++++++++++++++ .../main/java/rx/subjects/AsyncSubject.java | 124 ++++---------- .../java/rx/subjects/BehaviorSubject.java | 89 +++++----- .../main/java/rx/subjects/PublishSubject.java | 86 +++------- .../java/rx/subjects/BehaviorSubjectTest.java | 72 ++++----- .../java/rx/subjects/PublishSubjectTest.java | 117 ++------------ 6 files changed, 295 insertions(+), 345 deletions(-) create mode 100644 rxjava-core/src/main/java/rx/subjects/AbstractSubject.java diff --git a/rxjava-core/src/main/java/rx/subjects/AbstractSubject.java b/rxjava-core/src/main/java/rx/subjects/AbstractSubject.java new file mode 100644 index 0000000000..62c015c027 --- /dev/null +++ b/rxjava-core/src/main/java/rx/subjects/AbstractSubject.java @@ -0,0 +1,152 @@ +package rx.subjects; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; + +import rx.Notification; +import rx.Observer; +import rx.Subscription; +import rx.operators.SafeObservableSubscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action2; + +public abstract class AbstractSubject extends Subject { + + protected AbstractSubject(rx.Observable.OnSubscribeFunc onSubscribe) { + super(onSubscribe); + } + + protected static class SubjectState { + protected final ConcurrentHashMap> observers = new ConcurrentHashMap>(); + protected final AtomicReference> currentValue = new AtomicReference>(); + protected final AtomicBoolean completed = new AtomicBoolean(); + protected final ReentrantLock SUBSCRIPTION_LOCK = new ReentrantLock(); + } + + protected static OnSubscribeFunc getOnSubscribeFunc(final SubjectState state, final Action2, Observer> onEach) { + return new OnSubscribeFunc() { + @Override + public Subscription onSubscribe(Observer observer) { + /* + * Subscription needs to be synchronized with terminal states to ensure + * race conditions are handled. When subscribing we must make sure + * onComplete/onError is correctly emitted to all observers, even if it + * comes in while the onComplete/onError is being propagated. + */ + state.SUBSCRIPTION_LOCK.lock(); + try { + if (state.completed.get()) { + emitNotification(state.currentValue.get(), observer); + if (onEach != null) { + onEach.call(state, observer); + } + return Subscriptions.empty(); + } else { + // the subject is not completed so we subscribe + final SafeObservableSubscription subscription = new SafeObservableSubscription(); + + subscription.wrap(new Subscription() { + @Override + public void unsubscribe() { + // on unsubscribe remove it from the map of outbound observers to notify + state.observers.remove(subscription); + } + }); + + // on subscribe add it to the map of outbound observers to notify + state.observers.put(subscription, observer); + + // invoke onSubscribe logic + if (onEach != null) { + onEach.call(state, observer); + } + + return subscription; + } + } finally { + state.SUBSCRIPTION_LOCK.unlock(); + } + + } + + }; + } + + protected static void emitNotification(Notification value, Observer observer) { + // if null that means onNext was never invoked (no Notification set) + if (value != null) { + if (value.isOnNext()) { + observer.onNext(value.getValue()); + } else if (value.isOnError()) { + observer.onError(value.getThrowable()); + } else if (value.isOnCompleted()) { + observer.onCompleted(); + } + } + } + + /** + * Emit the current value. + * + * @param state + */ + protected static void emitNotification(final SubjectState state, final Action2, Observer> onEach) { + for (Subscription s : snapshotOfObservers(state)) { + Observer o = state.observers.get(s); + // emit notifications to this observer + emitNotification(state.currentValue.get(), o); + // onEach action if applicable + if (onEach != null) { + onEach.call(state, o); + } + } + } + + /** + * Emit the current value to all observers and remove their subscription. + * + * @param state + */ + protected void emitNotificationAndTerminate(final SubjectState state, final Action2, Observer> onEach) { + /* + * We can not allow new subscribers to be added while we execute the terminal state. + */ + state.SUBSCRIPTION_LOCK.lock(); + try { + if (state.completed.compareAndSet(false, true)) { + for (Subscription s : snapshotOfObservers(state)) { + Observer o = state.observers.get(s); + // emit notifications to this observer + emitNotification(state.currentValue.get(), o); + // onEach action if applicable + if (onEach != null) { + onEach.call(state, o); + } + + // remove the subscription as it is completed + state.observers.remove(s); + } + } + } finally { + state.SUBSCRIPTION_LOCK.unlock(); + } + } + + /** + * Current snapshot of 'state.observers.keySet()' so that concurrent modifications aren't included. + * + * This makes it behave deterministically in a single-threaded execution when nesting subscribes. + * + * In multi-threaded execution it will cause new subscriptions to wait until the following onNext instead + * of possibly being included in the current onNext iteration. + * + * @return List> + */ + private static Collection snapshotOfObservers(final SubjectState state) { + return new ArrayList(state.observers.keySet()); + } +} diff --git a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java index 3f7f5d0a0f..a9c18be805 100644 --- a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java @@ -15,16 +15,9 @@ */ package rx.subjects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.ReentrantLock; - import rx.Notification; import rx.Observer; -import rx.Subscription; -import rx.operators.SafeObservableSubscription; -import rx.subscriptions.Subscriptions; +import rx.util.functions.Action2; /** * Subject that publishes only the last event to each {@link Observer} that has subscribed when the @@ -55,7 +48,7 @@ * * @param */ -public class AsyncSubject extends Subject { +public class AsyncSubject extends AbstractSubject { /** * Create a new AsyncSubject @@ -63,115 +56,60 @@ public class AsyncSubject extends Subject { * @return a new AsyncSubject */ public static AsyncSubject create() { - final AsyncSubjectState state = new AsyncSubjectState(); + final SubjectState state = new SubjectState(); + OnSubscribeFunc onSubscribe = getOnSubscribeFunc(state, new Action2, Observer>() { - OnSubscribeFunc onSubscribe = new OnSubscribeFunc() { @Override - public Subscription onSubscribe(Observer observer) { - /* - * Subscription needs to be synchronized with terminal states to ensure - * race conditions are handled. When subscribing we must make sure - * onComplete/onError is correctly emitted to all observers, even if it - * comes in while the onComplete/onError is being propagated. - */ - state.SUBSCRIPTION_LOCK.lock(); - try { - if (state.completed.get()) { - emitNotificationToObserver(state, observer); - return Subscriptions.empty(); - } else { - // the subject is not completed so we subscribe - final SafeObservableSubscription subscription = new SafeObservableSubscription(); - - subscription.wrap(new Subscription() { - @Override - public void unsubscribe() { - // on unsubscribe remove it from the map of outbound observers to notify - state.observers.remove(subscription); - } - }); - - // on subscribe add it to the map of outbound observers to notify - state.observers.put(subscription, observer); - - return subscription; + public void call(SubjectState state, Observer o) { + // we want the last value + completed so add this extra logic + // to send onCompleted if the last value is an onNext + if (state.completed.get()) { + Notification value = state.currentValue.get(); + if (value != null && value.isOnNext()) { + o.onCompleted(); } - } finally { - state.SUBSCRIPTION_LOCK.unlock(); } - } - - }; - + }); return new AsyncSubject(onSubscribe, state); } - private static void emitNotificationToObserver(final AsyncSubjectState state, Observer observer) { - Notification finalValue = state.currentValue.get(); - - // if null that means onNext was never invoked (no Notification set) - if (finalValue != null) { - if (finalValue.isOnNext()) { - observer.onNext(finalValue.getValue()); - } else if (finalValue.isOnError()) { - observer.onError(finalValue.getThrowable()); - } - } - observer.onCompleted(); - } - - /** - * State externally constructed and passed in so the onSubscribe function has access to it. - * - * @param - */ - private static class AsyncSubjectState { - private final ConcurrentHashMap> observers = new ConcurrentHashMap>(); - private final AtomicReference> currentValue = new AtomicReference>(); - private final AtomicBoolean completed = new AtomicBoolean(); - private final ReentrantLock SUBSCRIPTION_LOCK = new ReentrantLock(); - } - - private final AsyncSubjectState state; + private final SubjectState state; - protected AsyncSubject(OnSubscribeFunc onSubscribe, AsyncSubjectState state) { + protected AsyncSubject(OnSubscribeFunc onSubscribe, SubjectState state) { super(onSubscribe); this.state = state; } @Override public void onCompleted() { - terminalState(); + /** + * Mark this subject as completed and emit latest value + 'onCompleted' to all Observers + */ + emitNotificationAndTerminate(state, new Action2, Observer>() { + + @Override + public void call(SubjectState state, Observer o) { + o.onCompleted(); + } + }); } @Override public void onError(Throwable e) { + /** + * Mark this subject as completed with an error as the last value and emit 'onError' to all Observers + */ state.currentValue.set(new Notification(e)); - terminalState(); + emitNotificationAndTerminate(state, null); } @Override public void onNext(T v) { + /** + * Store the latest value but do not send it. It only gets sent when 'onCompleted' occurs. + */ state.currentValue.set(new Notification(v)); } - private void terminalState() { - /* - * We can not allow new subscribers to be added while we execute the terminal state. - */ - state.SUBSCRIPTION_LOCK.lock(); - try { - if (state.completed.compareAndSet(false, true)) { - for (Subscription s : state.observers.keySet()) { - // emit notifications to this observer - emitNotificationToObserver(state, state.observers.get(s)); - // remove the subscription as it is completed - state.observers.remove(s); - } - } - } finally { - state.SUBSCRIPTION_LOCK.unlock(); - } - } } diff --git a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java index 55c91ed122..053a51f0cf 100644 --- a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java @@ -15,12 +15,9 @@ */ package rx.subjects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; - +import rx.Notification; import rx.Observer; -import rx.Subscription; -import rx.operators.SafeObservableSubscription; +import rx.util.functions.Action2; /** * Subject that publishes the most recent and all subsequent events to each subscribed {@link Observer}. @@ -50,7 +47,7 @@ * * @param */ -public class BehaviorSubject extends Subject { +public class BehaviorSubject extends AbstractSubject { /** * Creates a {@link BehaviorSubject} which publishes the last and all subsequent events to each {@link Observer} that subscribes to it. @@ -58,64 +55,68 @@ public class BehaviorSubject extends Subject { * @param defaultValue * The value which will be published to any {@link Observer} as long as the {@link BehaviorSubject} has not yet received any events. * @return the constructed {@link BehaviorSubject}. + * @deprecated Use {@link create()} instead. */ public static BehaviorSubject createWithDefaultValue(T defaultValue) { - final ConcurrentHashMap> observers = new ConcurrentHashMap>(); + return create(defaultValue); + } - final AtomicReference currentValue = new AtomicReference(defaultValue); + /** + * Creates a {@link BehaviorSubject} which publishes the last and all subsequent events to each {@link Observer} that subscribes to it. + * + * @param defaultValue + * The value which will be published to any {@link Observer} as long as the {@link BehaviorSubject} has not yet received any events. + * @return the constructed {@link BehaviorSubject}. + */ + public static BehaviorSubject create(T defaultValue) { + final SubjectState state = new SubjectState(); + // set a default value so subscriptions will immediately receive this until a new notification is received + state.currentValue.set(new Notification(defaultValue)); + OnSubscribeFunc onSubscribe = getOnSubscribeFunc(state, new Action2, Observer>() { - OnSubscribeFunc onSubscribe = new OnSubscribeFunc() { @Override - public Subscription onSubscribe(Observer observer) { - final SafeObservableSubscription subscription = new SafeObservableSubscription(); - - subscription.wrap(new Subscription() { - @Override - public void unsubscribe() { - // on unsubscribe remove it from the map of outbound observers to notify - observers.remove(subscription); - } - }); - - observer.onNext(currentValue.get()); - - // on subscribe add it to the map of outbound observers to notify - observers.put(subscription, observer); - return subscription; + public void call(SubjectState state, Observer o) { + /** + * When we subscribe we always emit the latest value to the observer, including + * terminal states which are recorded as the last value. + */ + emitNotification(state.currentValue.get(), o); } - }; - - return new BehaviorSubject(currentValue, onSubscribe, observers); + }); + return new BehaviorSubject(onSubscribe, state); } - private final ConcurrentHashMap> observers; - private final AtomicReference currentValue; + private final SubjectState state; - protected BehaviorSubject(AtomicReference currentValue, OnSubscribeFunc onSubscribe, ConcurrentHashMap> observers) { + protected BehaviorSubject(OnSubscribeFunc onSubscribe, SubjectState state) { super(onSubscribe); - this.currentValue = currentValue; - this.observers = observers; + this.state = state; } @Override public void onCompleted() { - for (Observer observer : observers.values()) { - observer.onCompleted(); - } + /** + * Mark this subject as completed and emit latest value + 'onCompleted' to all Observers + */ + state.currentValue.set(new Notification()); + emitNotificationAndTerminate(state, null); } @Override public void onError(Throwable e) { - for (Observer observer : observers.values()) { - observer.onError(e); - } + /** + * Mark this subject as completed with an error as the last value and emit 'onError' to all Observers + */ + state.currentValue.set(new Notification(e)); + emitNotificationAndTerminate(state, null); } @Override - public void onNext(T args) { - currentValue.set(args); - for (Observer observer : observers.values()) { - observer.onNext(args); - } + public void onNext(T v) { + /** + * Store the latest value and send it to all observers; + */ + state.currentValue.set(new Notification(v)); + emitNotification(state, null); } } diff --git a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java index fd32bd6e42..8cf2d75747 100644 --- a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java @@ -15,13 +15,8 @@ */ package rx.subjects; -import java.util.ArrayList; -import java.util.Collection; -import java.util.concurrent.ConcurrentHashMap; - +import rx.Notification; import rx.Observer; -import rx.Subscription; -import rx.operators.SafeObservableSubscription; /** * Subject that, once and {@link Observer} has subscribed, publishes all subsequent events to the subscriber. @@ -32,7 +27,7 @@ *

    *

     {@code
     
    - * ublishSubject subject = PublishSubject.create();
    + * PublishSubject subject = PublishSubject.create();
       // observer1 will receive all onNext and onCompleted events
       subject.subscribe(observer1);
       subject.onNext("one");
    @@ -46,75 +41,44 @@
      * 
      * @param 
      */
    -public class PublishSubject extends Subject {
    +public class PublishSubject extends AbstractSubject {
         public static  PublishSubject create() {
    -        final ConcurrentHashMap> observers = new ConcurrentHashMap>();
    -
    -        OnSubscribeFunc onSubscribe = new OnSubscribeFunc() {
    -            @Override
    -            public Subscription onSubscribe(Observer observer) {
    -                final SafeObservableSubscription subscription = new SafeObservableSubscription();
    -
    -                subscription.wrap(new Subscription() {
    -                    @Override
    -                    public void unsubscribe() {
    -                        // on unsubscribe remove it from the map of outbound observers to notify
    -                        observers.remove(subscription);
    -                    }
    -                });
    -
    -                // on subscribe add it to the map of outbound observers to notify
    -                observers.put(subscription, observer);
    -
    -                return subscription;
    -            }
    -
    -        };
    -
    -        return new PublishSubject(onSubscribe, observers);
    +        final SubjectState state = new SubjectState();
    +        OnSubscribeFunc onSubscribe = getOnSubscribeFunc(state, null);
    +        return new PublishSubject(onSubscribe, state);
         }
     
    -    private final ConcurrentHashMap> observers;
    +    private final SubjectState state;
     
    -    protected PublishSubject(OnSubscribeFunc onSubscribe, ConcurrentHashMap> observers) {
    +    protected PublishSubject(OnSubscribeFunc onSubscribe, SubjectState state) {
             super(onSubscribe);
    -        this.observers = observers;
    +        this.state = state;
         }
     
         @Override
         public void onCompleted() {
    -        for (Observer observer : snapshotOfValues()) {
    -            observer.onCompleted();
    -        }
    -        observers.clear();
    +        /**
    +         * Mark this subject as completed and emit latest value + 'onCompleted' to all Observers
    +         */
    +        state.currentValue.set(new Notification());
    +        emitNotificationAndTerminate(state, null);
         }
     
         @Override
         public void onError(Throwable e) {
    -        for (Observer observer : snapshotOfValues()) {
    -            observer.onError(e);
    -        }
    -        observers.clear();
    +        /**
    +         * Mark this subject as completed with an error as the last value and emit 'onError' to all Observers
    +         */
    +        state.currentValue.set(new Notification(e));
    +        emitNotificationAndTerminate(state, null);
         }
     
         @Override
    -    public void onNext(T args) {
    -        for (Observer observer : snapshotOfValues()) {
    -            observer.onNext(args);
    -        }
    -    }
    -
    -    /**
    -     * Current snapshot of 'values()' so that concurrent modifications aren't included.
    -     * 
    -     * This makes it behave deterministically in a single-threaded execution when nesting subscribes.
    -     * 
    -     * In multi-threaded execution it will cause new subscriptions to wait until the following onNext instead
    -     * of possibly being included in the current onNext iteration.
    -     * 
    -     * @return List>
    -     */
    -    private Collection> snapshotOfValues() {
    -        return new ArrayList>(observers.values());
    +    public void onNext(T v) {
    +        /**
    +         * Store the latest value and send it to all observers;
    +         */
    +        state.currentValue.set(new Notification(v));
    +        emitNotification(state, null);
         }
     }
    diff --git a/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java
    index b4ce9d8ace..57958c108f 100644
    --- a/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java
    +++ b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java
    @@ -1,12 +1,12 @@
     /**
      * Copyright 2013 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
    - *
    + * 
    + * 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.
    @@ -43,10 +43,6 @@ public void testThatObserverReceivesDefaultValueIfNothingWasPublished() {
             subject.onNext("two");
             subject.onNext("three");
     
    -        assertReceivedAllEvents(aObserver);
    -    }
    -
    -    private void assertReceivedAllEvents(Observer aObserver) {
             verify(aObserver, times(1)).onNext("default");
             verify(aObserver, times(1)).onNext("one");
             verify(aObserver, times(1)).onNext("two");
    @@ -57,7 +53,7 @@ private void assertReceivedAllEvents(Observer aObserver) {
     
         @Test
         public void testThatObserverDoesNotReceiveDefaultValueIfSomethingWasPublished() {
    -        BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default");
    +        BehaviorSubject subject = BehaviorSubject.create("default");
     
             subject.onNext("one");
     
    @@ -68,10 +64,6 @@ public void testThatObserverDoesNotReceiveDefaultValueIfSomethingWasPublished()
             subject.onNext("two");
             subject.onNext("three");
     
    -        assertDidNotReceiveTheDefaultValue(aObserver);
    -    }
    -
    -    private void assertDidNotReceiveTheDefaultValue(Observer aObserver) {
             verify(aObserver, Mockito.never()).onNext("default");
             verify(aObserver, times(1)).onNext("one");
             verify(aObserver, times(1)).onNext("two");
    @@ -82,7 +74,7 @@ private void assertDidNotReceiveTheDefaultValue(Observer aObserver) {
     
         @Test
         public void testCompleted() {
    -        BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default");
    +        BehaviorSubject subject = BehaviorSubject.create("default");
     
             @SuppressWarnings("unchecked")
             Observer aObserver = mock(Observer.class);
    @@ -91,12 +83,15 @@ public void testCompleted() {
             subject.onNext("one");
             subject.onCompleted();
     
    -        assertCompletedObserver(aObserver);
    +        verify(aObserver, times(1)).onNext("default");
    +        verify(aObserver, times(1)).onNext("one");
    +        verify(aObserver, Mockito.never()).onError(any(Throwable.class));
    +        verify(aObserver, times(1)).onCompleted();
         }
    -    
    +
         @Test
         public void testCompletedStopsEmittingData() {
    -        BehaviorSubject channel = BehaviorSubject.createWithDefaultValue(2013);
    +        BehaviorSubject channel = BehaviorSubject.create(2013);
             @SuppressWarnings("unchecked")
             Observer observerA = mock(Observer.class);
             @SuppressWarnings("unchecked")
    @@ -104,50 +99,45 @@ public void testCompletedStopsEmittingData() {
             @SuppressWarnings("unchecked")
             Observer observerC = mock(Observer.class);
     
    -        InOrder inOrder = inOrder(observerA, observerB, observerC);
    -
             Subscription a = channel.subscribe(observerA);
             Subscription b = channel.subscribe(observerB);
     
    -        inOrder.verify(observerA).onNext(2013);
    -        inOrder.verify(observerB).onNext(2013);
    -        
    +        InOrder inOrderA = inOrder(observerA);
    +        InOrder inOrderB = inOrder(observerB);
    +        InOrder inOrderC = inOrder(observerC);
    +
    +        inOrderA.verify(observerA).onNext(2013);
    +        inOrderB.verify(observerB).onNext(2013);
    +
             channel.onNext(42);
     
    -        inOrder.verify(observerA).onNext(42);
    -        inOrder.verify(observerB).onNext(42);
    +        inOrderA.verify(observerA).onNext(42);
    +        inOrderB.verify(observerB).onNext(42);
     
             a.unsubscribe();
    +        inOrderA.verifyNoMoreInteractions();
     
             channel.onNext(4711);
     
    -        inOrder.verify(observerA, never()).onNext(any());
    -        inOrder.verify(observerB).onNext(4711);
    +        inOrderB.verify(observerB).onNext(4711);
     
             channel.onCompleted();
     
    -        inOrder.verify(observerA, never()).onCompleted();
    -        inOrder.verify(observerB).onCompleted();
    +        inOrderB.verify(observerB).onCompleted();
     
             Subscription c = channel.subscribe(observerC);
     
    -        inOrder.verify(observerC).onCompleted();
    +        inOrderC.verify(observerC).onCompleted();
     
             channel.onNext(13);
     
    -        inOrder.verifyNoMoreInteractions();
    -    }
    -
    -    private void assertCompletedObserver(Observer aObserver) {
    -        verify(aObserver, times(1)).onNext("default");
    -        verify(aObserver, times(1)).onNext("one");
    -        verify(aObserver, Mockito.never()).onError(any(Throwable.class));
    -        verify(aObserver, times(1)).onCompleted();
    +        inOrderB.verifyNoMoreInteractions();
    +        inOrderC.verifyNoMoreInteractions();
         }
     
         @Test
         public void testCompletedAfterError() {
    -        BehaviorSubject subject = BehaviorSubject.createWithDefaultValue("default");
    +        BehaviorSubject subject = BehaviorSubject.create("default");
     
             @SuppressWarnings("unchecked")
             Observer aObserver = mock(Observer.class);
    @@ -158,10 +148,6 @@ public void testCompletedAfterError() {
             subject.onNext("two");
             subject.onCompleted();
     
    -        assertErrorObserver(aObserver);
    -    }
    -
    -    private void assertErrorObserver(Observer aObserver) {
             verify(aObserver, times(1)).onNext("default");
             verify(aObserver, times(1)).onNext("one");
             verify(aObserver, times(1)).onError(testException);
    @@ -173,7 +159,7 @@ public void testUnsubscribe() {
                     new Func0>() {
                         @Override
                         public BehaviorSubject call() {
    -                        return BehaviorSubject.createWithDefaultValue("default");
    +                        return BehaviorSubject.create("default");
                         }
                     }, new Action1>() {
                         @Override
    diff --git a/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java
    index 3de6d15d54..5e9cc2790f 100644
    --- a/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java
    +++ b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java
    @@ -20,18 +20,12 @@
     import static org.mockito.Mockito.*;
     
     import java.util.ArrayList;
    -import java.util.List;
    -import java.util.concurrent.atomic.AtomicBoolean;
     import java.util.concurrent.atomic.AtomicInteger;
    -import java.util.concurrent.atomic.AtomicReference;
    -
    -import junit.framework.Assert;
     
     import org.junit.Test;
     import org.mockito.InOrder;
     import org.mockito.Mockito;
     
    -import rx.Notification;
     import rx.Observable;
     import rx.Observer;
     import rx.Subscription;
    @@ -41,57 +35,6 @@
     
     public class PublishSubjectTest {
     
    -    @Test
    -    public void test() {
    -        PublishSubject subject = PublishSubject.create();
    -        final AtomicReference>> actualRef = new AtomicReference>>();
    -
    -        Observable>> wNotificationsList = subject.materialize().toList();
    -        wNotificationsList.subscribe(new Action1>>() {
    -            @Override
    -            public void call(List> actual) {
    -                actualRef.set(actual);
    -            }
    -        });
    -
    -        Subscription sub = Observable.create(new Observable.OnSubscribeFunc() {
    -            @Override
    -            public Subscription onSubscribe(final Observer observer) {
    -                final AtomicBoolean stop = new AtomicBoolean(false);
    -                new Thread() {
    -                    @Override
    -                    public void run() {
    -                        int i = 1;
    -                        while (!stop.get()) {
    -                            observer.onNext(i++);
    -                        }
    -                        observer.onCompleted();
    -                    }
    -                }.start();
    -                return new Subscription() {
    -                    @Override
    -                    public void unsubscribe() {
    -                        stop.set(true);
    -                    }
    -                };
    -            }
    -        }).subscribe(subject);
    -        // the subject has received an onComplete from the first subscribe because
    -        // it is synchronous and the next subscribe won't do anything.
    -        Observable.from(-1, -2, -3).subscribe(subject);
    -
    -        List> expected = new ArrayList>();
    -        expected.add(new Notification(-1));
    -        expected.add(new Notification(-2));
    -        expected.add(new Notification(-3));
    -        expected.add(new Notification());
    -        Assert.assertTrue(actualRef.get().containsAll(expected));
    -
    -        sub.unsubscribe();
    -    }
    -
    -    private final Throwable testException = new Throwable();
    -
         @Test
         public void testCompleted() {
             PublishSubject subject = PublishSubject.create();
    @@ -127,35 +70,37 @@ public void testCompletedStopsEmittingData() {
             @SuppressWarnings("unchecked")
             Observer observerC = mock(Observer.class);
     
    -        InOrder inOrder = inOrder(observerA, observerB, observerC);
    -
             Subscription a = channel.subscribe(observerA);
             Subscription b = channel.subscribe(observerB);
     
    +        InOrder inOrderA = inOrder(observerA);
    +        InOrder inOrderB = inOrder(observerB);
    +        InOrder inOrderC = inOrder(observerC);
    +
             channel.onNext(42);
     
    -        inOrder.verify(observerA).onNext(42);
    -        inOrder.verify(observerB).onNext(42);
    +        inOrderA.verify(observerA).onNext(42);
    +        inOrderB.verify(observerB).onNext(42);
     
             a.unsubscribe();
    +        inOrderA.verifyNoMoreInteractions();
     
             channel.onNext(4711);
     
    -        inOrder.verify(observerA, never()).onNext(any());
    -        inOrder.verify(observerB).onNext(4711);
    +        inOrderB.verify(observerB).onNext(4711);
     
             channel.onCompleted();
     
    -        inOrder.verify(observerA, never()).onCompleted();
    -        inOrder.verify(observerB).onCompleted();
    +        inOrderB.verify(observerB).onCompleted();
     
             Subscription c = channel.subscribe(observerC);
     
    -        inOrder.verify(observerC).onCompleted();
    +        inOrderC.verify(observerC).onCompleted();
     
             channel.onNext(13);
     
    -        inOrder.verifyNoMoreInteractions();
    +        inOrderB.verifyNoMoreInteractions();
    +        inOrderC.verifyNoMoreInteractions();
         }
     
         private void assertCompletedObserver(Observer aObserver) {
    @@ -380,43 +325,7 @@ public void testReSubscribe() {
             s2.unsubscribe();
         }
     
    -    /**
    -     * Even if subject received an onError/onCompleted, new subscriptions should be able to restart it.
    -     */
    -    @Test
    -    public void testReSubscribeAfterTerminalState() {
    -        final PublishSubject ps = PublishSubject.create();
    -
    -        Observer o1 = mock(Observer.class);
    -        Subscription s1 = ps.subscribe(o1);
    -
    -        // emit
    -        ps.onNext(1);
    -
    -        // validate we got it
    -        InOrder inOrder1 = inOrder(o1);
    -        inOrder1.verify(o1, times(1)).onNext(1);
    -        inOrder1.verifyNoMoreInteractions();
    -
    -        // unsubscribe
    -        s1.unsubscribe();
    -
    -        ps.onCompleted();
    -
    -        // emit again but nothing will be there to receive it
    -        ps.onNext(2);
     
    -        Observer o2 = mock(Observer.class);
    -        Subscription s2 = ps.subscribe(o2);
    -
    -        // emit
    -        ps.onNext(3);
    -
    -        // validate we got it
    -        InOrder inOrder2 = inOrder(o2);
    -        inOrder2.verify(o2, times(1)).onNext(3);
    -        inOrder2.verifyNoMoreInteractions();
    +    private final Throwable testException = new Throwable();
     
    -        s2.unsubscribe();
    -    }
     }
    
    From 02e584f5803cc1a5a6c5c2dba6c580aebc9950cd Mon Sep 17 00:00:00 2001
    From: David Gross 
    Date: Tue, 26 Nov 2013 09:45:59 -0800
    Subject: [PATCH 316/333] Javadoc improvements, including diagrams and wiki
     links for new operators
    
    ---
     rxjava-core/src/main/java/rx/Observable.java | 676 ++++++++++---------
     1 file changed, 351 insertions(+), 325 deletions(-)
    
    diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java
    index 1f33710e95..0cef17b1d3 100644
    --- a/rxjava-core/src/main/java/rx/Observable.java
    +++ b/rxjava-core/src/main/java/rx/Observable.java
    @@ -513,7 +513,7 @@ public Subscription subscribe(final Action1 onNext, final Action1Observable.publish() and Observable.multicast()
    +     * @see RxJava Wiki: Observable.publish() and Observable.multicast()
          */
         public  ConnectableObservable multicast(Subject subject) {
             return OperationMulticast.multicast(this, subject);
    @@ -604,7 +604,7 @@ public Subscription onSubscribe(Observer observer) {
          *             allow the Observer to cancel the subscription
          * @return an Observable that, when an {@link Observer} subscribes to it,
          *         will execute the given function
    -     * @see create()
    +     * @see RxJava Wiki: create()
          */
         public static  Observable create(OnSubscribeFunc func) {
             return new Observable(func);
    @@ -620,7 +620,7 @@ public static  Observable create(OnSubscribeFunc func) {
          * @return an Observable that returns no data to the {@link Observer} and
          *         immediately invokes the {@link Observer}'s
          *         {@link Observer#onCompleted() onCompleted} method
    -     * @see empty()
    +     * @see RxJava Wiki: empty()
          * @see MSDN: Observable.Empty Method
          */
         public static  Observable empty() {
    @@ -641,7 +641,7 @@ public static  Observable empty() {
          *         immediately invokes the {@link Observer}'s
          *         {@link Observer#onCompleted() onCompleted} method with the
          *         specified scheduler
    -     * @see empty()
    +     * @see RxJava Wiki: empty()
          * @see MSDN: Observable.Empty Method (IScheduler)
          */
         public static  Observable empty(Scheduler scheduler) {
    @@ -660,7 +660,7 @@ public static  Observable empty(Scheduler scheduler) {
          * @return an Observable that invokes the {@link Observer}'s
          *         {@link Observer#onError onError} method when the Observer
          *         subscribes to it
    -     * @see error()
    +     * @see RxJava Wiki: error()
          * @see MSDN: Observable.Throw Method
          */
         public static  Observable error(Throwable exception) {
    @@ -680,7 +680,7 @@ public static  Observable error(Throwable exception) {
          * @return an Observable that invokes the {@link Observer}'s
          *         {@link Observer#onError onError} method with the specified
          *         scheduler
    -     * @see error()
    +     * @see RxJava Wiki: error()
          * @see MSDN: Observable.Throw Method
          */
         public static  Observable error(Throwable exception, Scheduler scheduler) {
    @@ -702,7 +702,7 @@ public static  Observable error(Throwable exception, Scheduler scheduler)
          *            type of items to be emitted by the resulting Observable
          * @return an Observable that emits each item in the source {@link Iterable}
          *         sequence
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         public static  Observable from(Iterable iterable) {
             return create(OperationToObservableIterable.toObservableIterable(iterable));
    @@ -719,7 +719,7 @@ public static  Observable from(Iterable iterable) {
          *            type of items to be emitted by the resulting Observable
          * @return an Observable that emits each item in the source {@link Iterable}
          *         sequence with the specified scheduler
    -     * @see from()
    +     * @see RxJava Wiki: from()
          * @see MSDN: Observable.ToObservable
          */
         public static  Observable from(Iterable iterable, Scheduler scheduler) {
    @@ -740,7 +740,7 @@ public static  Observable from(Iterable iterable, Scheduler s
          * @param  the type of items in the Array and the type of items to be
          *            emitted by the resulting Observable
          * @return an Observable that emits each item in the source Array
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         public static  Observable from(T[] items) {
             return create(OperationToObservableIterable.toObservableIterable(Arrays.asList(items)));
    @@ -760,7 +760,7 @@ public static  Observable from(T[] items) {
          * @param  the type of the item, and the type of the item to be
          *            emitted by the resulting Observable
          * @return an Observable that emits the item
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         @SuppressWarnings("unchecked")
         // suppress unchecked because we are using varargs inside the method
    @@ -783,7 +783,7 @@ public static  Observable from(T t1) {
          * @param  the type of items, and the type of items to be emitted by the
          *            resulting Observable
          * @return an Observable that emits each item
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         @SuppressWarnings("unchecked")
         // suppress unchecked because we are using varargs inside the method
    @@ -807,7 +807,7 @@ public static  Observable from(T t1, T t2) {
          * @param  the type of items, and the type of items to be emitted by the
          *            resulting Observable
          * @return an Observable that emits each item
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         @SuppressWarnings("unchecked")
         // suppress unchecked because we are using varargs inside the method
    @@ -832,7 +832,7 @@ public static  Observable from(T t1, T t2, T t3) {
          * @param  the type of items, and the type of items to be emitted by the
          *            resulting Observable
          * @return an Observable that emits each item
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         @SuppressWarnings("unchecked")
         // suppress unchecked because we are using varargs inside the method
    @@ -858,7 +858,7 @@ public static  Observable from(T t1, T t2, T t3, T t4) {
          * @param  the type of items, and the type of items to be emitted by the
          *            resulting Observable
          * @return an Observable that emits each item
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         @SuppressWarnings("unchecked")
         // suppress unchecked because we are using varargs inside the method
    @@ -885,7 +885,7 @@ public static  Observable from(T t1, T t2, T t3, T t4, T t5) {
          * @param  the type of items, and the type of items to be emitted by the
          *            resulting Observable
          * @return an Observable that emits each item
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         @SuppressWarnings("unchecked")
         // suppress unchecked because we are using varargs inside the method
    @@ -913,7 +913,7 @@ public static  Observable from(T t1, T t2, T t3, T t4, T t5, T t6) {
          * @param  the type of items, and the type of items to be emitted by the
          *            resulting Observable
          * @return an Observable that emits each item
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         @SuppressWarnings("unchecked")
         // suppress unchecked because we are using varargs inside the method
    @@ -942,7 +942,7 @@ public static  Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7) {
          * @param  the type of items, and the type of items to be emitted by the
          *            resulting Observable
          * @return an Observable that emits each item
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         @SuppressWarnings("unchecked")
         // suppress unchecked because we are using varargs inside the method
    @@ -972,7 +972,7 @@ public static  Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T
          * @param  the type of items, and the type of items to be emitted by the
          *            resulting Observable
          * @return an Observable that emits each item
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         @SuppressWarnings("unchecked")
         // suppress unchecked because we are using varargs inside the method
    @@ -1003,7 +1003,7 @@ public static  Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T
          * @param  the type of items, and the type of items to be emitted by the
          *            resulting Observable
          * @return an Observable that emits each item
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         @SuppressWarnings("unchecked")
         // suppress unchecked because we are using varargs inside the method
    @@ -1025,7 +1025,7 @@ public static  Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T
          * @param start the value of the first integer in the sequence
          * @param count the number of sequential integers to generate
          * @return an Observable that emits a range of sequential integers
    -     * @see range()
    +     * @see RxJava Wiki: range()
          * @see Observable.Range Method (Int32, Int32)
          */
         public static Observable range(int start, int count) {
    @@ -1041,7 +1041,7 @@ public static Observable range(int start, int count) {
          * @param count the number of sequential integers to generate
          * @param scheduler the scheduler to run the generator loop on
          * @return an Observable that emits a range of sequential integers
    -     * @see range()
    +     * @see RxJava Wiki: range()
          * @see Observable.Range Method (Int32, Int32, IScheduler)
          */
         public static Observable range(int start, int count, Scheduler scheduler) {
    @@ -1066,7 +1066,7 @@ public static Observable range(int start, int count, Scheduler schedule
          * @param  the type of the items emitted by the Observable
          * @return an Observable whose {@link Observer}s trigger an invocation of
          *         the given Observable factory function
    -     * @see defer()
    +     * @see RxJava Wiki: defer()
          */
         public static  Observable defer(Func0> observableFactory) {
             return create(OperationDefer.defer(observableFactory));
    @@ -1090,7 +1090,7 @@ public static  Observable defer(Func0> o
          *              {@link Observer#onNext onNext} method
          * @param  the type of that item
          * @return an Observable that emits a single item and then completes
    -     * @see just()
    +     * @see RxJava Wiki: just()
          */
         public static  Observable just(T value) {
             List list = new ArrayList();
    @@ -1111,7 +1111,7 @@ public static  Observable just(T value) {
          * @param scheduler the scheduler to send the single element on
          * @return an Observable that emits a single item and then completes on a
          *         specified scheduler
    -     * @see just()
    +     * @see RxJava Wiki: just()
          */
         public static  Observable just(T value, Scheduler scheduler) {
             return just(value).observeOn(scheduler);
    @@ -1130,7 +1130,7 @@ public static  Observable just(T value, Scheduler scheduler) {
          * @return an Observable that emits items that are the result of flattening
          *         the items emitted by the Observables emitted by the
          *         {@code source} Observable
    -     * @see merge()
    +     * @see RxJava Wiki: merge()
          * @see MSDN: Observable.Merge Method
          */
         public static  Observable merge(Observable> source) {
    @@ -1150,7 +1150,7 @@ public static  Observable merge(Observablemerge()
    +     * @see RxJava Wiki: merge()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1173,7 +1173,7 @@ public static  Observable merge(Observable t1, Observablemerge()
    +     * @see RxJava Wiki: merge()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1197,7 +1197,7 @@ public static  Observable merge(Observable t1, Observablemerge()
    +     * @see RxJava Wiki: merge()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1222,7 +1222,7 @@ public static  Observable merge(Observable t1, Observablemerge()
    +     * @see RxJava Wiki: merge()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1248,7 +1248,7 @@ public static  Observable merge(Observable t1, Observablemerge()
    +     * @see RxJava Wiki: merge()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1275,7 +1275,7 @@ public static  Observable merge(Observable t1, Observablemerge()
    +     * @see RxJava Wiki: merge()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1303,7 +1303,7 @@ public static  Observable merge(Observable t1, Observablemerge()
    +     * @see RxJava Wiki: merge()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1332,7 +1332,7 @@ public static  Observable merge(Observable t1, Observablemerge()
    +     * @see RxJava Wiki: merge()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1351,7 +1351,7 @@ public static  Observable merge(Observable t1, Observableconcat()
    +     * @see RxJava Wiki: concat()
          * @see MSDN: Observable.Concat Method
          */
         public static  Observable concat(Observable> observables) {
    @@ -1369,7 +1369,7 @@ public static  Observable concat(Observableconcat()
    +     * @see RxJava Wiki: concat()
          * @see MSDN: Observable.Concat Method
          */
         @SuppressWarnings("unchecked")
    @@ -1391,7 +1391,7 @@ public static  Observable concat(Observable t1, Observableconcat()
    +     * @see RxJava Wiki: concat()
          * @see MSDN: Observable.Concat Method
          */
         @SuppressWarnings("unchecked")
    @@ -1413,7 +1413,7 @@ public static  Observable concat(Observable t1, Observableconcat()
    +     * @see RxJava Wiki: concat()
          * @see MSDN: Observable.Concat Method
          */
         @SuppressWarnings("unchecked")
    @@ -1436,7 +1436,7 @@ public static  Observable concat(Observable t1, Observableconcat()
    +     * @see RxJava Wiki: concat()
          * @see MSDN: Observable.Concat Method
          */
         @SuppressWarnings("unchecked")
    @@ -1460,7 +1460,7 @@ public static  Observable concat(Observable t1, Observableconcat()
    +     * @see RxJava Wiki: concat()
          * @see MSDN: Observable.Concat Method
          */
         @SuppressWarnings("unchecked")
    @@ -1485,7 +1485,7 @@ public static  Observable concat(Observable t1, Observableconcat()
    +     * @see RxJava Wiki: concat()
          * @see MSDN: Observable.Concat Method
          */
         @SuppressWarnings("unchecked")
    @@ -1511,7 +1511,7 @@ public static  Observable concat(Observable t1, Observableconcat()
    +     * @see RxJava Wiki: concat()
          * @see MSDN: Observable.Concat Method
          */
         @SuppressWarnings("unchecked")
    @@ -1538,7 +1538,7 @@ public static  Observable concat(Observable t1, Observableconcat()
    +     * @see RxJava Wiki: concat()
          * @see MSDN: Observable.Concat Method
          */
         @SuppressWarnings("unchecked")
    @@ -1568,7 +1568,7 @@ public static  Observable concat(Observable t1, ObservablemergeDelayError()
    +     * @see RxJava Wiki: mergeDelayError()
          * @see MSDN: Observable.Merge Method
          */
         public static  Observable mergeDelayError(Observable> source) {
    @@ -1596,7 +1596,7 @@ public static  Observable mergeDelayError(ObservablemergeDelayError()
    +     * @see RxJava Wiki: mergeDelayError()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1627,7 +1627,7 @@ public static  Observable mergeDelayError(Observable t1, Obse
          * @param t3 an Observable to be merged
          * @return an Observable that emits items that are the result of flattening
          *         the items emitted by the {@code source} Observables
    -     * @see mergeDelayError()
    +     * @see RxJava Wiki: mergeDelayError()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1660,7 +1660,7 @@ public static  Observable mergeDelayError(Observable t1, Obse
          * @param t4 an Observable to be merged
          * @return an Observable that emits items that are the result of flattening
          *         the items emitted by the {@code source} Observables
    -     * @see mergeDelayError()
    +     * @see RxJava Wiki: mergeDelayError()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1693,7 +1693,7 @@ public static  Observable mergeDelayError(Observable t1, Obse
          * @param t5 an Observable to be merged
          * @return an Observable that emits items that are the result of flattening
          *         the items emitted by the {@code source} Observables
    -     * @see mergeDelayError()
    +     * @see RxJava Wiki: mergeDelayError()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1727,7 +1727,7 @@ public static  Observable mergeDelayError(Observable t1, Obse
          * @param t6 an Observable to be merged
          * @return an Observable that emits items that are the result of flattening
          *         the items emitted by the {@code source} Observables
    -     * @see mergeDelayError()
    +     * @see RxJava Wiki: mergeDelayError()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1762,7 +1762,7 @@ public static  Observable mergeDelayError(Observable t1, Obse
          * @param t7 an Observable to be merged
          * @return an Observable that emits items that are the result of flattening
          *         the items emitted by the {@code source} Observables
    -     * @see mergeDelayError()
    +     * @see RxJava Wiki: mergeDelayError()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1798,7 +1798,7 @@ public static  Observable mergeDelayError(Observable t1, Obse
          * @param t8 an Observable to be merged
          * @return an Observable that emits items that are the result of flattening
          *         the items emitted by the {@code source} Observables
    -     * @see mergeDelayError()
    +     * @see RxJava Wiki: mergeDelayError()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1835,7 +1835,7 @@ public static  Observable mergeDelayError(Observable t1, Obse
          * @param t9 an Observable to be merged
          * @return an Observable that emits items that are the result of flattening
          *         the items emitted by the {@code source} Observables
    -     * @see mergeDelayError()
    +     * @see RxJava Wiki: mergeDelayError()
          * @see MSDN: Observable.Merge Method
          */
         @SuppressWarnings("unchecked")
    @@ -1855,7 +1855,7 @@ public static  Observable mergeDelayError(Observable t1, Obse
          * @param  the type of items (not) emitted by the Observable
          * @return an Observable that never sends any items or notifications to an
          *         {@link Observer}
    -     * @see never()
    +     * @see RxJava Wiki: never()
          */
         public static  Observable never() {
             return new NeverObservable();
    @@ -1871,7 +1871,7 @@ public static  Observable never() {
          * @param sequenceOfSequences the source Observable that emits Observables
          * @return an Observable that emits only the items emitted by the most
          *         recently published Observable
    -     * @see switchOnNext()
    +     * @see RxJava Wiki: switchOnNext()
          * @deprecated use {@link #switchOnNext}
          */
         @Deprecated
    @@ -1889,7 +1889,7 @@ public static  Observable switchDo(ObservableswitchOnNext()
    +     * @see RxJava Wiki: switchOnNext()
          */
         public static  Observable switchOnNext(Observable> sequenceOfSequences) {
             return create(OperationSwitch.switchDo(sequenceOfSequences));
    @@ -1913,7 +1913,7 @@ public static  Observable switchOnNext(Observablesynchronize()
    +     * @see RxJava Wiki: synchronize()
          */
         public Observable synchronize() {
             return create(OperationSynchronize.synchronize(this));
    @@ -1940,7 +1940,7 @@ public Observable synchronize() {
          * @return an Observable that is a chronologically well-behaved version of
          *         the source Observable, and that synchronously notifies its
          *         {@link Observer}s
    -     * @see synchronize()
    +     * @see RxJava Wiki: synchronize()
          */
         public Observable synchronize(Object lock) {
             return create(OperationSynchronize.synchronize(this, lock));
    @@ -1962,7 +1962,7 @@ public static  Observable synchronize(Observable source) {
          * @param interval interval size in time units (see below)
          * @param unit time units to use for the interval size
          * @return an Observable that emits an item each time interval
    -     * @see interval()
    +     * @see RxJava Wiki: interval()
          * @see MSDN: Observable.Interval
          */
         public static Observable interval(long interval, TimeUnit unit) {
    @@ -1978,7 +1978,7 @@ public static Observable interval(long interval, TimeUnit unit) {
          * @param unit time units to use for the interval size
          * @param scheduler the scheduler to use for scheduling the items
          * @return an Observable that emits an item each time interval
    -     * @see interval()
    +     * @see RxJava Wiki: interval()
          * @see MSDN: Observable.Interval
          */
         public static Observable interval(long interval, TimeUnit unit, Scheduler scheduler) {
    @@ -2007,7 +2007,7 @@ public static Observable interval(long interval, TimeUnit unit, Scheduler
          * @param unit the {@link TimeUnit} for the timeout 
          * @return an {@link Observable} that filters out items that are too
          *         quickly followed by newer items
    -     * @see debounce()
    +     * @see RxJava Wiki: debounce()
          * @see #throttleWithTimeout(long, TimeUnit)
          */
         public Observable debounce(long timeout, TimeUnit unit) {
    @@ -2038,7 +2038,7 @@ public Observable debounce(long timeout, TimeUnit unit) {
          *                  timers that handle the timeout for each event
          * @return an {@link Observable} that filters out items that are too
          *         quickly followed by newer items
    -     * @see debounce()
    +     * @see RxJava Wiki: debounce()
          * @see #throttleWithTimeout(long, TimeUnit, Scheduler)
          */
         public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) {
    @@ -2067,7 +2067,7 @@ public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler)
          * @param unit the {@link TimeUnit} for the timeout
          * @return an {@link Observable} that filters out items that are too
          *         quickly followed by newer items
    -     * @see throttleWithTimeout()
    +     * @see RxJava Wiki: throttleWithTimeout()
          * @see #debounce(long, TimeUnit)
          */
         public Observable throttleWithTimeout(long timeout, TimeUnit unit) {
    @@ -2098,7 +2098,7 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit) {
          *                  timers that handle the timeout for each event
          * @return an {@link Observable} that filters out items that are too
          *         quickly followed by newer items
    -     * @see throttleWithTimeout()
    +     * @see RxJava Wiki: throttleWithTimeout()
          * @see #debounce(long, TimeUnit, Scheduler)
          */
         public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler scheduler) {
    @@ -2118,7 +2118,7 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler
          *                       emitting the last item
          * @param unit the unit of time for the specified timeout
          * @return an Observable that performs the throttle operation
    -     * @see throttleFirst()
    +     * @see RxJava Wiki: throttleFirst()
          */
         public Observable throttleFirst(long windowDuration, TimeUnit unit) {
             return create(OperationThrottleFirst.throttleFirst(this, windowDuration, unit));
    @@ -2139,7 +2139,7 @@ public Observable throttleFirst(long windowDuration, TimeUnit unit) {
          * @param scheduler the {@link Scheduler} to use internally to manage the
          *                  timers that handle timeout for each event
          * @return an Observable that performs the throttle operation
    -     * @see throttleFirst()
    +     * @see RxJava Wiki: throttleFirst()
          */
         public Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler scheduler) {
             return create(OperationThrottleFirst.throttleFirst(this, skipDuration, unit, scheduler));
    @@ -2159,7 +2159,7 @@ public Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler s
          *                         will be emitted
          * @param unit the unit of time for the specified interval
          * @return an Observable that performs the throttle operation
    -     * @see throttleLast()
    +     * @see RxJava Wiki: throttleLast()
          * @see #sample(long, TimeUnit)
          */
         public Observable throttleLast(long intervalDuration, TimeUnit unit) {
    @@ -2180,7 +2180,7 @@ public Observable throttleLast(long intervalDuration, TimeUnit unit) {
          *                         will be emitted
          * @param unit the unit of time for the specified interval
          * @return an Observable that performs the throttle operation
    -     * @see throttleLast()
    +     * @see RxJava Wiki: throttleLast()
          * @see #sample(long, TimeUnit, Scheduler)
          */
         public Observable throttleLast(long intervalDuration, TimeUnit unit, Scheduler scheduler) {
    @@ -2195,7 +2195,7 @@ public Observable throttleLast(long intervalDuration, TimeUnit unit, Schedule
          * 
          * @return an Observable that emits timestamped items from the source
          *         Observable
    -     * @see timestamp()
    +     * @see RxJava Wiki: timestamp()
          */
         public Observable> timestamp() {
             return create(OperationTimestamp.timestamp(this));
    @@ -2218,7 +2218,7 @@ public Observable> timestamp() {
          * @param  the type of object that the {@link Future} returns, and also
          *            the type of item to be emitted by the resulting Observable
          * @return an Observable that emits the item from the source Future
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         public static  Observable from(Future future) {
             return create(OperationToObservableFuture.toObservableFuture(future));
    @@ -2242,7 +2242,7 @@ public static  Observable from(Future future) {
          * @param  the type of object that the {@link Future} returns, and also
          *            the type of item to be emitted by the resulting Observable
          * @return an Observable that emits the item from the source Future
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         public static  Observable from(Future future, Scheduler scheduler) {
             return create(OperationToObservableFuture.toObservableFuture(future)).subscribeOn(scheduler);
    @@ -2267,7 +2267,7 @@ public static  Observable from(Future future, Scheduler sched
          * @param  the type of object that the {@link Future} returns, and also
          *            the type of item to be emitted by the resulting Observable
          * @return an Observable that emits the item from the source {@link Future}
    -     * @see from()
    +     * @see RxJava Wiki: from()
          */
         public static  Observable from(Future future, long timeout, TimeUnit unit) {
             return create(OperationToObservableFuture.toObservableFuture(future, timeout, unit));
    @@ -2284,7 +2284,7 @@ public static  Observable from(Future future, long timeout, T
          * @param  the type of items emitted by each Observable
          * @return an Observable that emits Booleans that indicate whether the
          *         corresponding items emitted by the source Observables are equal
    -     * @see sequenceEqual()
    +     * @see RxJava Wiki: sequenceEqual()
          */
         public static  Observable sequenceEqual(Observable first, Observable second) {
             return sequenceEqual(first, second, new Func2() {
    @@ -2309,7 +2309,7 @@ public Boolean call(T first, T second) {
          * @param  the type of items emitted by each Observable
          * @return an Observable that emits Booleans that indicate whether the
          *         corresponding items emitted by the source Observables are equal
    -     * @see sequenceEqual()
    +     * @see RxJava Wiki: sequenceEqual()
          */
         public static  Observable sequenceEqual(Observable first, Observable second, Func2 equality) {
             return zip(first, second, equality);
    @@ -2340,7 +2340,7 @@ public static  Observable sequenceEqual(Observable firs
          *            each of the source Observables, results in an item that will
          *            be emitted by the resulting Observable
          * @return an Observable that emits the zipped results
    -     * @see zip()
    +     * @see RxJava Wiki: zip()
          */
         public static  Observable zip(Observable o1, Observable o2, Func2 zipFunction) {
             return create(OperationZip.zip(o1, o2, zipFunction));
    @@ -2373,7 +2373,7 @@ public static  Observable zip(Observable o1, Observa
          *                    each of the source Observables, results in an item
          *                    that will be emitted by the resulting Observable
          * @return an Observable that emits the zipped results
    -     * @see zip()
    +     * @see RxJava Wiki: zip()
          */
         public static  Observable zip(Observable o1, Observable o2, Observable o3, Func3 zipFunction) {
             return create(OperationZip.zip(o1, o2, o3, zipFunction));
    @@ -2407,7 +2407,7 @@ public static  Observable zip(Observable o1, Obs
          *            each of the source Observables, results in an item that will
          *            be emitted by the resulting Observable
          * @return an Observable that emits the zipped results
    -     * @see zip()
    +     * @see RxJava Wiki: zip()
          */
         public static  Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Func4 zipFunction) {
             return create(OperationZip.zip(o1, o2, o3, o4, zipFunction));
    @@ -2442,7 +2442,7 @@ public static  Observable zip(Observable o1,
          *                    each of the source Observables, results in an item
          *                    that will be emitted by the resulting Observable
          * @return an Observable that emits the zipped results
    -     * @see zip()
    +     * @see RxJava Wiki: zip()
          */
         public static  Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 zipFunction) {
             return create(OperationZip.zip(o1, o2, o3, o4, o5, zipFunction));
    @@ -2476,7 +2476,7 @@ public static  Observable zip(Observable
          *                    each of the source Observables, results in an item
          *                    that will be emitted by the resulting Observable
          * @return an Observable that emits the zipped results
    -     * @see zip()
    +     * @see RxJava Wiki: zip()
          */
         public static  Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6,
                 Func6 zipFunction) {
    @@ -2512,7 +2512,7 @@ public static  Observable zip(Observablezip()
    +     * @see RxJava Wiki: zip()
          */
         public static  Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7,
                 Func7 zipFunction) {
    @@ -2549,7 +2549,7 @@ public static  Observable zip(Observablezip()
    +     * @see RxJava Wiki: zip()
          */
         public static  Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8,
                 Func8 zipFunction) {
    @@ -2587,7 +2587,7 @@ public static  Observable zip(Observablezip()
    +     * @see RxJava Wiki: zip()
          */
         public static  Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8,
                 Observable o9, Func9 zipFunction) {
    @@ -2608,7 +2608,7 @@ public static  Observable zip(Observab
          *                        source observable values
          * @return an Observable that combines the source Observables with the
          *         given combine function
    -     * @see combineLatest()
    +     * @see RxJava Wiki: combineLatest()
          */
         public static  Observable combineLatest(Observable o1, Observable o2, Func2 combineFunction) {
             return create(OperationCombineLatest.combineLatest(o1, o2, combineFunction));
    @@ -2629,7 +2629,7 @@ public static  Observable combineLatest(Observable o
          *                        source observable values
          * @return an Observable that combines the source Observables with the
          *         given combine function
    -     * @see combineLatest()
    +     * @see RxJava Wiki: combineLatest()
          */
         public static  Observable combineLatest(Observable o1, Observable o2, Observable o3, Func3 combineFunction) {
             return create(OperationCombineLatest.combineLatest(o1, o2, o3, combineFunction));
    @@ -2651,7 +2651,7 @@ public static  Observable combineLatest(ObservablecombineLatest()
    +     * @see RxJava Wiki: combineLatest()
          */
         public static  Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4,
                 Func4 combineFunction) {
    @@ -2675,7 +2675,7 @@ public static  Observable combineLatest(ObservablecombineLatest()
    +     * @see RxJava Wiki: combineLatest()
          */
         public static  Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5,
                 Func5 combineFunction) {
    @@ -2700,7 +2700,7 @@ public static  Observable combineLatest(ObservablecombineLatest()
    +     * @see RxJava Wiki: combineLatest()
          */
         public static  Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6,
                 Func6 combineFunction) {
    @@ -2726,7 +2726,7 @@ public static  Observable combineLatest(Observable
          *                        source observable values
          * @return an Observable that combines the source Observables with the
          *         given combine function
    -     * @see combineLatest()
    +     * @see RxJava Wiki: combineLatest()
          */
         public static  Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7,
                 Func7 combineFunction) {
    @@ -2753,7 +2753,7 @@ public static  Observable combineLatest(Observ
          *                        source observable values
          * @return an Observable that combines the source Observables with the
          *         given combine function
    -     * @see combineLatest()
    +     * @see RxJava Wiki: combineLatest()
          */
         public static  Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8,
                 Func8 combineFunction) {
    @@ -2781,7 +2781,7 @@ public static  Observable combineLatest(Ob
          *                        source observable values
          * @return an Observable that combines the source Observables with the
          *         given combine function
    -     * @see combineLatest()
    +     * @see RxJava Wiki: combineLatest()
          */
         public static  Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9,
                 Func9 combineFunction) {
    @@ -2810,7 +2810,7 @@ public static  Observable combineLates
          *         buffers, which are emitted when the current {@link Observable}
          *         created with the {@link Func0} argument produces a
          *         {@link rx.util.Closing} object
    -     * @see buffer()
    +     * @see RxJava Wiki: buffer()
          */
         public Observable> buffer(Func0> bufferClosingSelector) {
             return create(OperationBuffer.buffer(this, bufferClosingSelector));
    @@ -2839,7 +2839,7 @@ public Observable> buffer(Func0>
          * @return an {@link Observable} that produces buffers that are created and
          *         emitted when the specified {@link Observable}s publish certain
          *         objects
    -     * @see buffer()
    +     * @see RxJava Wiki: buffer()
          */
         public Observable> buffer(Observable bufferOpenings, Func1> bufferClosingSelector) {
             return create(OperationBuffer.buffer(this, bufferOpenings, bufferClosingSelector));
    @@ -2858,7 +2858,7 @@ public Observable> buffer(Observable bufferOpenings,
          * @param count the maximum size of each buffer before it should be emitted
          * @return an {@link Observable} that produces connected, non-overlapping
          *         buffers containing at most "count" items
    -     * @see buffer()
    +     * @see RxJava Wiki: buffer()
          */
         public Observable> buffer(int count) {
             return create(OperationBuffer.buffer(this, count));
    @@ -2882,7 +2882,7 @@ public Observable> buffer(int count) {
          * @return an {@link Observable} that produces buffers every
          *         skip item containing at most count
          *         items
    -     * @see buffer()
    +     * @see RxJava Wiki: buffer()
          */
         public Observable> buffer(int count, int skip) {
             return create(OperationBuffer.buffer(this, count, skip));
    @@ -2904,7 +2904,7 @@ public Observable> buffer(int count, int skip) {
          *             argument
          * @return an {@link Observable} that produces connected, non-overlapping
          *         buffers with a fixed duration
    -     * @see buffer()
    +     * @see RxJava Wiki: buffer()
          */
         public Observable> buffer(long timespan, TimeUnit unit) {
             return create(OperationBuffer.buffer(this, timespan, unit));
    @@ -2928,7 +2928,7 @@ public Observable> buffer(long timespan, TimeUnit unit) {
          *                  and start of a buffer
          * @return an {@link Observable} that produces connected, non-overlapping
          *         buffers with a fixed duration
    -     * @see buffer()
    +     * @see RxJava Wiki: buffer()
          */
         public Observable> buffer(long timespan, TimeUnit unit, Scheduler scheduler) {
             return create(OperationBuffer.buffer(this, timespan, unit, scheduler));
    @@ -2952,7 +2952,7 @@ public Observable> buffer(long timespan, TimeUnit unit, Scheduler schedu
          * @return an {@link Observable} that produces connected, non-overlapping
          *         buffers that are emitted after a fixed duration or when the
          *         buffer reaches maximum capacity (whichever occurs first)
    -     * @see buffer()
    +     * @see RxJava Wiki: buffer()
          */
         public Observable> buffer(long timespan, TimeUnit unit, int count) {
             return create(OperationBuffer.buffer(this, timespan, unit, count));
    @@ -2978,7 +2978,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count) {
          * @return an {@link Observable} that produces connected, non-overlapping
          *         buffers that are emitted after a fixed duration or when the
          *         buffer has reached maximum capacity (whichever occurs first)
    -     * @see buffer()
    +     * @see RxJava Wiki: buffer()
          */
         public Observable> buffer(long timespan, TimeUnit unit, int count, Scheduler scheduler) {
             return create(OperationBuffer.buffer(this, timespan, unit, count, scheduler));
    @@ -3002,7 +3002,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count, Sched
          *             and timeshift arguments
          * @return an {@link Observable} that produces new buffers periodically and
          *         emits these after a fixed timespan has elapsed.
    -     * @see buffer()
    +     * @see RxJava Wiki: buffer()
          */
         public Observable> buffer(long timespan, long timeshift, TimeUnit unit) {
             return create(OperationBuffer.buffer(this, timespan, timeshift, unit));
    @@ -3028,7 +3028,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit)
          *                  and start of a buffer
          * @return an {@link Observable} that produces new buffers periodically and
          *         emits these after a fixed timespan has elapsed
    -     * @see buffer()
    +     * @see RxJava Wiki: buffer()
          */
         public Observable> buffer(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) {
             return create(OperationBuffer.buffer(this, timespan, timeshift, unit, scheduler));
    @@ -3053,7 +3053,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit,
          *         windows, which are emitted when the current {@link Observable}
          *         created with the closingSelector argument emits a
          *         {@link rx.util.Closing} object.
    -     * @see window()
    +     * @see RxJava Wiki: window()
          */
         public Observable> window(Func0> closingSelector) {
             return create(OperationWindow.window(this, closingSelector));
    @@ -3080,7 +3080,7 @@ public Observable> window(Func0window()
    +     * @see RxJava Wiki: window()
          */
         public Observable> window(Observable windowOpenings, Func1> closingSelector) {
             return create(OperationWindow.window(this, windowOpenings, closingSelector));
    @@ -3098,7 +3098,7 @@ public Observable> window(Observable windowOpen
          * @param count the maximum size of each window before it should be emitted
          * @return an {@link Observable} that produces connected, non-overlapping
          *         windows containing at most count items
    -     * @see window()
    +     * @see RxJava Wiki: window()
          */
         public Observable> window(int count) {
             return create(OperationWindow.window(this, count));
    @@ -3119,7 +3119,7 @@ public Observable> window(int count) {
          *             are equal this is the same operation as {@link #window(int)}.
          * @return an {@link Observable} that produces windows every "skipped"
          *         items containing at most count items
    -     * @see window()
    +     * @see RxJava Wiki: window()
          */
         public Observable> window(int count, int skip) {
             return create(OperationWindow.window(this, count, skip));
    @@ -3140,7 +3140,7 @@ public Observable> window(int count, int skip) {
          *             argument
          * @return an {@link Observable} that produces connected, non-overlapping
          *         windows with a fixed duration
    -     * @see window()
    +     * @see RxJava Wiki: window()
          */
         public Observable> window(long timespan, TimeUnit unit) {
             return create(OperationWindow.window(this, timespan, unit));
    @@ -3163,7 +3163,7 @@ public Observable> window(long timespan, TimeUnit unit) {
          *                  and start of a window
          * @return an {@link Observable} that produces connected, non-overlapping
          *         windows with a fixed duration
    -     * @see window()
    +     * @see RxJava Wiki: window()
          */
         public Observable> window(long timespan, TimeUnit unit, Scheduler scheduler) {
             return create(OperationWindow.window(this, timespan, unit, scheduler));
    @@ -3187,7 +3187,7 @@ public Observable> window(long timespan, TimeUnit unit, Scheduler
          * @return an {@link Observable} that produces connected, non-overlapping
          *         windows that are emitted after a fixed duration or when the
          *         window has reached maximum capacity (whichever occurs first)
    -     * @see window()
    +     * @see RxJava Wiki: window()
          */
         public Observable> window(long timespan, TimeUnit unit, int count) {
             return create(OperationWindow.window(this, timespan, unit, count));
    @@ -3213,7 +3213,7 @@ public Observable> window(long timespan, TimeUnit unit, int count)
          * @return an {@link Observable} that produces connected non-overlapping
          *         windows that are emitted after a fixed duration or when the
          *         window has reached maximum capacity (whichever occurs first).
    -     * @see window()
    +     * @see RxJava Wiki: window()
          */
         public Observable> window(long timespan, TimeUnit unit, int count, Scheduler scheduler) {
             return create(OperationWindow.window(this, timespan, unit, count, scheduler));
    @@ -3237,7 +3237,7 @@ public Observable> window(long timespan, TimeUnit unit, int count,
          *             and timeshift arguments
          * @return an {@link Observable} that produces new windows periodically and
          *         emits these after a fixed timespan has elapsed
    -     * @see window()
    +     * @see RxJava Wiki: window()
          */
         public Observable> window(long timespan, long timeshift, TimeUnit unit) {
             return create(OperationWindow.window(this, timespan, timeshift, unit));
    @@ -3263,7 +3263,7 @@ public Observable> window(long timespan, long timeshift, TimeUnit
          *                  and start of a window
          * @return an {@link Observable} that produces new windows periodically and
          *         emits these after a fixed timespan has elapsed
    -     * @see window()
    +     * @see RxJava Wiki: window()
          */
         public Observable> window(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) {
             return create(OperationWindow.window(this, timespan, timeshift, unit, scheduler));
    @@ -3292,7 +3292,7 @@ public Observable> window(long timespan, long timeshift, TimeUnit
          *                    each of the source Observables, results in an item
          *                    that will be emitted by the resulting Observable
          * @return an Observable that emits the zipped results
    -     * @see zip()
    +     * @see RxJava Wiki: zip()
          */
         public static  Observable zip(Observable> ws, final FuncN zipFunction) {
             return ws.toList().mapMany(new Func1>, Observable>() {
    @@ -3326,7 +3326,7 @@ public Observable call(List> wsList) {
          *                    each of the source Observables, results in an item
          *                    that will be emitted by the resulting Observable
          * @return an Observable that emits the zipped results
    -     * @see zip()
    +     * @see RxJava Wiki: zip()
          */
         public static  Observable zip(Iterable> ws, FuncN zipFunction) {
             return create(OperationZip.zip(ws, zipFunction));
    @@ -3342,7 +3342,7 @@ public static  Observable zip(Iterable> ws, FuncN<
          *                  the filter
          * @return an Observable that emits only those items in the original
          *         Observable that the filter evaluates as {@code true}
    -     * @see filter()
    +     * @see RxJava Wiki: filter()
          */
         public Observable filter(Func1 predicate) {
             return create(OperationFilter.filter(this, predicate));
    @@ -3355,7 +3355,7 @@ public Observable filter(Func1 predicate) {
          * 
          * 
          * @return an Observable of sequentially distinct items
    -     * @see distinctUntilChanged()
    +     * @see RxJava Wiki: distinctUntilChanged()
          * @see MSDN: Observable.distinctUntilChanged
          */
         public Observable distinctUntilChanged() {
    @@ -3373,7 +3373,7 @@ public Observable distinctUntilChanged() {
          *                    value that is used for deciding whether an item is
          *                    sequentially distinct from another one or not
          * @return an Observable of sequentially distinct items
    -     * @see distinctUntilChanged()
    +     * @see RxJava Wiki: distinctUntilChanged()
          * @see MSDN: Observable.distinctUntilChanged
          */
         public  Observable distinctUntilChanged(Func1 keySelector) {
    @@ -3387,7 +3387,7 @@ public  Observable distinctUntilChanged(Func1 keyS
          * 
          * 
          * @return an Observable of distinct items
    -     * @see distinct()
    +     * @see RxJava Wiki: distinct()
          * @see MSDN: Observable.distinct
          */
         public Observable distinct() {
    @@ -3404,7 +3404,7 @@ public Observable distinct() {
          *                    value that is used to decide whether an item is
          *                    distinct from another one or not
          * @return an Observable that emits distinct items
    -     * @see distinct()
    +     * @see RxJava Wiki: distinct()
          * @see MSDN: Observable.distinct
          */
         public  Observable distinct(Func1 keySelector) {
    @@ -3423,7 +3423,7 @@ public  Observable distinct(Func1 keySelector) {
          *                                   or equal to the number of elements in
          *                                   the source sequence
          * @throws IndexOutOfBoundsException if index is less than 0
    -     * @see elementAt()
    +     * @see RxJava Wiki: elementAt()
          */
         public Observable elementAt(int index) {
             return create(OperationElementAt.elementAt(this, index));
    @@ -3441,7 +3441,7 @@ public Observable elementAt(int index) {
          *         the source sequence, or the default item if the index is outside
          *         the bounds of the source sequence
          * @throws IndexOutOfBoundsException if index is less than 0
    -     * @see elementAtOrDefault()
    +     * @see RxJava Wiki: elementAtOrDefault()
          */
         public Observable elementAtOrDefault(int index, T defaultValue) {
             return create(OperationElementAt.elementAtOrDefault(this, index, defaultValue));
    @@ -3460,7 +3460,7 @@ public Observable elementAtOrDefault(int index, T defaultValue) {
          * 
          * @param predicate the condition to test every element
          * @return a subscription function for creating the target Observable
    -     * @see exists()
    +     * @see RxJava Wiki: exists()
          * @see MSDN: Observable.Any Note: the description in this page is wrong.
          */
         public Observable exists(Func1 predicate) {
    @@ -3475,7 +3475,7 @@ public Observable exists(Func1 predicate) {
          * @param element the item to search in the sequence
          * @return an Observable that emits true if the item is in the
          *         source sequence
    -     * @see contains()
    +     * @see RxJava Wiki: contains()
          * @see MSDN: Observable.Contains
          */
         public Observable contains(final T element) {
    @@ -3497,7 +3497,7 @@ public Boolean call(T t1) {
          *               Observable finishes
          * @return an Observable that emits the same items as the source Observable,
          *         then invokes the {@link Action0}
    -     * @see finallyDo()
    +     * @see RxJava Wiki: finallyDo()
          * @see MSDN: Observable.Finally Method
          */
         public Observable finallyDo(Action0 action) {
    @@ -3520,7 +3520,7 @@ public Observable finallyDo(Action0 action) {
          *         transformation function to each item emitted by the source
          *         Observable and merging the results of the Observables obtained
          *         from this transformation.
    -     * @see flatMap()
    +     * @see RxJava Wiki: flatMap()
          * @see #mapMany(Func1)
          */
         public  Observable flatMap(Func1> func) {
    @@ -3537,7 +3537,7 @@ public  Observable flatMap(Func1where()
    +     * @see RxJava Wiki: where()
          * @see #filter(Func1)
          */
         public Observable where(Func1 predicate) {
    @@ -3553,7 +3553,7 @@ public Observable where(Func1 predicate) {
          * @param func a function to apply to each item emitted by the Observable
          * @return an Observable that emits the items from the source Observable,
          *         transformed by the given function
    -     * @see map()
    +     * @see RxJava Wiki: map()
          * @see MSDN: Observable.Select
          */
         public  Observable map(Func1 func) {
    @@ -3571,7 +3571,7 @@ public  Observable map(Func1 func) {
          *             additional parameter.
          * @return an Observable that emits the items from the source Observable,
          *         transformed by the given function
    -     * @see mapWithIndex()
    +     * @see RxJava Wiki: mapWithIndex()
          * @see MSDN: Observable.Select
          */
         public  Observable mapWithIndex(Func2 func) {
    @@ -3594,7 +3594,7 @@ public  Observable mapWithIndex(Func2 fun
          *         transformation function to each item emitted by the source
          *         Observable and merging the results of the Observables obtained
          *         from this transformation.
    -     * @see mapMany()
    +     * @see RxJava Wiki: mapMany()
          * @see #flatMap(Func1)
          */
         public  Observable mapMany(Func1> func) {
    @@ -3610,7 +3610,7 @@ public  Observable mapMany(Func1materialize()
    +     * @see RxJava Wiki: materialize()
          * @see MSDN: Observable.materialize
          */
         public Observable> materialize() {
    @@ -3627,7 +3627,7 @@ public Observable> materialize() {
          *                  unsubscription actions on
          * @return the source Observable modified so that its subscriptions and
          *         unsubscriptions happen on the specified {@link Scheduler}
    -     * @see subscribeOn()
    +     * @see RxJava Wiki: subscribeOn()
          */
         public Observable subscribeOn(Scheduler scheduler) {
             return create(OperationSubscribeOn.subscribeOn(this, scheduler));
    @@ -3642,7 +3642,7 @@ public Observable subscribeOn(Scheduler scheduler) {
          * @param scheduler the {@link Scheduler} to notify {@link Observer}s on
          * @return the source Observable modified so that its {@link Observer}s are
          *         notified on the specified {@link Scheduler}
    -     * @see observeOn()
    +     * @see RxJava Wiki: observeOn()
          */
         public Observable observeOn(Scheduler scheduler) {
             return create(OperationObserveOn.observeOn(this, scheduler));
    @@ -3660,7 +3660,7 @@ public Observable observeOn(Scheduler scheduler) {
          *         the {@link Notification} objects emitted by the source Observable
          * @throws Throwable if the source Observable is not of type
          *                   {@code Observable>}
    -     * @see dematerialize()
    +     * @see RxJava Wiki: dematerialize()
          * @see MSDN: Observable.dematerialize
          */
         @SuppressWarnings("unchecked")
    @@ -3695,7 +3695,7 @@ public  Observable dematerialize() {
          *                       take over if the source Observable encounters an
          *                       error
          * @return the original Observable, with appropriately modified behavior
    -     * @see onErrorResumeNext()
    +     * @see RxJava Wiki: onErrorResumeNext()
          */
         public Observable onErrorResumeNext(final Func1> resumeFunction) {
             return create(OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction(this, resumeFunction));
    @@ -3728,7 +3728,7 @@ public Observable onErrorResumeNext(final Func1onErrorResumeNext()
    +     * @see RxJava Wiki: onErrorResumeNext()
          */
         public Observable onErrorResumeNext(final Observable resumeSequence) {
             return create(OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable(this, resumeSequence));
    @@ -3766,7 +3766,7 @@ public Observable onErrorResumeNext(final Observable resumeSeque
          *                       take over if the source Observable encounters an
          *                       error
          * @return the original Observable, with appropriately modified behavior
    -     * @see onExceptionResumeNextViaObservable()
    +     * @see RxJava Wiki: onExceptionResumeNextViaObservable()
          */
         public Observable onExceptionResumeNext(final Observable resumeSequence) {
             return create(OperationOnExceptionResumeNextViaObservable.onExceptionResumeNextViaObservable(this, resumeSequence));
    @@ -3797,7 +3797,7 @@ public Observable onExceptionResumeNext(final Observable resumeS
          *                       Observable will emit if the source Observable
          *                       encounters an error
          * @return the original Observable with appropriately modified behavior
    -     * @see onErrorReturn()
    +     * @see RxJava Wiki: onErrorReturn()
          */
         public Observable onErrorReturn(Func1 resumeFunction) {
             return create(OperationOnErrorReturn.onErrorReturn(this, resumeFunction));
    @@ -3824,7 +3824,7 @@ public Observable onErrorReturn(Func1 resumeFunction)
          * @return an Observable that emits a single item that is the result of
          *         accumulating the output from the source Observable
          * @throws IllegalArgumentException if the Observable sequence is empty
    -     * @see reduce()
    +     * @see RxJava Wiki: reduce()
          * @see MSDN: Observable.Aggregate
          * @see Wikipedia: Fold (higher-order function)
          */
    @@ -3845,7 +3845,7 @@ public Observable reduce(Func2 accumulator) {
          * 
          * @return an Observable that emits the number of counted elements of the
          *         source Observable as its single item
    -     * @see count()
    +     * @see RxJava Wiki: count()
          * @see MSDN: Observable.Count
          */
         public Observable count() {
    @@ -3866,7 +3866,7 @@ public Integer call(Integer t1, T t2) {
          * @param source source Observable to compute the sum of
          * @return an Observable that emits the sum of all the items of the
          *         source Observable as its single item
    -     * @see sum()
    +     * @see RxJava Wiki: sum()
          * @see MSDN: Observable.Sum
          */
         public static Observable sum(Observable source) {
    @@ -3882,7 +3882,7 @@ public static Observable sum(Observable source) {
          * @param source source Observable to compute the sum of
          * @return an Observable that emits the sum of all the items of the
          *         source Observable as its single item
    -     * @see sumLongs()
    +     * @see RxJava Wiki: sumLongs()
          * @see MSDN: Observable.Sum
          */
         public static Observable sumLongs(Observable source) {
    @@ -3898,7 +3898,7 @@ public static Observable sumLongs(Observable source) {
          * @param source source Observable to compute the sum of
          * @return an Observable that emits the sum of all the items of the
          *         source Observable as its single item
    -     * @see sumFloats()
    +     * @see RxJava Wiki: sumFloats()
          * @see MSDN: Observable.Sum
          */
         public static Observable sumFloats(Observable source) {
    @@ -3914,7 +3914,7 @@ public static Observable sumFloats(Observable source) {
          * @param source source Observable to compute the sum of
          * @return an Observable that emits the sum of all the items of the
          *         source Observable as its single item
    -     * @see sumDoubles()
    +     * @see RxJava Wiki: sumDoubles()
          * @see MSDN: Observable.Sum
          */
         public static Observable sumDoubles(Observable source) {
    @@ -3931,7 +3931,7 @@ public static Observable sumDoubles(Observable source) {
          * @return an Observable that emits the average of all the items emitted by
          *         the source Observable as its single item
          * @throws IllegalArgumentException if the Observable sequence is empty
    -     * @see average()
    +     * @see RxJava Wiki: average()
          * @see MSDN: Observable.Average
          */
         public static Observable average(Observable source) {
    @@ -3947,7 +3947,7 @@ public static Observable average(Observable source) {
          * @param source source observable to compute the average of
          * @return an Observable that emits the average of all the items emitted by
          *         the source Observable as its single item
    -     * @see averageLongs()
    +     * @see RxJava Wiki: averageLongs()
          * @see MSDN: Observable.Average
          */
         public static Observable averageLongs(Observable source) {
    @@ -3963,7 +3963,7 @@ public static Observable averageLongs(Observable source) {
          * @param source source observable to compute the average of
          * @return an Observable that emits the average of all the items emitted by
          *         the source Observable as its single item
    -     * @see averageFloats()
    +     * @see RxJava Wiki: averageFloats()
          * @see MSDN: Observable.Average
          */
         public static Observable averageFloats(Observable source) {
    @@ -3979,7 +3979,7 @@ public static Observable averageFloats(Observable source) {
          * @param source source observable to compute the average of
          * @return an Observable that emits the average of all the items emitted by
          *         the source Observable as its single item
    -     * @see averageDoubles()
    +     * @see RxJava Wiki: averageDoubles()
          * @see MSDN: Observable.Average
          */
         public static Observable averageDoubles(Observable source) {
    @@ -4012,7 +4012,7 @@ public static > Observable min(Observable
          * @return an Observable that emits the minimum value according to the
          *         specified comparator
          * @throws IllegalArgumentException if the source is empty
    -     * @see min()
    +     * @see RxJava Wiki: min()
          * @see MSDN: Observable.Min
          */
         public Observable min(Comparator comparator) {
    @@ -4029,7 +4029,7 @@ public Observable min(Comparator comparator) {
          * @param selector the key selector function
          * @return an Observable that emits a List of the items with the minimum key
          *         value
    -     * @see minBy()
    +     * @see RxJava Wiki: minBy()
          * @see MSDN: Observable.MinBy
          */
         public > Observable> minBy(Func1 selector) {
    @@ -4047,7 +4047,7 @@ public > Observable> minBy(Func1 s
          * @param comparator the comparator used to compare key values
          * @return an Observable that emits a List of the elements with the minimum
          *         key value according to the specified comparator
    -     * @see minBy()
    +     * @see RxJava Wiki: minBy()
          * @see MSDN: Observable.MinBy
          */
         public  Observable> minBy(Func1 selector, Comparator comparator) {
    @@ -4063,7 +4063,7 @@ public  Observable> minBy(Func1 selector, Comparator
          * @param source an Observable to determine the maximum item of
          * @return an Observable that emits the maximum element
          * @throws IllegalArgumentException if the source is empty
    -     * @see max()
    +     * @see RxJava Wiki: max()
          * @see MSDN: Observable.Max
          */
         public static > Observable max(Observable source) {
    @@ -4081,7 +4081,7 @@ public static > Observable max(Observable
          * @return an Observable that emits the maximum item according to the
          *         specified comparator
          * @throws IllegalArgumentException if the source is empty
    -     * @see max()
    +     * @see RxJava Wiki: max()
          * @see MSDN: Observable.Max
          */
         public Observable max(Comparator comparator) {
    @@ -4097,7 +4097,7 @@ public Observable max(Comparator comparator) {
          * @param selector the key selector function
          * @return an Observable that emits a List of the items with the maximum key
          *         value
    -     * @see maxBy()
    +     * @see RxJava Wiki: maxBy()
          * @see MSDN: Observable.MaxBy
          */
         public > Observable> maxBy(Func1 selector) {
    @@ -4115,7 +4115,7 @@ public > Observable> maxBy(Func1 s
          * @param comparator the comparator used to compare key values
          * @return an Observable that emits a List of the elements with the maximum
          *         key value according to the specified comparator
    -     * @see maxBy()
    +     * @see RxJava Wiki: maxBy()
          * @see MSDN: Observable.MaxBy
          */
         public  Observable> maxBy(Func1 selector, Comparator comparator) {
    @@ -4131,7 +4131,7 @@ public  Observable> maxBy(Func1 selector, Comparator
          * 
          * @return a {@link ConnectableObservable} that upon connection causes the
          *         source Observable to emit items to its {@link Observer}s
    -     * @see replay()
    +     * @see RxJava Wiki: replay()
          */
         public ConnectableObservable replay() {
             return OperationMulticast.multicast(this, ReplaySubject. create());
    @@ -4154,7 +4154,7 @@ public ConnectableObservable replay() {
          * 
          * @param retryCount number of retry attempts before failing
          * @return an Observable with retry logic
    -     * @see retry()
    +     * @see RxJava Wiki: retry()
          */
         public Observable retry(int retryCount) {
             return create(OperationRetry.retry(this, retryCount));
    @@ -4177,7 +4177,7 @@ public Observable retry(int retryCount) {
          * output would be [1, 2, 1, 2, 3, 4, 5, onCompleted].
          * 
          * @return an Observable with retry logic
    -     * @see retry()
    +     * @see RxJava Wiki: retry()
          */
         public Observable retry() {
             return create(OperationRetry.retry(this));
    @@ -4201,7 +4201,7 @@ public Observable retry() {
          * 
          * @return an Observable that, when first subscribed to, caches all of its
          *         notifications for the benefit of subsequent subscribers.
    -     * @see cache()
    +     * @see RxJava Wiki: cache()
          */
         public Observable cache() {
             return create(OperationCache.cache(this));
    @@ -4219,7 +4219,7 @@ public Observable cache() {
          *          {@code Observable}
          * @return an Observable with the output of the {@link Func1} executed on a
          *         {@link Scheduler}
    -     * @see parallel()
    +     * @see RxJava Wiki: parallel()
          */
         public  Observable parallel(Func1, Observable> f) {
             return OperationParallel.parallel(this, f);
    @@ -4237,7 +4237,7 @@ public  Observable parallel(Func1, Observable> f) {
          * @param s a {@link Scheduler} to perform the work on
          * @return an Observable with the output of the {@link Func1} executed on a
          *         {@link Scheduler}
    -     * @see parallel()
    +     * @see RxJava Wiki: parallel()
          */
     
         public  Observable parallel(final Func1, Observable> f, final Scheduler s) {
    @@ -4264,7 +4264,7 @@ public  Observable parallel(final Func1, Observable> f, f
          * @param parallelObservables the number of Observables to merge into
          * @return an Observable of Observables constrained to number defined by
          *         parallelObservables
    -     * @see parallelMerge()
    +     * @see RxJava Wiki: parallelMerge()
          */
         public static  Observable> parallelMerge(Observable> source, int parallelObservables) {
             return OperationParallelMerge.parallelMerge(source, parallelObservables);
    @@ -4290,7 +4290,7 @@ public static  Observable> parallelMerge(ObservableparallelObservables.
    -     * @see parallelMerge()
    +     * @see RxJava Wiki: parallelMerge()
          */
         public static  Observable> parallelMerge(Observable> source, int parallelObservables, Scheduler scheduler) {
             return OperationParallelMerge.parallelMerge(source, parallelObservables, scheduler);
    @@ -4306,7 +4306,7 @@ public static  Observable> parallelMerge(Observablepublish()
    +     * @see RxJava Wiki: publish()
          */
         public ConnectableObservable publish() {
             return OperationMulticast.multicast(this, PublishSubject. create());
    @@ -4319,7 +4319,7 @@ public ConnectableObservable publish() {
          * 
          * 
          * @return a {@link ConnectableObservable}
    -     * @see publishLast()
    +     * @see RxJava Wiki: publishLast()
          */
         public ConnectableObservable publishLast() {
             return OperationMulticast.multicast(this, AsyncSubject. create());
    @@ -4330,7 +4330,7 @@ public ConnectableObservable publishLast() {
          * 

    * * - * @see aggregate() + * @see RxJava Wiki: aggregate() * @see #reduce(Func2) */ public Observable aggregate(Func2 accumulator) { @@ -4359,7 +4359,7 @@ public Observable aggregate(Func2 accumulator) { * @return an Observable that emits a single item that is the result of * accumulating the output from the items emitted by the source * Observable - * @see reduce() + * @see RxJava Wiki: reduce() * @see MSDN: Observable.Aggregate * @see Wikipedia: Fold (higher-order function) */ @@ -4372,7 +4372,7 @@ public Observable reduce(R initialValue, Func2 accumulat *

    * * - * @see aggregate() + * @see RxJava Wiki: aggregate() * @see #reduce(Object, Func2) */ public Observable aggregate(R initialValue, Func2 accumulator) { @@ -4400,7 +4400,7 @@ public Observable aggregate(R initialValue, Func2 accumu * accumulator call * @return an Observable that emits the results of each call to the * accumulator function - * @see scan() + * @see RxJava Wiki: scan() * @see MSDN: Observable.Scan */ public Observable scan(Func2 accumulator) { @@ -4417,7 +4417,7 @@ public Observable scan(Func2 accumulator) { * @param unit the {@link TimeUnit} in which period is defined * @return an Observable that emits the results of sampling the items * emitted by the source Observable at the specified time interval - * @see sample() + * @see RxJava Wiki: sample() */ public Observable sample(long period, TimeUnit unit) { return create(OperationSample.sample(this, period, unit)); @@ -4434,7 +4434,7 @@ public Observable sample(long period, TimeUnit unit) { * @param scheduler the {@link Scheduler} to use when sampling * @return an Observable that emits the results of sampling the items * emitted by the source Observable at the specified time interval - * @see sample() + * @see RxJava Wiki: sample() */ public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { return create(OperationSample.sample(this, period, unit, scheduler)); @@ -4462,7 +4462,7 @@ public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { * accumulator call * @return an Observable that emits the results of each call to the * accumulator function - * @see scan() + * @see RxJava Wiki: scan() * @see MSDN: Observable.Scan */ public Observable scan(R initialValue, Func2 accumulator) { @@ -4479,7 +4479,7 @@ public Observable scan(R initialValue, Func2 accumulator * @return an Observable that emits true if all items emitted * by the source Observable satisfy the predicate; otherwise, * false - * @see all() + * @see RxJava Wiki: all() */ public Observable all(Func1 predicate) { return create(OperationAll.all(this, predicate)); @@ -4499,7 +4499,7 @@ public Observable all(Func1 predicate) { * @return an Observable that is identical to the source Observable except * that it does not emit the first num items that the * source emits - * @see skip() + * @see RxJava Wiki: skip() */ public Observable skip(int num) { return create(OperationSkip.skip(this, num)); @@ -4514,7 +4514,7 @@ public Observable skip(int num) { * @return an Observable that emits only the very first item from the * source, or none if the source Observable completes without * emitting a single item - * @see first() + * @see RxJava Wiki: first() * @see MSDN: Observable.First */ public Observable first() { @@ -4531,7 +4531,7 @@ public Observable first() { * @return an Observable that emits only the very first item satisfying the * given condition from the source, or none if the source Observable * completes without emitting a single matching item - * @see first() + * @see RxJava Wiki: first() * @see MSDN: Observable.First */ public Observable first(Func1 predicate) { @@ -4549,7 +4549,7 @@ public Observable first(Func1 predicate) { * @return an Observable that emits only the very first item from the * source, or a default value if the source Observable completes * without emitting a single item - * @see firstOrDefault() + * @see RxJava Wiki: firstOrDefault() * @see MSDN: Observable.FirstOrDefault */ public Observable firstOrDefault(T defaultValue) { @@ -4568,7 +4568,7 @@ public Observable firstOrDefault(T defaultValue) { * doesn't emit anything that satisfies the given condition * @return an Observable that emits only the very first item from the source * that satisfies the given condition, or a default value otherwise - * @see firstOrDefault() + * @see RxJava Wiki: firstOrDefault() * @see MSDN: Observable.FirstOrDefault */ public Observable firstOrDefault(Func1 predicate, T defaultValue) { @@ -4584,7 +4584,7 @@ public Observable firstOrDefault(Func1 predicate, T defau * @param defaultValue the value to return if the sequence is empty * @return an Observable that emits the specified default value if the * source is empty; otherwise, the items emitted by the source - * @see defaultIfEmpty() + * @see RxJava Wiki: defaultIfEmpty() * @see MSDN: Observable.DefaultIfEmpty */ public Observable defaultIfEmpty(T defaultValue) { @@ -4607,7 +4607,7 @@ public Observable defaultIfEmpty(T defaultValue) { * from the source Observable, or all of the items from the source * Observable if that Observable emits fewer than num * items - * @see take() + * @see RxJava Wiki: take() */ public Observable take(final int num) { return create(OperationTake.take(this, num)); @@ -4624,7 +4624,7 @@ public Observable take(final int num) { * @return an Observable that emits the items from the source Observable so * long as each item satisfies the condition defined by * predicate - * @see takeWhile() + * @see RxJava Wiki: takeWhile() */ public Observable takeWhile(final Func1 predicate) { return create(OperationTakeWhile.takeWhile(this, predicate)); @@ -4643,7 +4643,7 @@ public Observable takeWhile(final Func1 predicate) { * @return an Observable that emits items from the source Observable so long * as the predicate continues to return true for each * item, then completes - * @see takeWhileWithIndex() + * @see RxJava Wiki: takeWhileWithIndex() */ public Observable takeWhileWithIndex(final Func2 predicate) { return create(OperationTakeWhile.takeWhileWithIndex(this, predicate)); @@ -4658,7 +4658,7 @@ public Observable takeWhileWithIndex(final Func2first() + * @see RxJava Wiki: first() * @see MSDN: Observable.First * @see #first() */ @@ -4676,7 +4676,7 @@ public Observable takeFirst() { * @return an Observable that emits only the very first item satisfying the * given condition from the source, or none if the source Observable * completes without emitting a single matching item - * @see first() + * @see RxJava Wiki: first() * @see MSDN: Observable.First * @see #first(Func1) */ @@ -4694,7 +4694,7 @@ public Observable takeFirst(Func1 predicate) { * emitted by the source Observable * @return an Observable that emits only the last count items * emitted by the source Observable - * @see takeLast() + * @see RxJava Wiki: takeLast() */ public Observable takeLast(final int count) { return create(OperationTakeLast.takeLast(this, count)); @@ -4712,7 +4712,7 @@ public Observable takeLast(final int count) { * @param the type of items emitted by other * @return an Observable that emits the items of the source Observable until * such time as other emits its first item - * @see takeUntil() + * @see RxJava Wiki: takeUntil() */ public Observable takeUntil(Observable other) { return OperationTakeUntil.takeUntil(this, other); @@ -4731,7 +4731,7 @@ public Observable takeUntil(Observable other) { * as a second parameter. * @return an Observable that emits all items from the source Observable as * soon as the condition becomes false - * @see skipWhileWithIndex() + * @see RxJava Wiki: skipWhileWithIndex() * @see MSDN: Observable.SkipWhile */ public Observable skipWhileWithIndex(Func2 predicate) { @@ -4749,7 +4749,7 @@ public Observable skipWhileWithIndex(Func2 predi * Observable for a condition * @return an Observable that emits all items from the source Observable as * soon as the condition becomes false - * @see skipWhile() + * @see RxJava Wiki: skipWhile() * @see MSDN: Observable.SkipWhile */ public Observable skipWhile(Func1 predicate) { @@ -4772,7 +4772,7 @@ public Observable skipWhile(Func1 predicate) { * @return an Observable sequence emitting the source sequence items * except for the bypassed ones at the end * @throws IndexOutOfBoundsException if count is less than zero - * @see skipLast() + * @see RxJava Wiki: skipLast() * @see MSDN: Observable.SkipLast */ public Observable skipLast(int count) { @@ -4799,7 +4799,7 @@ public Observable skipLast(int count) { * * @return an Observable that emits a single item: a List containing all of * the items emitted by the source Observable. - * @see toList() + * @see RxJava Wiki: toList() */ public Observable> toList() { return create(OperationToObservableList.toObservableList(this)); @@ -4818,7 +4818,7 @@ public Observable> toList() { * all other items emitted by the Observable * @return an Observable that emits the items from the source Observable in * sorted order - * @see toSortedList() + * @see RxJava Wiki: toSortedList() */ public Observable> toSortedList() { return create(OperationToObservableSortedList.toSortedList(this)); @@ -4835,7 +4835,7 @@ public Observable> toSortedList() { * indicates their sort order * @return an Observable that emits the items from the source Observable in * sorted order - * @see toSortedList() + * @see RxJava Wiki: toSortedList() */ public Observable> toSortedList(Func2 sortFunction) { return create(OperationToObservableSortedList.toSortedList(this, sortFunction)); @@ -4850,7 +4850,7 @@ public Observable> toSortedList(Func2 sor * @param values Iterable of the items you want the modified Observable to * emit first * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(Iterable values) { return concat(Observable. from(values), this); @@ -4866,7 +4866,7 @@ public Observable startWith(Iterable values) { * emit first * @param scheduler the scheduler to emit the prepended values on * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() * @see MSDN: Observable.StartWith */ public Observable startWith(Iterable values, Scheduler scheduler) { @@ -4882,7 +4882,7 @@ public Observable startWith(Iterable values, Scheduler scheduler) { * @param values the items you want the modified Observable to emit first * @param scheduler the scheduler to emit the prepended values on * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() * @see MSDN: Observable.StartWith */ public Observable startWith(T[] values, Scheduler scheduler) { @@ -4897,7 +4897,7 @@ public Observable startWith(T[] values, Scheduler scheduler) { * * @param t1 item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1) { return concat(Observable. from(t1), this); @@ -4912,7 +4912,7 @@ public Observable startWith(T t1) { * @param t1 first item to emit * @param t2 second item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2) { return concat(Observable. from(t1, t2), this); @@ -4928,7 +4928,7 @@ public Observable startWith(T t1, T t2) { * @param t2 second item to emit * @param t3 third item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3) { return concat(Observable. from(t1, t2, t3), this); @@ -4945,7 +4945,7 @@ public Observable startWith(T t1, T t2, T t3) { * @param t3 third item to emit * @param t4 fourth item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4) { return concat(Observable. from(t1, t2, t3, t4), this); @@ -4963,7 +4963,7 @@ public Observable startWith(T t1, T t2, T t3, T t4) { * @param t4 fourth item to emit * @param t5 fifth item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5) { return concat(Observable. from(t1, t2, t3, t4, t5), this); @@ -4982,7 +4982,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5) { * @param t5 fifth item to emit * @param t6 sixth item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { return concat(Observable. from(t1, t2, t3, t4, t5, t6), this); @@ -5002,7 +5002,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { * @param t6 sixth item to emit * @param t7 seventh item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7), this); @@ -5023,7 +5023,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { * @param t7 seventh item to emit * @param t8 eighth item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8), this); @@ -5045,7 +5045,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { * @param t8 eighth item to emit * @param t9 ninth item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8, t9), this); @@ -5068,7 +5068,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T * which corresponds to a unique key value and emits items * representing items from the source Observable that share that key * value - * @see groupBy + * @see RxJava Wiki: groupBy */ public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) { return create(OperationGroupBy.groupBy(this, keySelector, elementSelector)); @@ -5087,7 +5087,7 @@ public Observable> groupBy(final Func1groupBy + * @see RxJava Wiki: groupBy */ public Observable> groupBy(final Func1 keySelector) { return create(OperationGroupBy.groupBy(this, keySelector)); @@ -5103,7 +5103,7 @@ public Observable> groupBy(final Func1 * * @return an Observable that emits a Boolean - * @see isEmpty() + * @see RxJava Wiki: isEmpty() * @see MSDN: Observable.Any */ public Observable isEmpty() { @@ -5118,7 +5118,7 @@ public Observable isEmpty() { * * * @return - * @see last() + * @see RxJava Wiki: last() */ public Observable last() { return create(OperationLast.last(this)); @@ -5129,7 +5129,7 @@ public Observable last() { * with blocking operators). * * @return - * @see Blocking Observable Operators + * @see RxJava Wiki: Blocking Observable Operators */ public BlockingObservable toBlockingObservable() { return BlockingObservable.from(this); @@ -5143,7 +5143,7 @@ public BlockingObservable toBlockingObservable() { * @param klass the target class type which the items will be converted to * @return an Observable that emits each item from the source Observable * converted to the specified type - * @see cast() + * @see RxJava Wiki: cast() * @see MSDN: Observable.Cast */ public Observable cast(final Class klass) { @@ -5159,7 +5159,7 @@ public Observable cast(final Class klass) { * Observable * @return an Observable that emits items from the source Observable of * type klass. - * @see ofClass() + * @see RxJava Wiki: ofType() * @see MSDN: Observable.OfType */ public Observable ofType(final Class klass) { @@ -5178,7 +5178,7 @@ public Boolean call(T t) { * * @return an empty Observable that only calls onCompleted or * onError - * @see ignoreElements() + * @see RxJava Wiki: ignoreElements() * @see MSDN: Observable.IgnoreElements */ public Observable ignoreElements() { @@ -5198,7 +5198,7 @@ public Observable ignoreElements() { * timeout argument. * @return the source Observable with a TimeoutException in * case of a timeout - * @see timeout() + * @see RxJava Wiki: timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit) { @@ -5220,7 +5220,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit) { * @param other sequence to return in case of a timeout * @return the source sequence switching to the other sequence in case of a * timeout - * @see timeout() + * @see RxJava Wiki: timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Observable other) { @@ -5241,7 +5241,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, ObservableTimeoutException in case * of a timeout - * @see timeout() + * @see RxJava Wiki: timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { @@ -5264,7 +5264,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler schedule * @param scheduler Scheduler to run the timeout timers on * @return the source sequence switching to the other sequence in case of a * timeout - * @see timeout() + * @see RxJava Wiki: timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Observable other, Scheduler scheduler) { @@ -5278,7 +5278,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Observable * * @return an Observable that emits time interval information items - * @see timeInterval() + * @see RxJava Wiki: timeInterval() * @see MSDN: Observable.TimeInterval */ public Observable> timeInterval() { @@ -5293,7 +5293,7 @@ public Observable> timeInterval() { * * @param scheduler Scheduler used to compute time intervals * @return an Observable that emits time interval information items - * @see timeInterval() + * @see RxJava Wiki: timeInterval() * @see MSDN: Observable.TimeInterval */ public Observable> timeInterval(Scheduler scheduler) { @@ -5310,7 +5310,7 @@ public Observable> timeInterval(Scheduler scheduler) { * @param observableFactory the factory function to obtain an Observable * @return the Observable whose lifetime controls the lifetime of the * dependent resource object - * @see using() + * @see RxJava Wiki: using() * @see MSDN: Observable.Using */ public static Observable using(Func0 resourceFactory, Func1> observableFactory) { @@ -5326,7 +5326,7 @@ public static Observable using(Func0amb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2) { @@ -5343,7 +5343,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3) { @@ -5361,7 +5361,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4) { @@ -5380,7 +5380,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5) { @@ -5400,7 +5400,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6) { @@ -5421,7 +5421,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7) { @@ -5443,7 +5443,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8) { @@ -5466,7 +5466,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9) { @@ -5481,7 +5481,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Iterable> sources) { @@ -5496,7 +5496,7 @@ public static Observable amb(Iterable> * @param observer the action to invoke for each item emitted in the source * sequence * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() + * @see RxJava Wiki: doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(Observer observer) { @@ -5511,7 +5511,7 @@ public Observable doOnEach(Observer observer) { * @param onNext the action to invoke for each item in the source * sequence * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() + * @see RxJava Wiki: doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext) { @@ -5540,7 +5540,7 @@ public void onNext(T args) { * * @param onError the action to invoke if onError is invoked * @return the source sequence with the side-effecting behavior applied - * @see doOnError() + * @see RxJava Wiki: doOnError() * @see MSDN: Observable.Do */ public Observable doOnError(final Action1 onError) { @@ -5571,7 +5571,7 @@ public void onNext(T args) { } * @param onCompleted the action to invoke when onCompleted is * called * @return the source sequence with the side-effecting behavior applied - * @see doOnCompleted() + * @see RxJava Wiki: doOnCompleted() * @see MSDN: Observable.Do */ public Observable doOnCompleted(final Action0 onCompleted) { @@ -5600,7 +5600,7 @@ public void onNext(T args) { } * @param onError the action to invoke when the source Observable calls * onError * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() + * @see RxJava Wiki: doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext, final Action1 onError) { @@ -5633,7 +5633,7 @@ public void onNext(T args) { * @param onCompleted the action to invoke when the source Observable calls * onCompleted * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() + * @see RxJava Wiki: doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext, final Action1 onError, final Action0 onCompleted) { @@ -5707,8 +5707,8 @@ private boolean isInternalImplementation(Object o) { * @return Pattern object that matches when both Observable sequences have * an available item * @throws NullPointerException if right is null - * @see and() - * @see MSDN: Observable.And + * @see RxJava Wiki: and() + * @see MSDN: Observable.And */ public Pattern2 and(Observable right) { return OperationJoinPatterns.and(this, right); @@ -5725,8 +5725,8 @@ public Pattern2 and(Observable right) { * @return Plan that produces the projected results, to be fed (with other * plans) to the When operator * @throws NullPointerException if selector is null - * @see then() - * @see MSDN: Observable.Then + * @see RxJava Wiki: then() + * @see MSDN: Observable.Then */ public Plan0 then(Func1 selector) { return OperationJoinPatterns.then(this, selector); @@ -5742,8 +5742,8 @@ public Plan0 then(Func1 selector) { * @return an Observable sequence with the results from matching several * patterns * @throws NullPointerException if plans is null - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ public static Observable when(Plan0... plans) { return create(OperationJoinPatterns.when(plans)); @@ -5759,8 +5759,8 @@ public static Observable when(Plan0... plans) { * @return an Observable sequence with the results from matching several * patterns * @throws NullPointerException if plans is null - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ public static Observable when(Iterable> plans) { if (plans == null) { @@ -5776,8 +5776,8 @@ public static Observable when(Iterable> plans) { * * @param p1 the plan to join * @return an Observable sequence with the results from matching a pattern - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1) { @@ -5793,8 +5793,8 @@ public static Observable when(Plan0 p1) { * @param p2 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2) { @@ -5811,8 +5811,8 @@ public static Observable when(Plan0 p1, Plan0 p2) { * @param p3 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3) { @@ -5830,8 +5830,8 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3) { * @param p4 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4) { @@ -5850,8 +5850,8 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p5 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5) { @@ -5871,8 +5871,8 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p6 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6) { @@ -5893,8 +5893,8 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p7 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7) { @@ -5916,8 +5916,8 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p8 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8) { @@ -5940,8 +5940,8 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p9 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8, Plan0 p9) { @@ -5950,19 +5950,23 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan /** * Correlates the elements of two sequences based on overlapping durations. - * @param right The right observable sequence to join elements for. - * @param leftDurationSelector A function to select the duration of each + *

    + * + * + * @param right the right observable sequence to join elements for + * @param leftDurationSelector a function to select the duration of each * element of this observable sequence, used to - * determine overlap. - * @param rightDurationSelector A function to select the duration of each + * determine overlap + * @param rightDurationSelector a function to select the duration of each * element of the right observable sequence, - * used to determine overlap. - * @param resultSelector A function invoked to compute a result element + * used to determine overlap + * @param resultSelector a function invoked to compute a result element * for any two overlapping elements of the left and - * right observable sequences. - * @return An observable sequence that contains result elements computed - * from source elements that have an overlapping duration. - * @see MSDN: Observable.Join + * right observable sequences + * @return an observable sequence that contains result elements computed + * from source elements that have an overlapping duration + * @see RxJava Wiki: join() + * @see MSDN: Observable.Join */ public Observable join(Observable right, Func1> leftDurationSelector, Func1> rightDurationSelector, @@ -5974,15 +5978,18 @@ public Observable join(Observable< * Return an Observable that emits a single HashMap containing all items * emitted by the source Observable, mapped by the keys returned by the * {@code keySelector} function. + *

    + * * - * If a source item maps to the same key, the HashMap will contain the latest - * of those items. + * If a source item maps to the same key, the HashMap will contain the + * latest of those items. * - * @param keySelector the function that extracts the key from the source items - * to be used as keys in the HashMap. + * @param keySelector the function that extracts the key from the source + * items to be used as keys in the HashMap * @return an Observable that emits a single HashMap containing the mapped * values of the source Observable - * @see MSDN: Observable.ToDictionary + * @see RxJava Wiki: toMap() + * @see MSDN: Observable.ToDictionary */ public Observable> toMap(Func1 keySelector) { return create(OperationToMap.toMap(this, keySelector)); @@ -5991,106 +5998,125 @@ public Observable> toMap(Func1 keySelector /** * Return an Observable that emits a single HashMap containing elements with * key and value extracted from the values emitted by the source Observable. + *

    + * + *

    + * If a source item maps to the same key, the HashMap will contain the + * latest of those items. * - * If a source item maps to the same key, the HashMap will contain the latest - * of those items. - * - * @param keySelector the function that extracts the key from the source items - * to be used as key in the HashMap - * @param valueSelector the function that extracts the value from the source items - * to be used as value in the HashMap + * @param keySelector the function that extracts the key from the source + * items to be used as key in the HashMap + * @param valueSelector the function that extracts the value from the source + * items to be used as value in the HashMap * @return an Observable that emits a single HashMap containing the mapped * values of the source Observable - * @see MSDN: Observable.ToDictionary + * @see RxJava Wiki: toMap() + * @see MSDN: Observable.ToDictionary */ public Observable> toMap(Func1 keySelector, Func1 valueSelector) { return create(OperationToMap.toMap(this, keySelector, valueSelector)); } /** - * Return an Observable that emits a single Map, returned by the mapFactory function, - * containing key and value extracted from the values emitted by the source Observable. + * Return an Observable that emits a single Map, returned by the + * mapFactory function, containing key and value extracted from + * the values emitted by the source Observable. + *

    + * * - * @param keySelector the function that extracts the key from the source items - * to be used as key in the Map - * @param valueSelector the function that extracts the value from the source items - * to be used as value in the Map + * @param keySelector the function that extracts the key from the source + * items to be used as key in the Map + * @param valueSelector the function that extracts the value from the source + * items to be used as value in the Map * @param mapFactory the function that returns an Map instance to be used * @return an Observable that emits a single Map containing the mapped * values of the source Observable + * @see RxJava Wiki: toMap() */ public Observable> toMap(Func1 keySelector, Func1 valueSelector, Func0> mapFactory) { return create(OperationToMap.toMap(this, keySelector, valueSelector, mapFactory)); } /** - * Return an Observable that emits a single HashMap containing an ArrayList of elements, - * emitted by the source Observable and keyed by the keySelector function. + * Return an Observable that emits a single HashMap containing an ArrayList + * of elements, emitted by the source Observable and keyed by the + * keySelector function. + *

    + * * - * @param keySelector the function that extracts the key from the source items - * to be used as key in the HashMap - * @return an Observable that emits a single HashMap containing an ArrayList of elements - * mapped from the source Observable - * @see MSDN: Observable.ToLookup + * @param keySelector the function that extracts the key from the source + * items to be used as key in the HashMap + * @return an Observable that emits a single HashMap containing an ArrayList + * of elements mapped from the source Observable + * @see RxJava Wiki: toMultiMap() + * @see MSDN: Observable.ToLookup */ public Observable>> toMultimap(Func1 keySelector) { return create(OperationToMultimap.toMultimap(this, keySelector)); } /** - * Return an Observable that emits a single HashMap containing an ArrayList of values, - * extracted by the valueSelector function, emitted by the source Observable - * and keyed by the keySelector function. - * - * @param keySelector the function that extracts the key from the source items - * to be used as key in the HashMap - * @param valueSelector the function that extracts the value from the source items - * to be used as value in the Map - * @return an Observable that emits a single HashMap containing an ArrayList of elements - * mapped from the source Observable + * Return an Observable that emits a single HashMap containing an ArrayList + * of values, extracted by the valueSelector function, emitted + * by the source Observable and keyed by the keySelector + * function. + *

    + * * - * @see MSDN: Observable.ToLookup + * @param keySelector the function that extracts the key from the source + * items to be used as key in the HashMap + * @param valueSelector the function that extracts the value from the source + * items to be used as value in the Map + * @return an Observable that emits a single HashMap containing an ArrayList + * of elements mapped from the source Observable + * @see RxJava Wiki: toMultiMap() + * @see MSDN: Observable.ToLookup */ public Observable>> toMultimap(Func1 keySelector, Func1 valueSelector) { return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector)); } /** - * Return an Observable that emits a single Map, returned by the mapFactory function, - * containing an ArrayList of values, extracted by the valueSelector function, - * emitted by the source Observable and keyed by the - * keySelector function. + * Return an Observable that emits a single Map, returned by the + * mapFactory function, containing an ArrayList of values, + * extracted by the valueSelector function, emitted by the + * source Observable and keyed by the keySelector function. + *

    + * * - * @param keySelector the function that extracts the key from the source items - * to be used as key in the Map - * @param valueSelector the function that extracts the value from the source items - * to be used as value in the Map + * @param keySelector the function that extracts the key from the source + * items to be used as key in the Map + * @param valueSelector the function that extracts the value from the source + * items to be used as value in the Map * @param mapFactory the function that returns an Map instance to be used - * @return an Observable that emits a single Map containing the list of mapped values - * of the source observable. + * @return an Observable that emits a single Map containing the list of + * mapped values of the source observable. + * @see RxJava Wiki: toMultiMap() */ public Observable>> toMultimap(Func1 keySelector, Func1 valueSelector, Func0>> mapFactory) { return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector, mapFactory)); } /** - * Return an Observable that emits a single Map, returned by the mapFactory function, - * containing a custom collection of values, extracted by the valueSelector function, - * emitted by the source Observable and keyed by the - * keySelector function. + * Return an Observable that emits a single Map, returned by the + * mapFactory function, containing a custom collection of + * values, extracted by the valueSelector function, emitted by + * the source Observable and keyed by the keySelector function. + *

    + * * - * @param keySelector the function that extracts the key from the source items - * to be used as key in the Map - * @param valueSelector the function that extracts the value from the source items - * to be used as value in the Map + * @param keySelector the function that extracts the key from the source + * items to be used as key in the Map + * @param valueSelector the function that extracts the value from the source + * items to be used as value in the Map * @param mapFactory the function that returns an Map instance to be used - * @param collectionFactory the function that returns a Collection instance for - * a particular key to be used in the Map - * @return an Observable that emits a single Map containing the collection of mapped values - * of the source observable. + * @param collectionFactory the function that returns a Collection instance + * for a particular key to be used in the Map + * @return an Observable that emits a single Map containing the collection + * of mapped values of the source Observable. + * @see RxJava Wiki: toMultiMap() */ public Observable>> toMultimap(Func1 keySelector, Func1 valueSelector, Func0>> mapFactory, Func1> collectionFactory) { return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector, mapFactory, collectionFactory)); } } - From 1ab9270b74b5715dd8e1e1926f7c905e6bd9cdd8 Mon Sep 17 00:00:00 2001 From: DavidMGross Date: Tue, 26 Nov 2013 09:51:32 -0800 Subject: [PATCH 317/333] Update Observable.java improve javadocs, including diagrams and wiki links for new operators --- rxjava-core/src/main/java/rx/Observable.java | 713 ++++++++++++------- 1 file changed, 445 insertions(+), 268 deletions(-) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 18c267cc1e..0cef17b1d3 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -19,8 +19,10 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -53,6 +55,7 @@ import rx.operators.OperationFirstOrDefault; import rx.operators.OperationGroupBy; import rx.operators.OperationInterval; +import rx.operators.OperationJoin; import rx.operators.OperationJoinPatterns; import rx.operators.OperationLast; import rx.operators.OperationMap; @@ -86,6 +89,8 @@ import rx.operators.OperationTimeInterval; import rx.operators.OperationTimeout; import rx.operators.OperationTimestamp; +import rx.operators.OperationToMap; +import rx.operators.OperationToMultimap; import rx.operators.OperationToObservableFuture; import rx.operators.OperationToObservableIterable; import rx.operators.OperationToObservableList; @@ -508,7 +513,7 @@ public Subscription subscribe(final Action1 onNext, final Action1Observable.publish() and Observable.multicast() + * @see RxJava Wiki: Observable.publish() and Observable.multicast() */ public ConnectableObservable multicast(Subject subject) { return OperationMulticast.multicast(this, subject); @@ -599,7 +604,7 @@ public Subscription onSubscribe(Observer observer) { * allow the Observer to cancel the subscription * @return an Observable that, when an {@link Observer} subscribes to it, * will execute the given function - * @see create() + * @see RxJava Wiki: create() */ public static Observable create(OnSubscribeFunc func) { return new Observable(func); @@ -615,7 +620,7 @@ public static Observable create(OnSubscribeFunc func) { * @return an Observable that returns no data to the {@link Observer} and * immediately invokes the {@link Observer}'s * {@link Observer#onCompleted() onCompleted} method - * @see empty() + * @see RxJava Wiki: empty() * @see MSDN: Observable.Empty Method */ public static Observable empty() { @@ -636,7 +641,7 @@ public static Observable empty() { * immediately invokes the {@link Observer}'s * {@link Observer#onCompleted() onCompleted} method with the * specified scheduler - * @see empty() + * @see RxJava Wiki: empty() * @see MSDN: Observable.Empty Method (IScheduler) */ public static Observable empty(Scheduler scheduler) { @@ -655,7 +660,7 @@ public static Observable empty(Scheduler scheduler) { * @return an Observable that invokes the {@link Observer}'s * {@link Observer#onError onError} method when the Observer * subscribes to it - * @see error() + * @see RxJava Wiki: error() * @see MSDN: Observable.Throw Method */ public static Observable error(Throwable exception) { @@ -675,7 +680,7 @@ public static Observable error(Throwable exception) { * @return an Observable that invokes the {@link Observer}'s * {@link Observer#onError onError} method with the specified * scheduler - * @see error() + * @see RxJava Wiki: error() * @see MSDN: Observable.Throw Method */ public static Observable error(Throwable exception, Scheduler scheduler) { @@ -697,7 +702,7 @@ public static Observable error(Throwable exception, Scheduler scheduler) * type of items to be emitted by the resulting Observable * @return an Observable that emits each item in the source {@link Iterable} * sequence - * @see from() + * @see RxJava Wiki: from() */ public static Observable from(Iterable iterable) { return create(OperationToObservableIterable.toObservableIterable(iterable)); @@ -714,7 +719,7 @@ public static Observable from(Iterable iterable) { * type of items to be emitted by the resulting Observable * @return an Observable that emits each item in the source {@link Iterable} * sequence with the specified scheduler - * @see from() + * @see RxJava Wiki: from() * @see MSDN: Observable.ToObservable */ public static Observable from(Iterable iterable, Scheduler scheduler) { @@ -735,7 +740,7 @@ public static Observable from(Iterable iterable, Scheduler s * @param the type of items in the Array and the type of items to be * emitted by the resulting Observable * @return an Observable that emits each item in the source Array - * @see from() + * @see RxJava Wiki: from() */ public static Observable from(T[] items) { return create(OperationToObservableIterable.toObservableIterable(Arrays.asList(items))); @@ -755,7 +760,7 @@ public static Observable from(T[] items) { * @param the type of the item, and the type of the item to be * emitted by the resulting Observable * @return an Observable that emits the item - * @see from() + * @see RxJava Wiki: from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -778,7 +783,7 @@ public static Observable from(T t1) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item - * @see from() + * @see RxJava Wiki: from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -802,7 +807,7 @@ public static Observable from(T t1, T t2) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item - * @see from() + * @see RxJava Wiki: from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -827,7 +832,7 @@ public static Observable from(T t1, T t2, T t3) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item - * @see from() + * @see RxJava Wiki: from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -853,7 +858,7 @@ public static Observable from(T t1, T t2, T t3, T t4) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item - * @see from() + * @see RxJava Wiki: from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -880,7 +885,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item - * @see from() + * @see RxJava Wiki: from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -908,7 +913,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item - * @see from() + * @see RxJava Wiki: from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -937,7 +942,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item - * @see from() + * @see RxJava Wiki: from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -967,7 +972,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item - * @see from() + * @see RxJava Wiki: from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -998,7 +1003,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * @param the type of items, and the type of items to be emitted by the * resulting Observable * @return an Observable that emits each item - * @see from() + * @see RxJava Wiki: from() */ @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method @@ -1020,7 +1025,7 @@ public static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T * @param start the value of the first integer in the sequence * @param count the number of sequential integers to generate * @return an Observable that emits a range of sequential integers - * @see range() + * @see RxJava Wiki: range() * @see Observable.Range Method (Int32, Int32) */ public static Observable range(int start, int count) { @@ -1036,7 +1041,7 @@ public static Observable range(int start, int count) { * @param count the number of sequential integers to generate * @param scheduler the scheduler to run the generator loop on * @return an Observable that emits a range of sequential integers - * @see range() + * @see RxJava Wiki: range() * @see Observable.Range Method (Int32, Int32, IScheduler) */ public static Observable range(int start, int count, Scheduler scheduler) { @@ -1061,7 +1066,7 @@ public static Observable range(int start, int count, Scheduler schedule * @param the type of the items emitted by the Observable * @return an Observable whose {@link Observer}s trigger an invocation of * the given Observable factory function - * @see defer() + * @see RxJava Wiki: defer() */ public static Observable defer(Func0> observableFactory) { return create(OperationDefer.defer(observableFactory)); @@ -1085,7 +1090,7 @@ public static Observable defer(Func0> o * {@link Observer#onNext onNext} method * @param the type of that item * @return an Observable that emits a single item and then completes - * @see just() + * @see RxJava Wiki: just() */ public static Observable just(T value) { List list = new ArrayList(); @@ -1106,7 +1111,7 @@ public static Observable just(T value) { * @param scheduler the scheduler to send the single element on * @return an Observable that emits a single item and then completes on a * specified scheduler - * @see just() + * @see RxJava Wiki: just() */ public static Observable just(T value, Scheduler scheduler) { return just(value).observeOn(scheduler); @@ -1125,7 +1130,7 @@ public static Observable just(T value, Scheduler scheduler) { * @return an Observable that emits items that are the result of flattening * the items emitted by the Observables emitted by the * {@code source} Observable - * @see merge() + * @see RxJava Wiki: merge() * @see MSDN: Observable.Merge Method */ public static Observable merge(Observable> source) { @@ -1145,7 +1150,7 @@ public static Observable merge(Observablemerge() + * @see RxJava Wiki: merge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1168,7 +1173,7 @@ public static Observable merge(Observable t1, Observablemerge() + * @see RxJava Wiki: merge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1192,7 +1197,7 @@ public static Observable merge(Observable t1, Observablemerge() + * @see RxJava Wiki: merge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1217,7 +1222,7 @@ public static Observable merge(Observable t1, Observablemerge() + * @see RxJava Wiki: merge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1243,7 +1248,7 @@ public static Observable merge(Observable t1, Observablemerge() + * @see RxJava Wiki: merge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1270,7 +1275,7 @@ public static Observable merge(Observable t1, Observablemerge() + * @see RxJava Wiki: merge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1298,7 +1303,7 @@ public static Observable merge(Observable t1, Observablemerge() + * @see RxJava Wiki: merge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1327,7 +1332,7 @@ public static Observable merge(Observable t1, Observablemerge() + * @see RxJava Wiki: merge() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1346,7 +1351,7 @@ public static Observable merge(Observable t1, Observableconcat() + * @see RxJava Wiki: concat() * @see MSDN: Observable.Concat Method */ public static Observable concat(Observable> observables) { @@ -1364,7 +1369,7 @@ public static Observable concat(Observableconcat() + * @see RxJava Wiki: concat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1386,7 +1391,7 @@ public static Observable concat(Observable t1, Observableconcat() + * @see RxJava Wiki: concat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1408,7 +1413,7 @@ public static Observable concat(Observable t1, Observableconcat() + * @see RxJava Wiki: concat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1431,7 +1436,7 @@ public static Observable concat(Observable t1, Observableconcat() + * @see RxJava Wiki: concat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1455,7 +1460,7 @@ public static Observable concat(Observable t1, Observableconcat() + * @see RxJava Wiki: concat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1480,7 +1485,7 @@ public static Observable concat(Observable t1, Observableconcat() + * @see RxJava Wiki: concat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1506,7 +1511,7 @@ public static Observable concat(Observable t1, Observableconcat() + * @see RxJava Wiki: concat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1533,7 +1538,7 @@ public static Observable concat(Observable t1, Observableconcat() + * @see RxJava Wiki: concat() * @see MSDN: Observable.Concat Method */ @SuppressWarnings("unchecked") @@ -1563,7 +1568,7 @@ public static Observable concat(Observable t1, ObservablemergeDelayError() + * @see RxJava Wiki: mergeDelayError() * @see MSDN: Observable.Merge Method */ public static Observable mergeDelayError(Observable> source) { @@ -1591,7 +1596,7 @@ public static Observable mergeDelayError(ObservablemergeDelayError() + * @see RxJava Wiki: mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1622,7 +1627,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t3 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables - * @see mergeDelayError() + * @see RxJava Wiki: mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1655,7 +1660,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t4 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables - * @see mergeDelayError() + * @see RxJava Wiki: mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1688,7 +1693,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t5 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables - * @see mergeDelayError() + * @see RxJava Wiki: mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1722,7 +1727,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t6 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables - * @see mergeDelayError() + * @see RxJava Wiki: mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1757,7 +1762,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t7 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables - * @see mergeDelayError() + * @see RxJava Wiki: mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1793,7 +1798,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t8 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables - * @see mergeDelayError() + * @see RxJava Wiki: mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1830,7 +1835,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param t9 an Observable to be merged * @return an Observable that emits items that are the result of flattening * the items emitted by the {@code source} Observables - * @see mergeDelayError() + * @see RxJava Wiki: mergeDelayError() * @see MSDN: Observable.Merge Method */ @SuppressWarnings("unchecked") @@ -1850,7 +1855,7 @@ public static Observable mergeDelayError(Observable t1, Obse * @param the type of items (not) emitted by the Observable * @return an Observable that never sends any items or notifications to an * {@link Observer} - * @see never() + * @see RxJava Wiki: never() */ public static Observable never() { return new NeverObservable(); @@ -1866,7 +1871,7 @@ public static Observable never() { * @param sequenceOfSequences the source Observable that emits Observables * @return an Observable that emits only the items emitted by the most * recently published Observable - * @see switchOnNext() + * @see RxJava Wiki: switchOnNext() * @deprecated use {@link #switchOnNext} */ @Deprecated @@ -1884,7 +1889,7 @@ public static Observable switchDo(ObservableswitchOnNext() + * @see RxJava Wiki: switchOnNext() */ public static Observable switchOnNext(Observable> sequenceOfSequences) { return create(OperationSwitch.switchDo(sequenceOfSequences)); @@ -1908,7 +1913,7 @@ public static Observable switchOnNext(Observablesynchronize() + * @see RxJava Wiki: synchronize() */ public Observable synchronize() { return create(OperationSynchronize.synchronize(this)); @@ -1935,7 +1940,7 @@ public Observable synchronize() { * @return an Observable that is a chronologically well-behaved version of * the source Observable, and that synchronously notifies its * {@link Observer}s - * @see synchronize() + * @see RxJava Wiki: synchronize() */ public Observable synchronize(Object lock) { return create(OperationSynchronize.synchronize(this, lock)); @@ -1957,7 +1962,7 @@ public static Observable synchronize(Observable source) { * @param interval interval size in time units (see below) * @param unit time units to use for the interval size * @return an Observable that emits an item each time interval - * @see interval() + * @see RxJava Wiki: interval() * @see MSDN: Observable.Interval */ public static Observable interval(long interval, TimeUnit unit) { @@ -1973,7 +1978,7 @@ public static Observable interval(long interval, TimeUnit unit) { * @param unit time units to use for the interval size * @param scheduler the scheduler to use for scheduling the items * @return an Observable that emits an item each time interval - * @see interval() + * @see RxJava Wiki: interval() * @see MSDN: Observable.Interval */ public static Observable interval(long interval, TimeUnit unit, Scheduler scheduler) { @@ -2002,7 +2007,7 @@ public static Observable interval(long interval, TimeUnit unit, Scheduler * @param unit the {@link TimeUnit} for the timeout * @return an {@link Observable} that filters out items that are too * quickly followed by newer items - * @see debounce() + * @see RxJava Wiki: debounce() * @see #throttleWithTimeout(long, TimeUnit) */ public Observable debounce(long timeout, TimeUnit unit) { @@ -2033,7 +2038,7 @@ public Observable debounce(long timeout, TimeUnit unit) { * timers that handle the timeout for each event * @return an {@link Observable} that filters out items that are too * quickly followed by newer items - * @see debounce() + * @see RxJava Wiki: debounce() * @see #throttleWithTimeout(long, TimeUnit, Scheduler) */ public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) { @@ -2062,7 +2067,7 @@ public Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) * @param unit the {@link TimeUnit} for the timeout * @return an {@link Observable} that filters out items that are too * quickly followed by newer items - * @see throttleWithTimeout() + * @see RxJava Wiki: throttleWithTimeout() * @see #debounce(long, TimeUnit) */ public Observable throttleWithTimeout(long timeout, TimeUnit unit) { @@ -2093,7 +2098,7 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit) { * timers that handle the timeout for each event * @return an {@link Observable} that filters out items that are too * quickly followed by newer items - * @see throttleWithTimeout() + * @see RxJava Wiki: throttleWithTimeout() * @see #debounce(long, TimeUnit, Scheduler) */ public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler scheduler) { @@ -2113,7 +2118,7 @@ public Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler * emitting the last item * @param unit the unit of time for the specified timeout * @return an Observable that performs the throttle operation - * @see throttleFirst() + * @see RxJava Wiki: throttleFirst() */ public Observable throttleFirst(long windowDuration, TimeUnit unit) { return create(OperationThrottleFirst.throttleFirst(this, windowDuration, unit)); @@ -2134,7 +2139,7 @@ public Observable throttleFirst(long windowDuration, TimeUnit unit) { * @param scheduler the {@link Scheduler} to use internally to manage the * timers that handle timeout for each event * @return an Observable that performs the throttle operation - * @see throttleFirst() + * @see RxJava Wiki: throttleFirst() */ public Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler scheduler) { return create(OperationThrottleFirst.throttleFirst(this, skipDuration, unit, scheduler)); @@ -2154,7 +2159,7 @@ public Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler s * will be emitted * @param unit the unit of time for the specified interval * @return an Observable that performs the throttle operation - * @see throttleLast() + * @see RxJava Wiki: throttleLast() * @see #sample(long, TimeUnit) */ public Observable throttleLast(long intervalDuration, TimeUnit unit) { @@ -2175,7 +2180,7 @@ public Observable throttleLast(long intervalDuration, TimeUnit unit) { * will be emitted * @param unit the unit of time for the specified interval * @return an Observable that performs the throttle operation - * @see throttleLast() + * @see RxJava Wiki: throttleLast() * @see #sample(long, TimeUnit, Scheduler) */ public Observable throttleLast(long intervalDuration, TimeUnit unit, Scheduler scheduler) { @@ -2190,7 +2195,7 @@ public Observable throttleLast(long intervalDuration, TimeUnit unit, Schedule * * @return an Observable that emits timestamped items from the source * Observable - * @see timestamp() + * @see RxJava Wiki: timestamp() */ public Observable> timestamp() { return create(OperationTimestamp.timestamp(this)); @@ -2213,7 +2218,7 @@ public Observable> timestamp() { * @param the type of object that the {@link Future} returns, and also * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source Future - * @see from() + * @see RxJava Wiki: from() */ public static Observable from(Future future) { return create(OperationToObservableFuture.toObservableFuture(future)); @@ -2237,7 +2242,7 @@ public static Observable from(Future future) { * @param the type of object that the {@link Future} returns, and also * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source Future - * @see from() + * @see RxJava Wiki: from() */ public static Observable from(Future future, Scheduler scheduler) { return create(OperationToObservableFuture.toObservableFuture(future)).subscribeOn(scheduler); @@ -2262,7 +2267,7 @@ public static Observable from(Future future, Scheduler sched * @param the type of object that the {@link Future} returns, and also * the type of item to be emitted by the resulting Observable * @return an Observable that emits the item from the source {@link Future} - * @see from() + * @see RxJava Wiki: from() */ public static Observable from(Future future, long timeout, TimeUnit unit) { return create(OperationToObservableFuture.toObservableFuture(future, timeout, unit)); @@ -2279,7 +2284,7 @@ public static Observable from(Future future, long timeout, T * @param the type of items emitted by each Observable * @return an Observable that emits Booleans that indicate whether the * corresponding items emitted by the source Observables are equal - * @see sequenceEqual() + * @see RxJava Wiki: sequenceEqual() */ public static Observable sequenceEqual(Observable first, Observable second) { return sequenceEqual(first, second, new Func2() { @@ -2304,7 +2309,7 @@ public Boolean call(T first, T second) { * @param the type of items emitted by each Observable * @return an Observable that emits Booleans that indicate whether the * corresponding items emitted by the source Observables are equal - * @see sequenceEqual() + * @see RxJava Wiki: sequenceEqual() */ public static Observable sequenceEqual(Observable first, Observable second, Func2 equality) { return zip(first, second, equality); @@ -2335,7 +2340,7 @@ public static Observable sequenceEqual(Observable firs * each of the source Observables, results in an item that will * be emitted by the resulting Observable * @return an Observable that emits the zipped results - * @see zip() + * @see RxJava Wiki: zip() */ public static Observable zip(Observable o1, Observable o2, Func2 zipFunction) { return create(OperationZip.zip(o1, o2, zipFunction)); @@ -2368,7 +2373,7 @@ public static Observable zip(Observable o1, Observa * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results - * @see zip() + * @see RxJava Wiki: zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Func3 zipFunction) { return create(OperationZip.zip(o1, o2, o3, zipFunction)); @@ -2402,7 +2407,7 @@ public static Observable zip(Observable o1, Obs * each of the source Observables, results in an item that will * be emitted by the resulting Observable * @return an Observable that emits the zipped results - * @see zip() + * @see RxJava Wiki: zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Func4 zipFunction) { return create(OperationZip.zip(o1, o2, o3, o4, zipFunction)); @@ -2437,7 +2442,7 @@ public static Observable zip(Observable o1, * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results - * @see zip() + * @see RxJava Wiki: zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 zipFunction) { return create(OperationZip.zip(o1, o2, o3, o4, o5, zipFunction)); @@ -2471,7 +2476,7 @@ public static Observable zip(Observable * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results - * @see zip() + * @see RxJava Wiki: zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Func6 zipFunction) { @@ -2507,7 +2512,7 @@ public static Observable zip(Observablezip() + * @see RxJava Wiki: zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Func7 zipFunction) { @@ -2544,7 +2549,7 @@ public static Observable zip(Observablezip() + * @see RxJava Wiki: zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Func8 zipFunction) { @@ -2582,7 +2587,7 @@ public static Observable zip(Observablezip() + * @see RxJava Wiki: zip() */ public static Observable zip(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9, Func9 zipFunction) { @@ -2603,7 +2608,7 @@ public static Observable zip(Observab * source observable values * @return an Observable that combines the source Observables with the * given combine function - * @see combineLatest() + * @see RxJava Wiki: combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Func2 combineFunction) { return create(OperationCombineLatest.combineLatest(o1, o2, combineFunction)); @@ -2624,7 +2629,7 @@ public static Observable combineLatest(Observable o * source observable values * @return an Observable that combines the source Observables with the * given combine function - * @see combineLatest() + * @see RxJava Wiki: combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Func3 combineFunction) { return create(OperationCombineLatest.combineLatest(o1, o2, o3, combineFunction)); @@ -2646,7 +2651,7 @@ public static Observable combineLatest(ObservablecombineLatest() + * @see RxJava Wiki: combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Func4 combineFunction) { @@ -2670,7 +2675,7 @@ public static Observable combineLatest(ObservablecombineLatest() + * @see RxJava Wiki: combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Func5 combineFunction) { @@ -2695,7 +2700,7 @@ public static Observable combineLatest(ObservablecombineLatest() + * @see RxJava Wiki: combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Func6 combineFunction) { @@ -2721,7 +2726,7 @@ public static Observable combineLatest(Observable * source observable values * @return an Observable that combines the source Observables with the * given combine function - * @see combineLatest() + * @see RxJava Wiki: combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Func7 combineFunction) { @@ -2748,7 +2753,7 @@ public static Observable combineLatest(Observ * source observable values * @return an Observable that combines the source Observables with the * given combine function - * @see combineLatest() + * @see RxJava Wiki: combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Func8 combineFunction) { @@ -2776,7 +2781,7 @@ public static Observable combineLatest(Ob * source observable values * @return an Observable that combines the source Observables with the * given combine function - * @see combineLatest() + * @see RxJava Wiki: combineLatest() */ public static Observable combineLatest(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9, Func9 combineFunction) { @@ -2805,7 +2810,7 @@ public static Observable combineLates * buffers, which are emitted when the current {@link Observable} * created with the {@link Func0} argument produces a * {@link rx.util.Closing} object - * @see buffer() + * @see RxJava Wiki: buffer() */ public Observable> buffer(Func0> bufferClosingSelector) { return create(OperationBuffer.buffer(this, bufferClosingSelector)); @@ -2834,7 +2839,7 @@ public Observable> buffer(Func0> * @return an {@link Observable} that produces buffers that are created and * emitted when the specified {@link Observable}s publish certain * objects - * @see buffer() + * @see RxJava Wiki: buffer() */ public Observable> buffer(Observable bufferOpenings, Func1> bufferClosingSelector) { return create(OperationBuffer.buffer(this, bufferOpenings, bufferClosingSelector)); @@ -2853,7 +2858,7 @@ public Observable> buffer(Observable bufferOpenings, * @param count the maximum size of each buffer before it should be emitted * @return an {@link Observable} that produces connected, non-overlapping * buffers containing at most "count" items - * @see buffer() + * @see RxJava Wiki: buffer() */ public Observable> buffer(int count) { return create(OperationBuffer.buffer(this, count)); @@ -2877,7 +2882,7 @@ public Observable> buffer(int count) { * @return an {@link Observable} that produces buffers every * skip item containing at most count * items - * @see buffer() + * @see RxJava Wiki: buffer() */ public Observable> buffer(int count, int skip) { return create(OperationBuffer.buffer(this, count, skip)); @@ -2899,7 +2904,7 @@ public Observable> buffer(int count, int skip) { * argument * @return an {@link Observable} that produces connected, non-overlapping * buffers with a fixed duration - * @see buffer() + * @see RxJava Wiki: buffer() */ public Observable> buffer(long timespan, TimeUnit unit) { return create(OperationBuffer.buffer(this, timespan, unit)); @@ -2923,7 +2928,7 @@ public Observable> buffer(long timespan, TimeUnit unit) { * and start of a buffer * @return an {@link Observable} that produces connected, non-overlapping * buffers with a fixed duration - * @see buffer() + * @see RxJava Wiki: buffer() */ public Observable> buffer(long timespan, TimeUnit unit, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, unit, scheduler)); @@ -2947,7 +2952,7 @@ public Observable> buffer(long timespan, TimeUnit unit, Scheduler schedu * @return an {@link Observable} that produces connected, non-overlapping * buffers that are emitted after a fixed duration or when the * buffer reaches maximum capacity (whichever occurs first) - * @see buffer() + * @see RxJava Wiki: buffer() */ public Observable> buffer(long timespan, TimeUnit unit, int count) { return create(OperationBuffer.buffer(this, timespan, unit, count)); @@ -2973,7 +2978,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count) { * @return an {@link Observable} that produces connected, non-overlapping * buffers that are emitted after a fixed duration or when the * buffer has reached maximum capacity (whichever occurs first) - * @see buffer() + * @see RxJava Wiki: buffer() */ public Observable> buffer(long timespan, TimeUnit unit, int count, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, unit, count, scheduler)); @@ -2997,7 +3002,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count, Sched * and timeshift arguments * @return an {@link Observable} that produces new buffers periodically and * emits these after a fixed timespan has elapsed. - * @see buffer() + * @see RxJava Wiki: buffer() */ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) { return create(OperationBuffer.buffer(this, timespan, timeshift, unit)); @@ -3023,7 +3028,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) * and start of a buffer * @return an {@link Observable} that produces new buffers periodically and * emits these after a fixed timespan has elapsed - * @see buffer() + * @see RxJava Wiki: buffer() */ public Observable> buffer(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { return create(OperationBuffer.buffer(this, timespan, timeshift, unit, scheduler)); @@ -3048,7 +3053,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit, * windows, which are emitted when the current {@link Observable} * created with the closingSelector argument emits a * {@link rx.util.Closing} object. - * @see window() + * @see RxJava Wiki: window() */ public Observable> window(Func0> closingSelector) { return create(OperationWindow.window(this, closingSelector)); @@ -3075,7 +3080,7 @@ public Observable> window(Func0window() + * @see RxJava Wiki: window() */ public Observable> window(Observable windowOpenings, Func1> closingSelector) { return create(OperationWindow.window(this, windowOpenings, closingSelector)); @@ -3093,7 +3098,7 @@ public Observable> window(Observable windowOpen * @param count the maximum size of each window before it should be emitted * @return an {@link Observable} that produces connected, non-overlapping * windows containing at most count items - * @see window() + * @see RxJava Wiki: window() */ public Observable> window(int count) { return create(OperationWindow.window(this, count)); @@ -3114,7 +3119,7 @@ public Observable> window(int count) { * are equal this is the same operation as {@link #window(int)}. * @return an {@link Observable} that produces windows every "skipped" * items containing at most count items - * @see window() + * @see RxJava Wiki: window() */ public Observable> window(int count, int skip) { return create(OperationWindow.window(this, count, skip)); @@ -3135,7 +3140,7 @@ public Observable> window(int count, int skip) { * argument * @return an {@link Observable} that produces connected, non-overlapping * windows with a fixed duration - * @see window() + * @see RxJava Wiki: window() */ public Observable> window(long timespan, TimeUnit unit) { return create(OperationWindow.window(this, timespan, unit)); @@ -3158,7 +3163,7 @@ public Observable> window(long timespan, TimeUnit unit) { * and start of a window * @return an {@link Observable} that produces connected, non-overlapping * windows with a fixed duration - * @see window() + * @see RxJava Wiki: window() */ public Observable> window(long timespan, TimeUnit unit, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, unit, scheduler)); @@ -3182,7 +3187,7 @@ public Observable> window(long timespan, TimeUnit unit, Scheduler * @return an {@link Observable} that produces connected, non-overlapping * windows that are emitted after a fixed duration or when the * window has reached maximum capacity (whichever occurs first) - * @see window() + * @see RxJava Wiki: window() */ public Observable> window(long timespan, TimeUnit unit, int count) { return create(OperationWindow.window(this, timespan, unit, count)); @@ -3208,7 +3213,7 @@ public Observable> window(long timespan, TimeUnit unit, int count) * @return an {@link Observable} that produces connected non-overlapping * windows that are emitted after a fixed duration or when the * window has reached maximum capacity (whichever occurs first). - * @see window() + * @see RxJava Wiki: window() */ public Observable> window(long timespan, TimeUnit unit, int count, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, unit, count, scheduler)); @@ -3232,7 +3237,7 @@ public Observable> window(long timespan, TimeUnit unit, int count, * and timeshift arguments * @return an {@link Observable} that produces new windows periodically and * emits these after a fixed timespan has elapsed - * @see window() + * @see RxJava Wiki: window() */ public Observable> window(long timespan, long timeshift, TimeUnit unit) { return create(OperationWindow.window(this, timespan, timeshift, unit)); @@ -3258,7 +3263,7 @@ public Observable> window(long timespan, long timeshift, TimeUnit * and start of a window * @return an {@link Observable} that produces new windows periodically and * emits these after a fixed timespan has elapsed - * @see window() + * @see RxJava Wiki: window() */ public Observable> window(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { return create(OperationWindow.window(this, timespan, timeshift, unit, scheduler)); @@ -3287,7 +3292,7 @@ public Observable> window(long timespan, long timeshift, TimeUnit * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results - * @see zip() + * @see RxJava Wiki: zip() */ public static Observable zip(Observable> ws, final FuncN zipFunction) { return ws.toList().mapMany(new Func1>, Observable>() { @@ -3321,7 +3326,7 @@ public Observable call(List> wsList) { * each of the source Observables, results in an item * that will be emitted by the resulting Observable * @return an Observable that emits the zipped results - * @see zip() + * @see RxJava Wiki: zip() */ public static Observable zip(Iterable> ws, FuncN zipFunction) { return create(OperationZip.zip(ws, zipFunction)); @@ -3337,7 +3342,7 @@ public static Observable zip(Iterable> ws, FuncN< * the filter * @return an Observable that emits only those items in the original * Observable that the filter evaluates as {@code true} - * @see filter() + * @see RxJava Wiki: filter() */ public Observable filter(Func1 predicate) { return create(OperationFilter.filter(this, predicate)); @@ -3350,7 +3355,7 @@ public Observable filter(Func1 predicate) { * * * @return an Observable of sequentially distinct items - * @see distinctUntilChanged() + * @see RxJava Wiki: distinctUntilChanged() * @see MSDN: Observable.distinctUntilChanged */ public Observable distinctUntilChanged() { @@ -3368,7 +3373,7 @@ public Observable distinctUntilChanged() { * value that is used for deciding whether an item is * sequentially distinct from another one or not * @return an Observable of sequentially distinct items - * @see distinctUntilChanged() + * @see RxJava Wiki: distinctUntilChanged() * @see MSDN: Observable.distinctUntilChanged */ public Observable distinctUntilChanged(Func1 keySelector) { @@ -3382,7 +3387,7 @@ public Observable distinctUntilChanged(Func1 keyS * * * @return an Observable of distinct items - * @see distinct() + * @see RxJava Wiki: distinct() * @see MSDN: Observable.distinct */ public Observable distinct() { @@ -3399,7 +3404,7 @@ public Observable distinct() { * value that is used to decide whether an item is * distinct from another one or not * @return an Observable that emits distinct items - * @see distinct() + * @see RxJava Wiki: distinct() * @see MSDN: Observable.distinct */ public Observable distinct(Func1 keySelector) { @@ -3418,7 +3423,7 @@ public Observable distinct(Func1 keySelector) { * or equal to the number of elements in * the source sequence * @throws IndexOutOfBoundsException if index is less than 0 - * @see elementAt() + * @see RxJava Wiki: elementAt() */ public Observable elementAt(int index) { return create(OperationElementAt.elementAt(this, index)); @@ -3436,7 +3441,7 @@ public Observable elementAt(int index) { * the source sequence, or the default item if the index is outside * the bounds of the source sequence * @throws IndexOutOfBoundsException if index is less than 0 - * @see elementAtOrDefault() + * @see RxJava Wiki: elementAtOrDefault() */ public Observable elementAtOrDefault(int index, T defaultValue) { return create(OperationElementAt.elementAtOrDefault(this, index, defaultValue)); @@ -3455,7 +3460,7 @@ public Observable elementAtOrDefault(int index, T defaultValue) { * * @param predicate the condition to test every element * @return a subscription function for creating the target Observable - * @see exists() + * @see RxJava Wiki: exists() * @see MSDN: Observable.Any Note: the description in this page is wrong. */ public Observable exists(Func1 predicate) { @@ -3470,7 +3475,7 @@ public Observable exists(Func1 predicate) { * @param element the item to search in the sequence * @return an Observable that emits true if the item is in the * source sequence - * @see contains() + * @see RxJava Wiki: contains() * @see MSDN: Observable.Contains */ public Observable contains(final T element) { @@ -3492,7 +3497,7 @@ public Boolean call(T t1) { * Observable finishes * @return an Observable that emits the same items as the source Observable, * then invokes the {@link Action0} - * @see finallyDo() + * @see RxJava Wiki: finallyDo() * @see MSDN: Observable.Finally Method */ public Observable finallyDo(Action0 action) { @@ -3515,7 +3520,7 @@ public Observable finallyDo(Action0 action) { * transformation function to each item emitted by the source * Observable and merging the results of the Observables obtained * from this transformation. - * @see flatMap() + * @see RxJava Wiki: flatMap() * @see #mapMany(Func1) */ public Observable flatMap(Func1> func) { @@ -3532,7 +3537,7 @@ public Observable flatMap(Func1where() + * @see RxJava Wiki: where() * @see #filter(Func1) */ public Observable where(Func1 predicate) { @@ -3548,7 +3553,7 @@ public Observable where(Func1 predicate) { * @param func a function to apply to each item emitted by the Observable * @return an Observable that emits the items from the source Observable, * transformed by the given function - * @see map() + * @see RxJava Wiki: map() * @see MSDN: Observable.Select */ public Observable map(Func1 func) { @@ -3566,7 +3571,7 @@ public Observable map(Func1 func) { * additional parameter. * @return an Observable that emits the items from the source Observable, * transformed by the given function - * @see mapWithIndex() + * @see RxJava Wiki: mapWithIndex() * @see MSDN: Observable.Select */ public Observable mapWithIndex(Func2 func) { @@ -3589,7 +3594,7 @@ public Observable mapWithIndex(Func2 fun * transformation function to each item emitted by the source * Observable and merging the results of the Observables obtained * from this transformation. - * @see mapMany() + * @see RxJava Wiki: mapMany() * @see #flatMap(Func1) */ public Observable mapMany(Func1> func) { @@ -3605,7 +3610,7 @@ public Observable mapMany(Func1materialize() + * @see RxJava Wiki: materialize() * @see MSDN: Observable.materialize */ public Observable> materialize() { @@ -3622,7 +3627,7 @@ public Observable> materialize() { * unsubscription actions on * @return the source Observable modified so that its subscriptions and * unsubscriptions happen on the specified {@link Scheduler} - * @see subscribeOn() + * @see RxJava Wiki: subscribeOn() */ public Observable subscribeOn(Scheduler scheduler) { return create(OperationSubscribeOn.subscribeOn(this, scheduler)); @@ -3637,7 +3642,7 @@ public Observable subscribeOn(Scheduler scheduler) { * @param scheduler the {@link Scheduler} to notify {@link Observer}s on * @return the source Observable modified so that its {@link Observer}s are * notified on the specified {@link Scheduler} - * @see observeOn() + * @see RxJava Wiki: observeOn() */ public Observable observeOn(Scheduler scheduler) { return create(OperationObserveOn.observeOn(this, scheduler)); @@ -3655,7 +3660,7 @@ public Observable observeOn(Scheduler scheduler) { * the {@link Notification} objects emitted by the source Observable * @throws Throwable if the source Observable is not of type * {@code Observable>} - * @see dematerialize() + * @see RxJava Wiki: dematerialize() * @see MSDN: Observable.dematerialize */ @SuppressWarnings("unchecked") @@ -3690,7 +3695,7 @@ public Observable dematerialize() { * take over if the source Observable encounters an * error * @return the original Observable, with appropriately modified behavior - * @see onErrorResumeNext() + * @see RxJava Wiki: onErrorResumeNext() */ public Observable onErrorResumeNext(final Func1> resumeFunction) { return create(OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction(this, resumeFunction)); @@ -3723,7 +3728,7 @@ public Observable onErrorResumeNext(final Func1onErrorResumeNext() + * @see RxJava Wiki: onErrorResumeNext() */ public Observable onErrorResumeNext(final Observable resumeSequence) { return create(OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable(this, resumeSequence)); @@ -3761,7 +3766,7 @@ public Observable onErrorResumeNext(final Observable resumeSeque * take over if the source Observable encounters an * error * @return the original Observable, with appropriately modified behavior - * @see onExceptionResumeNextViaObservable() + * @see RxJava Wiki: onExceptionResumeNextViaObservable() */ public Observable onExceptionResumeNext(final Observable resumeSequence) { return create(OperationOnExceptionResumeNextViaObservable.onExceptionResumeNextViaObservable(this, resumeSequence)); @@ -3792,7 +3797,7 @@ public Observable onExceptionResumeNext(final Observable resumeS * Observable will emit if the source Observable * encounters an error * @return the original Observable with appropriately modified behavior - * @see onErrorReturn() + * @see RxJava Wiki: onErrorReturn() */ public Observable onErrorReturn(Func1 resumeFunction) { return create(OperationOnErrorReturn.onErrorReturn(this, resumeFunction)); @@ -3819,7 +3824,7 @@ public Observable onErrorReturn(Func1 resumeFunction) * @return an Observable that emits a single item that is the result of * accumulating the output from the source Observable * @throws IllegalArgumentException if the Observable sequence is empty - * @see reduce() + * @see RxJava Wiki: reduce() * @see MSDN: Observable.Aggregate * @see Wikipedia: Fold (higher-order function) */ @@ -3840,7 +3845,7 @@ public Observable reduce(Func2 accumulator) { * * @return an Observable that emits the number of counted elements of the * source Observable as its single item - * @see count() + * @see RxJava Wiki: count() * @see MSDN: Observable.Count */ public Observable count() { @@ -3861,7 +3866,7 @@ public Integer call(Integer t1, T t2) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see sum() + * @see RxJava Wiki: sum() * @see MSDN: Observable.Sum */ public static Observable sum(Observable source) { @@ -3877,7 +3882,7 @@ public static Observable sum(Observable source) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see sumLongs() + * @see RxJava Wiki: sumLongs() * @see MSDN: Observable.Sum */ public static Observable sumLongs(Observable source) { @@ -3893,7 +3898,7 @@ public static Observable sumLongs(Observable source) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see sumFloats() + * @see RxJava Wiki: sumFloats() * @see MSDN: Observable.Sum */ public static Observable sumFloats(Observable source) { @@ -3909,7 +3914,7 @@ public static Observable sumFloats(Observable source) { * @param source source Observable to compute the sum of * @return an Observable that emits the sum of all the items of the * source Observable as its single item - * @see sumDoubles() + * @see RxJava Wiki: sumDoubles() * @see MSDN: Observable.Sum */ public static Observable sumDoubles(Observable source) { @@ -3926,7 +3931,7 @@ public static Observable sumDoubles(Observable source) { * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item * @throws IllegalArgumentException if the Observable sequence is empty - * @see average() + * @see RxJava Wiki: average() * @see MSDN: Observable.Average */ public static Observable average(Observable source) { @@ -3942,7 +3947,7 @@ public static Observable average(Observable source) { * @param source source observable to compute the average of * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item - * @see averageLongs() + * @see RxJava Wiki: averageLongs() * @see MSDN: Observable.Average */ public static Observable averageLongs(Observable source) { @@ -3958,7 +3963,7 @@ public static Observable averageLongs(Observable source) { * @param source source observable to compute the average of * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item - * @see averageFloats() + * @see RxJava Wiki: averageFloats() * @see MSDN: Observable.Average */ public static Observable averageFloats(Observable source) { @@ -3974,7 +3979,7 @@ public static Observable averageFloats(Observable source) { * @param source source observable to compute the average of * @return an Observable that emits the average of all the items emitted by * the source Observable as its single item - * @see averageDoubles() + * @see RxJava Wiki: averageDoubles() * @see MSDN: Observable.Average */ public static Observable averageDoubles(Observable source) { @@ -4007,7 +4012,7 @@ public static > Observable min(Observable * @return an Observable that emits the minimum value according to the * specified comparator * @throws IllegalArgumentException if the source is empty - * @see min() + * @see RxJava Wiki: min() * @see MSDN: Observable.Min */ public Observable min(Comparator comparator) { @@ -4024,7 +4029,7 @@ public Observable min(Comparator comparator) { * @param selector the key selector function * @return an Observable that emits a List of the items with the minimum key * value - * @see minBy() + * @see RxJava Wiki: minBy() * @see MSDN: Observable.MinBy */ public > Observable> minBy(Func1 selector) { @@ -4042,7 +4047,7 @@ public > Observable> minBy(Func1 s * @param comparator the comparator used to compare key values * @return an Observable that emits a List of the elements with the minimum * key value according to the specified comparator - * @see minBy() + * @see RxJava Wiki: minBy() * @see MSDN: Observable.MinBy */ public Observable> minBy(Func1 selector, Comparator comparator) { @@ -4058,7 +4063,7 @@ public Observable> minBy(Func1 selector, Comparator * @param source an Observable to determine the maximum item of * @return an Observable that emits the maximum element * @throws IllegalArgumentException if the source is empty - * @see max() + * @see RxJava Wiki: max() * @see MSDN: Observable.Max */ public static > Observable max(Observable source) { @@ -4076,7 +4081,7 @@ public static > Observable max(Observable * @return an Observable that emits the maximum item according to the * specified comparator * @throws IllegalArgumentException if the source is empty - * @see max() + * @see RxJava Wiki: max() * @see MSDN: Observable.Max */ public Observable max(Comparator comparator) { @@ -4092,7 +4097,7 @@ public Observable max(Comparator comparator) { * @param selector the key selector function * @return an Observable that emits a List of the items with the maximum key * value - * @see maxBy() + * @see RxJava Wiki: maxBy() * @see MSDN: Observable.MaxBy */ public > Observable> maxBy(Func1 selector) { @@ -4110,7 +4115,7 @@ public > Observable> maxBy(Func1 s * @param comparator the comparator used to compare key values * @return an Observable that emits a List of the elements with the maximum * key value according to the specified comparator - * @see maxBy() + * @see RxJava Wiki: maxBy() * @see MSDN: Observable.MaxBy */ public Observable> maxBy(Func1 selector, Comparator comparator) { @@ -4126,7 +4131,7 @@ public Observable> maxBy(Func1 selector, Comparator * * @return a {@link ConnectableObservable} that upon connection causes the * source Observable to emit items to its {@link Observer}s - * @see replay() + * @see RxJava Wiki: replay() */ public ConnectableObservable replay() { return OperationMulticast.multicast(this, ReplaySubject. create()); @@ -4149,7 +4154,7 @@ public ConnectableObservable replay() { * * @param retryCount number of retry attempts before failing * @return an Observable with retry logic - * @see retry() + * @see RxJava Wiki: retry() */ public Observable retry(int retryCount) { return create(OperationRetry.retry(this, retryCount)); @@ -4172,7 +4177,7 @@ public Observable retry(int retryCount) { * output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. * * @return an Observable with retry logic - * @see retry() + * @see RxJava Wiki: retry() */ public Observable retry() { return create(OperationRetry.retry(this)); @@ -4196,7 +4201,7 @@ public Observable retry() { * * @return an Observable that, when first subscribed to, caches all of its * notifications for the benefit of subsequent subscribers. - * @see cache() + * @see RxJava Wiki: cache() */ public Observable cache() { return create(OperationCache.cache(this)); @@ -4214,7 +4219,7 @@ public Observable cache() { * {@code Observable} * @return an Observable with the output of the {@link Func1} executed on a * {@link Scheduler} - * @see parallel() + * @see RxJava Wiki: parallel() */ public Observable parallel(Func1, Observable> f) { return OperationParallel.parallel(this, f); @@ -4232,7 +4237,7 @@ public Observable parallel(Func1, Observable> f) { * @param s a {@link Scheduler} to perform the work on * @return an Observable with the output of the {@link Func1} executed on a * {@link Scheduler} - * @see parallel() + * @see RxJava Wiki: parallel() */ public Observable parallel(final Func1, Observable> f, final Scheduler s) { @@ -4259,7 +4264,7 @@ public Observable parallel(final Func1, Observable> f, f * @param parallelObservables the number of Observables to merge into * @return an Observable of Observables constrained to number defined by * parallelObservables - * @see parallelMerge() + * @see RxJava Wiki: parallelMerge() */ public static Observable> parallelMerge(Observable> source, int parallelObservables) { return OperationParallelMerge.parallelMerge(source, parallelObservables); @@ -4285,7 +4290,7 @@ public static Observable> parallelMerge(ObservableparallelObservables. - * @see parallelMerge() + * @see RxJava Wiki: parallelMerge() */ public static Observable> parallelMerge(Observable> source, int parallelObservables, Scheduler scheduler) { return OperationParallelMerge.parallelMerge(source, parallelObservables, scheduler); @@ -4301,7 +4306,7 @@ public static Observable> parallelMerge(Observablepublish() + * @see RxJava Wiki: publish() */ public ConnectableObservable publish() { return OperationMulticast.multicast(this, PublishSubject. create()); @@ -4314,7 +4319,7 @@ public ConnectableObservable publish() { * * * @return a {@link ConnectableObservable} - * @see publishLast() + * @see RxJava Wiki: publishLast() */ public ConnectableObservable publishLast() { return OperationMulticast.multicast(this, AsyncSubject. create()); @@ -4325,7 +4330,7 @@ public ConnectableObservable publishLast() { *

    * * - * @see aggregate() + * @see RxJava Wiki: aggregate() * @see #reduce(Func2) */ public Observable aggregate(Func2 accumulator) { @@ -4354,7 +4359,7 @@ public Observable aggregate(Func2 accumulator) { * @return an Observable that emits a single item that is the result of * accumulating the output from the items emitted by the source * Observable - * @see reduce() + * @see RxJava Wiki: reduce() * @see MSDN: Observable.Aggregate * @see Wikipedia: Fold (higher-order function) */ @@ -4367,7 +4372,7 @@ public Observable reduce(R initialValue, Func2 accumulat *

    * * - * @see aggregate() + * @see RxJava Wiki: aggregate() * @see #reduce(Object, Func2) */ public Observable aggregate(R initialValue, Func2 accumulator) { @@ -4395,7 +4400,7 @@ public Observable aggregate(R initialValue, Func2 accumu * accumulator call * @return an Observable that emits the results of each call to the * accumulator function - * @see scan() + * @see RxJava Wiki: scan() * @see MSDN: Observable.Scan */ public Observable scan(Func2 accumulator) { @@ -4412,7 +4417,7 @@ public Observable scan(Func2 accumulator) { * @param unit the {@link TimeUnit} in which period is defined * @return an Observable that emits the results of sampling the items * emitted by the source Observable at the specified time interval - * @see sample() + * @see RxJava Wiki: sample() */ public Observable sample(long period, TimeUnit unit) { return create(OperationSample.sample(this, period, unit)); @@ -4429,7 +4434,7 @@ public Observable sample(long period, TimeUnit unit) { * @param scheduler the {@link Scheduler} to use when sampling * @return an Observable that emits the results of sampling the items * emitted by the source Observable at the specified time interval - * @see sample() + * @see RxJava Wiki: sample() */ public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { return create(OperationSample.sample(this, period, unit, scheduler)); @@ -4457,7 +4462,7 @@ public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { * accumulator call * @return an Observable that emits the results of each call to the * accumulator function - * @see scan() + * @see RxJava Wiki: scan() * @see MSDN: Observable.Scan */ public Observable scan(R initialValue, Func2 accumulator) { @@ -4474,7 +4479,7 @@ public Observable scan(R initialValue, Func2 accumulator * @return an Observable that emits true if all items emitted * by the source Observable satisfy the predicate; otherwise, * false - * @see all() + * @see RxJava Wiki: all() */ public Observable all(Func1 predicate) { return create(OperationAll.all(this, predicate)); @@ -4494,7 +4499,7 @@ public Observable all(Func1 predicate) { * @return an Observable that is identical to the source Observable except * that it does not emit the first num items that the * source emits - * @see skip() + * @see RxJava Wiki: skip() */ public Observable skip(int num) { return create(OperationSkip.skip(this, num)); @@ -4509,7 +4514,7 @@ public Observable skip(int num) { * @return an Observable that emits only the very first item from the * source, or none if the source Observable completes without * emitting a single item - * @see first() + * @see RxJava Wiki: first() * @see MSDN: Observable.First */ public Observable first() { @@ -4526,7 +4531,7 @@ public Observable first() { * @return an Observable that emits only the very first item satisfying the * given condition from the source, or none if the source Observable * completes without emitting a single matching item - * @see first() + * @see RxJava Wiki: first() * @see MSDN: Observable.First */ public Observable first(Func1 predicate) { @@ -4544,7 +4549,7 @@ public Observable first(Func1 predicate) { * @return an Observable that emits only the very first item from the * source, or a default value if the source Observable completes * without emitting a single item - * @see firstOrDefault() + * @see RxJava Wiki: firstOrDefault() * @see MSDN: Observable.FirstOrDefault */ public Observable firstOrDefault(T defaultValue) { @@ -4563,7 +4568,7 @@ public Observable firstOrDefault(T defaultValue) { * doesn't emit anything that satisfies the given condition * @return an Observable that emits only the very first item from the source * that satisfies the given condition, or a default value otherwise - * @see firstOrDefault() + * @see RxJava Wiki: firstOrDefault() * @see MSDN: Observable.FirstOrDefault */ public Observable firstOrDefault(Func1 predicate, T defaultValue) { @@ -4579,7 +4584,7 @@ public Observable firstOrDefault(Func1 predicate, T defau * @param defaultValue the value to return if the sequence is empty * @return an Observable that emits the specified default value if the * source is empty; otherwise, the items emitted by the source - * @see defaultIfEmpty() + * @see RxJava Wiki: defaultIfEmpty() * @see MSDN: Observable.DefaultIfEmpty */ public Observable defaultIfEmpty(T defaultValue) { @@ -4602,7 +4607,7 @@ public Observable defaultIfEmpty(T defaultValue) { * from the source Observable, or all of the items from the source * Observable if that Observable emits fewer than num * items - * @see take() + * @see RxJava Wiki: take() */ public Observable take(final int num) { return create(OperationTake.take(this, num)); @@ -4619,7 +4624,7 @@ public Observable take(final int num) { * @return an Observable that emits the items from the source Observable so * long as each item satisfies the condition defined by * predicate - * @see takeWhile() + * @see RxJava Wiki: takeWhile() */ public Observable takeWhile(final Func1 predicate) { return create(OperationTakeWhile.takeWhile(this, predicate)); @@ -4638,7 +4643,7 @@ public Observable takeWhile(final Func1 predicate) { * @return an Observable that emits items from the source Observable so long * as the predicate continues to return true for each * item, then completes - * @see takeWhileWithIndex() + * @see RxJava Wiki: takeWhileWithIndex() */ public Observable takeWhileWithIndex(final Func2 predicate) { return create(OperationTakeWhile.takeWhileWithIndex(this, predicate)); @@ -4653,7 +4658,7 @@ public Observable takeWhileWithIndex(final Func2first() + * @see RxJava Wiki: first() * @see MSDN: Observable.First * @see #first() */ @@ -4671,7 +4676,7 @@ public Observable takeFirst() { * @return an Observable that emits only the very first item satisfying the * given condition from the source, or none if the source Observable * completes without emitting a single matching item - * @see first() + * @see RxJava Wiki: first() * @see MSDN: Observable.First * @see #first(Func1) */ @@ -4689,7 +4694,7 @@ public Observable takeFirst(Func1 predicate) { * emitted by the source Observable * @return an Observable that emits only the last count items * emitted by the source Observable - * @see takeLast() + * @see RxJava Wiki: takeLast() */ public Observable takeLast(final int count) { return create(OperationTakeLast.takeLast(this, count)); @@ -4707,7 +4712,7 @@ public Observable takeLast(final int count) { * @param the type of items emitted by other * @return an Observable that emits the items of the source Observable until * such time as other emits its first item - * @see takeUntil() + * @see RxJava Wiki: takeUntil() */ public Observable takeUntil(Observable other) { return OperationTakeUntil.takeUntil(this, other); @@ -4726,7 +4731,7 @@ public Observable takeUntil(Observable other) { * as a second parameter. * @return an Observable that emits all items from the source Observable as * soon as the condition becomes false - * @see skipWhileWithIndex() + * @see RxJava Wiki: skipWhileWithIndex() * @see MSDN: Observable.SkipWhile */ public Observable skipWhileWithIndex(Func2 predicate) { @@ -4744,7 +4749,7 @@ public Observable skipWhileWithIndex(Func2 predi * Observable for a condition * @return an Observable that emits all items from the source Observable as * soon as the condition becomes false - * @see skipWhile() + * @see RxJava Wiki: skipWhile() * @see MSDN: Observable.SkipWhile */ public Observable skipWhile(Func1 predicate) { @@ -4767,7 +4772,7 @@ public Observable skipWhile(Func1 predicate) { * @return an Observable sequence emitting the source sequence items * except for the bypassed ones at the end * @throws IndexOutOfBoundsException if count is less than zero - * @see skipLast() + * @see RxJava Wiki: skipLast() * @see MSDN: Observable.SkipLast */ public Observable skipLast(int count) { @@ -4794,7 +4799,7 @@ public Observable skipLast(int count) { * * @return an Observable that emits a single item: a List containing all of * the items emitted by the source Observable. - * @see toList() + * @see RxJava Wiki: toList() */ public Observable> toList() { return create(OperationToObservableList.toObservableList(this)); @@ -4813,7 +4818,7 @@ public Observable> toList() { * all other items emitted by the Observable * @return an Observable that emits the items from the source Observable in * sorted order - * @see toSortedList() + * @see RxJava Wiki: toSortedList() */ public Observable> toSortedList() { return create(OperationToObservableSortedList.toSortedList(this)); @@ -4830,7 +4835,7 @@ public Observable> toSortedList() { * indicates their sort order * @return an Observable that emits the items from the source Observable in * sorted order - * @see toSortedList() + * @see RxJava Wiki: toSortedList() */ public Observable> toSortedList(Func2 sortFunction) { return create(OperationToObservableSortedList.toSortedList(this, sortFunction)); @@ -4845,7 +4850,7 @@ public Observable> toSortedList(Func2 sor * @param values Iterable of the items you want the modified Observable to * emit first * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(Iterable values) { return concat(Observable. from(values), this); @@ -4861,7 +4866,7 @@ public Observable startWith(Iterable values) { * emit first * @param scheduler the scheduler to emit the prepended values on * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() * @see MSDN: Observable.StartWith */ public Observable startWith(Iterable values, Scheduler scheduler) { @@ -4877,7 +4882,7 @@ public Observable startWith(Iterable values, Scheduler scheduler) { * @param values the items you want the modified Observable to emit first * @param scheduler the scheduler to emit the prepended values on * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() * @see MSDN: Observable.StartWith */ public Observable startWith(T[] values, Scheduler scheduler) { @@ -4892,7 +4897,7 @@ public Observable startWith(T[] values, Scheduler scheduler) { * * @param t1 item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1) { return concat(Observable. from(t1), this); @@ -4907,7 +4912,7 @@ public Observable startWith(T t1) { * @param t1 first item to emit * @param t2 second item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2) { return concat(Observable. from(t1, t2), this); @@ -4923,7 +4928,7 @@ public Observable startWith(T t1, T t2) { * @param t2 second item to emit * @param t3 third item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3) { return concat(Observable. from(t1, t2, t3), this); @@ -4940,7 +4945,7 @@ public Observable startWith(T t1, T t2, T t3) { * @param t3 third item to emit * @param t4 fourth item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4) { return concat(Observable. from(t1, t2, t3, t4), this); @@ -4958,7 +4963,7 @@ public Observable startWith(T t1, T t2, T t3, T t4) { * @param t4 fourth item to emit * @param t5 fifth item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5) { return concat(Observable. from(t1, t2, t3, t4, t5), this); @@ -4977,7 +4982,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5) { * @param t5 fifth item to emit * @param t6 sixth item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { return concat(Observable. from(t1, t2, t3, t4, t5, t6), this); @@ -4997,7 +5002,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6) { * @param t6 sixth item to emit * @param t7 seventh item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7), this); @@ -5018,7 +5023,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { * @param t7 seventh item to emit * @param t8 eighth item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8), this); @@ -5040,7 +5045,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { * @param t8 eighth item to emit * @param t9 ninth item to emit * @return an Observable that exhibits the modified behavior - * @see startWith() + * @see RxJava Wiki: startWith() */ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { return concat(Observable. from(t1, t2, t3, t4, t5, t6, t7, t8, t9), this); @@ -5063,7 +5068,7 @@ public Observable startWith(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T * which corresponds to a unique key value and emits items * representing items from the source Observable that share that key * value - * @see groupBy + * @see RxJava Wiki: groupBy */ public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) { return create(OperationGroupBy.groupBy(this, keySelector, elementSelector)); @@ -5082,7 +5087,7 @@ public Observable> groupBy(final Func1groupBy + * @see RxJava Wiki: groupBy */ public Observable> groupBy(final Func1 keySelector) { return create(OperationGroupBy.groupBy(this, keySelector)); @@ -5098,7 +5103,7 @@ public Observable> groupBy(final Func1 * * @return an Observable that emits a Boolean - * @see isEmpty() + * @see RxJava Wiki: isEmpty() * @see MSDN: Observable.Any */ public Observable isEmpty() { @@ -5113,7 +5118,7 @@ public Observable isEmpty() { * * * @return - * @see last() + * @see RxJava Wiki: last() */ public Observable last() { return create(OperationLast.last(this)); @@ -5124,7 +5129,7 @@ public Observable last() { * with blocking operators). * * @return - * @see Blocking Observable Operators + * @see RxJava Wiki: Blocking Observable Operators */ public BlockingObservable toBlockingObservable() { return BlockingObservable.from(this); @@ -5138,7 +5143,7 @@ public BlockingObservable toBlockingObservable() { * @param klass the target class type which the items will be converted to * @return an Observable that emits each item from the source Observable * converted to the specified type - * @see cast() + * @see RxJava Wiki: cast() * @see MSDN: Observable.Cast */ public Observable cast(final Class klass) { @@ -5154,7 +5159,7 @@ public Observable cast(final Class klass) { * Observable * @return an Observable that emits items from the source Observable of * type klass. - * @see ofClass() + * @see RxJava Wiki: ofType() * @see MSDN: Observable.OfType */ public Observable ofType(final Class klass) { @@ -5173,7 +5178,7 @@ public Boolean call(T t) { * * @return an empty Observable that only calls onCompleted or * onError - * @see ignoreElements() + * @see RxJava Wiki: ignoreElements() * @see MSDN: Observable.IgnoreElements */ public Observable ignoreElements() { @@ -5193,7 +5198,7 @@ public Observable ignoreElements() { * timeout argument. * @return the source Observable with a TimeoutException in * case of a timeout - * @see timeout() + * @see RxJava Wiki: timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit) { @@ -5215,7 +5220,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit) { * @param other sequence to return in case of a timeout * @return the source sequence switching to the other sequence in case of a * timeout - * @see timeout() + * @see RxJava Wiki: timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Observable other) { @@ -5236,7 +5241,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, ObservableTimeoutException in case * of a timeout - * @see timeout() + * @see RxJava Wiki: timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { @@ -5259,7 +5264,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Scheduler schedule * @param scheduler Scheduler to run the timeout timers on * @return the source sequence switching to the other sequence in case of a * timeout - * @see timeout() + * @see RxJava Wiki: timeout() * @see MSDN: Observable.Timeout */ public Observable timeout(long timeout, TimeUnit timeUnit, Observable other, Scheduler scheduler) { @@ -5273,7 +5278,7 @@ public Observable timeout(long timeout, TimeUnit timeUnit, Observable * * @return an Observable that emits time interval information items - * @see timeInterval() + * @see RxJava Wiki: timeInterval() * @see MSDN: Observable.TimeInterval */ public Observable> timeInterval() { @@ -5288,7 +5293,7 @@ public Observable> timeInterval() { * * @param scheduler Scheduler used to compute time intervals * @return an Observable that emits time interval information items - * @see timeInterval() + * @see RxJava Wiki: timeInterval() * @see MSDN: Observable.TimeInterval */ public Observable> timeInterval(Scheduler scheduler) { @@ -5305,7 +5310,7 @@ public Observable> timeInterval(Scheduler scheduler) { * @param observableFactory the factory function to obtain an Observable * @return the Observable whose lifetime controls the lifetime of the * dependent resource object - * @see using() + * @see RxJava Wiki: using() * @see MSDN: Observable.Using */ public static Observable using(Func0 resourceFactory, Func1> observableFactory) { @@ -5321,7 +5326,7 @@ public static Observable using(Func0amb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2) { @@ -5338,7 +5343,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3) { @@ -5356,7 +5361,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4) { @@ -5375,7 +5380,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5) { @@ -5395,7 +5400,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6) { @@ -5416,7 +5421,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7) { @@ -5438,7 +5443,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8) { @@ -5461,7 +5466,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Observable o1, Observable o2, Observable o3, Observable o4, Observable o5, Observable o6, Observable o7, Observable o8, Observable o9) { @@ -5476,7 +5481,7 @@ public static Observable amb(Observable o1, Observableamb() + * @see RxJava Wiki: amb() * @see MSDN: Observable.Amb */ public static Observable amb(Iterable> sources) { @@ -5491,7 +5496,7 @@ public static Observable amb(Iterable> * @param observer the action to invoke for each item emitted in the source * sequence * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() + * @see RxJava Wiki: doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(Observer observer) { @@ -5506,7 +5511,7 @@ public Observable doOnEach(Observer observer) { * @param onNext the action to invoke for each item in the source * sequence * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() + * @see RxJava Wiki: doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext) { @@ -5535,7 +5540,7 @@ public void onNext(T args) { * * @param onError the action to invoke if onError is invoked * @return the source sequence with the side-effecting behavior applied - * @see doOnError() + * @see RxJava Wiki: doOnError() * @see MSDN: Observable.Do */ public Observable doOnError(final Action1 onError) { @@ -5566,7 +5571,7 @@ public void onNext(T args) { } * @param onCompleted the action to invoke when onCompleted is * called * @return the source sequence with the side-effecting behavior applied - * @see doOnCompleted() + * @see RxJava Wiki: doOnCompleted() * @see MSDN: Observable.Do */ public Observable doOnCompleted(final Action0 onCompleted) { @@ -5595,7 +5600,7 @@ public void onNext(T args) { } * @param onError the action to invoke when the source Observable calls * onError * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() + * @see RxJava Wiki: doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext, final Action1 onError) { @@ -5628,7 +5633,7 @@ public void onNext(T args) { * @param onCompleted the action to invoke when the source Observable calls * onCompleted * @return the source sequence with the side-effecting behavior applied - * @see doOnEach() + * @see RxJava Wiki: doOnEach() * @see MSDN: Observable.Do */ public Observable doOnEach(final Action1 onNext, final Action1 onError, final Action0 onCompleted) { @@ -5696,14 +5701,14 @@ private boolean isInternalImplementation(Object o) { * Creates a pattern that matches when both Observable sequences have an * available item. *

    - * + * * * @param right Observable sequence to match with the left sequence * @return Pattern object that matches when both Observable sequences have * an available item * @throws NullPointerException if right is null - * @see and() - * @see MSDN: Observable.And + * @see RxJava Wiki: and() + * @see MSDN: Observable.And */ public Pattern2 and(Observable right) { return OperationJoinPatterns.and(this, right); @@ -5713,15 +5718,15 @@ public Pattern2 and(Observable right) { * Matches when the Observable sequence has an available item and * projects the item by invoking the selector function. *

    - * + * * * @param selector Selector that will be invoked for elements in the source * sequence * @return Plan that produces the projected results, to be fed (with other * plans) to the When operator * @throws NullPointerException if selector is null - * @see then() - * @see MSDN: Observable.Then + * @see RxJava Wiki: then() + * @see MSDN: Observable.Then */ public Plan0 then(Func1 selector) { return OperationJoinPatterns.then(this, selector); @@ -5730,15 +5735,15 @@ public Plan0 then(Func1 selector) { /** * Joins together the results from several patterns. *

    - * + * * * @param plans a series of plans created by use of the Then operator on * patterns * @return an Observable sequence with the results from matching several * patterns * @throws NullPointerException if plans is null - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ public static Observable when(Plan0... plans) { return create(OperationJoinPatterns.when(plans)); @@ -5747,15 +5752,15 @@ public static Observable when(Plan0... plans) { /** * Joins together the results from several patterns. *

    - * + * * * @param plans a series of plans created by use of the Then operator on * patterns * @return an Observable sequence with the results from matching several * patterns * @throws NullPointerException if plans is null - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ public static Observable when(Iterable> plans) { if (plans == null) { @@ -5767,12 +5772,12 @@ public static Observable when(Iterable> plans) { /** * Joins the results from a pattern. *

    - * + * * * @param p1 the plan to join * @return an Observable sequence with the results from matching a pattern - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1) { @@ -5782,14 +5787,14 @@ public static Observable when(Plan0 p1) { /** * Joins together the results from several patterns. *

    - * + * * * @param p1 a plan * @param p2 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2) { @@ -5799,15 +5804,15 @@ public static Observable when(Plan0 p1, Plan0 p2) { /** * Joins together the results from several patterns. *

    - * + * * * @param p1 a plan * @param p2 a plan * @param p3 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3) { @@ -5817,7 +5822,7 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3) { /** * Joins together the results from several patterns. *

    - * + * * * @param p1 a plan * @param p2 a plan @@ -5825,8 +5830,8 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3) { * @param p4 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4) { @@ -5836,7 +5841,7 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan /** * Joins together the results from several patterns. *

    - * + * * * @param p1 a plan * @param p2 a plan @@ -5845,8 +5850,8 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p5 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5) { @@ -5856,7 +5861,7 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan /** * Joins together the results from several patterns. *

    - * + * * * @param p1 a plan * @param p2 a plan @@ -5866,8 +5871,8 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p6 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6) { @@ -5877,7 +5882,7 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan /** * Joins together the results from several patterns. *

    - * + * * * @param p1 a plan * @param p2 a plan @@ -5888,8 +5893,8 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p7 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7) { @@ -5899,7 +5904,7 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan /** * Joins together the results from several patterns. *

    - * + * * * @param p1 a plan * @param p2 a plan @@ -5911,8 +5916,8 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p8 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8) { @@ -5922,7 +5927,7 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan /** * Joins together the results from several patterns. *

    - * + * * * @param p1 a plan * @param p2 a plan @@ -5935,11 +5940,183 @@ public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan * @param p9 a plan * @return an Observable sequence with the results from matching several * patterns - * @see when() - * @see MSDN: Observable.When + * @see RxJava Wiki: when() + * @see MSDN: Observable.When */ @SuppressWarnings("unchecked") public static Observable when(Plan0 p1, Plan0 p2, Plan0 p3, Plan0 p4, Plan0 p5, Plan0 p6, Plan0 p7, Plan0 p8, Plan0 p9) { return create(OperationJoinPatterns.when(p1, p2, p3, p4, p5, p6, p7, p8, p9)); } + + /** + * Correlates the elements of two sequences based on overlapping durations. + *

    + * + * + * @param right the right observable sequence to join elements for + * @param leftDurationSelector a function to select the duration of each + * element of this observable sequence, used to + * determine overlap + * @param rightDurationSelector a function to select the duration of each + * element of the right observable sequence, + * used to determine overlap + * @param resultSelector a function invoked to compute a result element + * for any two overlapping elements of the left and + * right observable sequences + * @return an observable sequence that contains result elements computed + * from source elements that have an overlapping duration + * @see RxJava Wiki: join() + * @see MSDN: Observable.Join + */ + public Observable join(Observable right, Func1> leftDurationSelector, + Func1> rightDurationSelector, + Func2 resultSelector) { + return create(new OperationJoin(this, right, leftDurationSelector, rightDurationSelector, resultSelector)); + } + + /** + * Return an Observable that emits a single HashMap containing all items + * emitted by the source Observable, mapped by the keys returned by the + * {@code keySelector} function. + *

    + * + * + * If a source item maps to the same key, the HashMap will contain the + * latest of those items. + * + * @param keySelector the function that extracts the key from the source + * items to be used as keys in the HashMap + * @return an Observable that emits a single HashMap containing the mapped + * values of the source Observable + * @see RxJava Wiki: toMap() + * @see MSDN: Observable.ToDictionary + */ + public Observable> toMap(Func1 keySelector) { + return create(OperationToMap.toMap(this, keySelector)); + } + + /** + * Return an Observable that emits a single HashMap containing elements with + * key and value extracted from the values emitted by the source Observable. + *

    + * + *

    + * If a source item maps to the same key, the HashMap will contain the + * latest of those items. + * + * @param keySelector the function that extracts the key from the source + * items to be used as key in the HashMap + * @param valueSelector the function that extracts the value from the source + * items to be used as value in the HashMap + * @return an Observable that emits a single HashMap containing the mapped + * values of the source Observable + * @see RxJava Wiki: toMap() + * @see MSDN: Observable.ToDictionary + */ + public Observable> toMap(Func1 keySelector, Func1 valueSelector) { + return create(OperationToMap.toMap(this, keySelector, valueSelector)); + } + + /** + * Return an Observable that emits a single Map, returned by the + * mapFactory function, containing key and value extracted from + * the values emitted by the source Observable. + *

    + * + * + * @param keySelector the function that extracts the key from the source + * items to be used as key in the Map + * @param valueSelector the function that extracts the value from the source + * items to be used as value in the Map + * @param mapFactory the function that returns an Map instance to be used + * @return an Observable that emits a single Map containing the mapped + * values of the source Observable + * @see RxJava Wiki: toMap() + */ + public Observable> toMap(Func1 keySelector, Func1 valueSelector, Func0> mapFactory) { + return create(OperationToMap.toMap(this, keySelector, valueSelector, mapFactory)); + } + + /** + * Return an Observable that emits a single HashMap containing an ArrayList + * of elements, emitted by the source Observable and keyed by the + * keySelector function. + *

    + * + * + * @param keySelector the function that extracts the key from the source + * items to be used as key in the HashMap + * @return an Observable that emits a single HashMap containing an ArrayList + * of elements mapped from the source Observable + * @see RxJava Wiki: toMultiMap() + * @see MSDN: Observable.ToLookup + */ + public Observable>> toMultimap(Func1 keySelector) { + return create(OperationToMultimap.toMultimap(this, keySelector)); + } + + /** + * Return an Observable that emits a single HashMap containing an ArrayList + * of values, extracted by the valueSelector function, emitted + * by the source Observable and keyed by the keySelector + * function. + *

    + * + * + * @param keySelector the function that extracts the key from the source + * items to be used as key in the HashMap + * @param valueSelector the function that extracts the value from the source + * items to be used as value in the Map + * @return an Observable that emits a single HashMap containing an ArrayList + * of elements mapped from the source Observable + * @see RxJava Wiki: toMultiMap() + * @see MSDN: Observable.ToLookup + */ + public Observable>> toMultimap(Func1 keySelector, Func1 valueSelector) { + return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector)); + } + + /** + * Return an Observable that emits a single Map, returned by the + * mapFactory function, containing an ArrayList of values, + * extracted by the valueSelector function, emitted by the + * source Observable and keyed by the keySelector function. + *

    + * + * + * @param keySelector the function that extracts the key from the source + * items to be used as key in the Map + * @param valueSelector the function that extracts the value from the source + * items to be used as value in the Map + * @param mapFactory the function that returns an Map instance to be used + * @return an Observable that emits a single Map containing the list of + * mapped values of the source observable. + * @see RxJava Wiki: toMultiMap() + */ + public Observable>> toMultimap(Func1 keySelector, Func1 valueSelector, Func0>> mapFactory) { + return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector, mapFactory)); + } + + /** + * Return an Observable that emits a single Map, returned by the + * mapFactory function, containing a custom collection of + * values, extracted by the valueSelector function, emitted by + * the source Observable and keyed by the keySelector function. + *

    + * + * + * @param keySelector the function that extracts the key from the source + * items to be used as key in the Map + * @param valueSelector the function that extracts the value from the source + * items to be used as value in the Map + * @param mapFactory the function that returns an Map instance to be used + * @param collectionFactory the function that returns a Collection instance + * for a particular key to be used in the Map + * @return an Observable that emits a single Map containing the collection + * of mapped values of the source Observable. + * @see RxJava Wiki: toMultiMap() + */ + public Observable>> toMultimap(Func1 keySelector, Func1 valueSelector, Func0>> mapFactory, Func1> collectionFactory) { + return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector, mapFactory, collectionFactory)); + } } From 2192f203e8029166122ce040681907479d90c781 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 26 Nov 2013 11:17:28 -0800 Subject: [PATCH 318/333] Missing ! .... grrr. --- .../src/main/scala/rx/lang/scala/Subscription.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala index 6826b03e94..bd3c6849a9 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala @@ -64,7 +64,7 @@ object Subscription { def isUnsubscribed = unsubscribed.get() val asJavaSubscription = new rx.Subscription { - def unsubscribe() { if(unsubscribed.get()) { u ; unsubscribed.set(true) }} + def unsubscribe() { if(!unsubscribed.get()) { u ; unsubscribed.set(true) }} } } } From 3f04560867ce79a9f305659ab71b4c7a2c0be69c Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 26 Nov 2013 11:21:42 -0800 Subject: [PATCH 319/333] Fix bad unit test - no further events expected after unsubscribe --- .../rx/lang/scala/examples/TestSchedulerExample.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/TestSchedulerExample.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/TestSchedulerExample.scala index df588c9689..55df673708 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/TestSchedulerExample.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/TestSchedulerExample.scala @@ -35,11 +35,14 @@ class TestSchedulerExample extends JUnitSuite { verify(observer, never).onCompleted() verify(observer, never).onError(any(classOf[Throwable])) + verify(observer, never).onNext(2L) + sub.unsubscribe(); + scheduler.advanceTimeTo(4 seconds) - verify(observer, never).onNext(2L) - verify(observer, times(1)).onCompleted() - verify(observer, never).onError(any(classOf[Throwable])) + + // after unsubscription we expect no further events + verifyNoMoreInteractions(observer) } } From 3fe31fb63e7bf75a3d4cac023622d1adbabb1e00 Mon Sep 17 00:00:00 2001 From: headinthebox Date: Tue, 26 Nov 2013 10:47:55 -0800 Subject: [PATCH 320/333] Calling constructor explicitly on schedulers --- .../rx/lang/scala/concurrency/Schedulers.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala index 8ba88ba2d0..f2bbd5d6fa 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala @@ -13,29 +13,29 @@ object Schedulers { /** * Returns a [[rx.lang.scala.Scheduler]] that executes work immediately on the current thread. */ - def immediate: Scheduler = rx.concurrency.Schedulers.immediate() + def immediate: Scheduler = Scheduler(rx.concurrency.Schedulers.immediate()) /** * Returns a [[rx.lang.scala.Scheduler]] that queues work on the current thread to be executed after the current work completes. */ - def currentThread: Scheduler = rx.concurrency.Schedulers.currentThread() + def currentThread: Scheduler = Scheduler(rx.concurrency.Schedulers.currentThread()) /** * Returns a [[rx.lang.scala.Scheduler]] that creates a new {@link Thread} for each unit of work. */ - def newThread: Scheduler = rx.concurrency.Schedulers.newThread + def newThread: Scheduler = Scheduler(rx.concurrency.Schedulers.newThread) /** * Returns a [[rx.lang.scala.Scheduler]] that queues work on an `java.util.concurrent.Executor`. * * Note that this does not support scheduled actions with a delay. */ - def executor(executor: Executor): Scheduler = rx.concurrency.Schedulers.executor(executor) + def executor(executor: Executor): Scheduler = Scheduler(rx.concurrency.Schedulers.executor(executor)) /** * Returns a [[rx.lang.scala.Scheduler]] that queues work on an `java.util.concurrent.ScheduledExecutorService`. */ - def executor(executor: ScheduledExecutorService): Scheduler = rx.concurrency.Schedulers.executor(executor) + def executor(executor: ScheduledExecutorService): Scheduler = Scheduler(rx.concurrency.Schedulers.executor(executor)) /** * Returns a [[rx.lang.scala.Scheduler]] intended for computational work. @@ -46,7 +46,7 @@ object Schedulers { * * Do not perform IO-bound work on this scheduler. Use [[rx.lang.scala.concurrency.Schedulers.threadPoolForIO]] instead. */ - def threadPoolForComputation: Scheduler = rx.concurrency.Schedulers.threadPoolForComputation() + def threadPoolForComputation: Scheduler = Scheduler(rx.concurrency.Schedulers.threadPoolForComputation()) /** * [[rx.lang.scala.Scheduler]] intended for IO-bound work. @@ -57,6 +57,6 @@ object Schedulers { * * Do not perform computational work on this scheduler. Use [[rx.lang.scala.concurrency.Schedulers.threadPoolForComputation]] instead. */ - def threadPoolForIO: Scheduler = rx.concurrency.Schedulers.threadPoolForIO() + def threadPoolForIO: Scheduler = Scheduler(rx.concurrency.Schedulers.threadPoolForIO()) } From 0c7dc06502679caf512a502545238407d71e5afa Mon Sep 17 00:00:00 2001 From: headinthebox Date: Tue, 26 Nov 2013 11:07:03 -0800 Subject: [PATCH 321/333] Notification constructors Implicit conversions --- .../lang/scala/ImplicitFunctionConversions.scala | 9 ++++++--- .../main/scala/rx/lang/scala/Notification.scala | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index 6a64c858e8..ead475bda9 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -39,6 +39,9 @@ object ImplicitFunctionConversions { } } + implicit def toJavaNotification[T](s: Notification[T]): rx.Notification[_ <: T] = s.asJava + implicit def toScalaNotification[T](s: rx.Notification[_ <: T]): Notification[T] = Notification(s) + implicit def toJavaSubscription(s: Subscription): rx.Subscription = s.asJavaSubscription implicit def toScalaSubscription(s: rx.Subscription): Subscription = Subscription(s) @@ -46,11 +49,11 @@ object ImplicitFunctionConversions { implicit def javaSchedulerToScalaScheduler(s: rx.Scheduler): Scheduler = Scheduler(s) implicit def toJavaObserver[T](s: Observer[T]): rx.Observer[_ >: T] = s.asJavaObserver - implicit def toScalaObserver[T](s: rx.Observer[T]): Observer[T] = Observer(s) + implicit def toScalaObserver[T](s: rx.Observer[_ >: T]): Observer[T] = Observer(s) implicit def toJavaObservable[T](s: Observable[T]): rx.Observable[_ <: T] = s.asJavaObservable - implicit def toScalaObservable[T](s: rx.Observable[T]): Observable[T] = Observable(s) - + implicit def toScalaObservable[T](s: rx.Observable[_ <: T]): Observable[T] = Observable(s) + implicit def scalaFunction1ToOnSubscribeFunc[T](f: rx.lang.scala.Observer[T] => Subscription) = new rx.Observable.OnSubscribeFunc[T] { def onSubscribe(obs: rx.Observer[_ >: T]): rx.Subscription = { diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala index 430cfd8e80..718a733956 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala @@ -36,6 +36,11 @@ object Notification { } object OnNext { + + def apply[T](value: T): Notification[T] = { + Notification(new rx.Notification[T](value)) + } + def unapply[U](n: Notification[U]): Option[U] = n match { case n2: OnNext[U] => Some(n.asJava.getValue) case _ => None @@ -47,6 +52,11 @@ object Notification { } object OnError { + + def apply[T](error: Throwable): Notification[T] = { + Notification(new rx.Notification[T](error)) + } + def unapply[U](n: Notification[U]): Option[Throwable] = n match { case n2: OnError[U] => Some(n2.asJava.getThrowable) case _ => None @@ -56,6 +66,11 @@ object Notification { class OnCompleted[T](val asJava: rx.Notification[_ <: T]) extends Notification[T] {} object OnCompleted { + + def apply[T](): Notification[T] = { + Notification(new rx.Notification()) + } + def unapply[U](n: Notification[U]): Option[Unit] = n match { case n2: OnCompleted[U] => Some() case _ => None From 46db81669ee418645d004898ebb29e364e824bcd Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 26 Nov 2013 11:28:10 -0800 Subject: [PATCH 322/333] Add License Headers --- .../main/scala/rx/lang/scala/Notification.scala | 15 +++++++++++++++ .../src/main/scala/rx/lang/scala/Observer.scala | 15 +++++++++++++++ .../src/main/scala/rx/lang/scala/Scheduler.scala | 15 +++++++++++++++ .../main/scala/rx/lang/scala/WithFilter.scala | 15 +++++++++++++++ .../rx/lang/scala/concurrency/Schedulers.scala | 15 +++++++++++++++ .../lang/scala/concurrency/TestScheduler.scala | 15 +++++++++++++++ .../rx/lang/scala/observables/package.scala | 15 +++++++++++++++ .../rx/lang/scala/subjects/AsyncSubject.scala | 15 +++++++++++++++ .../rx/lang/scala/subjects/BehaviorSubject.scala | 15 +++++++++++++++ .../rx/lang/scala/subjects/PublishSubject.scala | 15 +++++++++++++++ .../rx/lang/scala/subjects/ReplaySubject.scala | 15 +++++++++++++++ .../scala/rx/lang/scala/subjects/Subject.scala | 15 +++++++++++++++ .../scala/rx/lang/scala/subjects/package.scala | 15 +++++++++++++++ .../subscriptions/BooleanSubscription.scala | 15 +++++++++++++++ .../subscriptions/CompositeSubscription.scala | 15 +++++++++++++++ .../MultiAssignmentSubscription.scala | 15 +++++++++++++++ .../scala/subscriptions/SerialSubscription.scala | 15 +++++++++++++++ .../rx/lang/scala/subscriptions/package.scala | 15 +++++++++++++++ .../src/main/java/rx/operators/README.txt | 16 ++++++++++++++++ .../main/java/rx/subjects/AbstractSubject.java | 15 +++++++++++++++ 20 files changed, 301 insertions(+) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala index 718a733956..21491bc96b 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala index f66ea52c57..73f865ef84 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala index 2142b3d402..4c84eed840 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala import java.util.Date diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala index d8524825ba..729450d2a5 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala import ImplicitFunctionConversions.scalaBooleanFunction1ToRxBooleanFunc1 diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala index f2bbd5d6fa..cbba7fd1db 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.concurrency import java.util.concurrent.Executor diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala index e2873f9a29..f7b7e4beba 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.concurrency import scala.concurrent.duration.Duration diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala index 8507f0a54c..2b43860b53 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala index ad23ce841c..80892b9ac1 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.subjects import rx.lang.scala.Subject diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala index fdf873f096..5b358aba9a 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.subjects import rx.lang.scala.Subject diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala index a5fd50af29..7c06101460 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.subjects import rx.lang.scala.Subject diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala index 6d4698e163..f88fb65280 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.subjects import rx.lang.scala.Subject diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala index cb92df90d9..08ba9e404c 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala index cf7db56f11..7566d5fad8 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala index c65049a12d..be2cb5f392 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.subscriptions import rx.lang.scala._ diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala index 6437013a4f..1fe4a4afa5 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.subscriptions import rx.lang.scala._ diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala index 7955766810..84740870c7 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.subscriptions import rx.lang.scala._ diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala index c54a39b58d..94b50f93e8 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala.subscriptions import rx.lang.scala.Subscription diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/package.scala index 4662cb9ccb..c54f83c982 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/package.scala @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.lang.scala /** diff --git a/rxjava-core/src/main/java/rx/operators/README.txt b/rxjava-core/src/main/java/rx/operators/README.txt index c2d441a10c..1be4baa962 100644 --- a/rxjava-core/src/main/java/rx/operators/README.txt +++ b/rxjava-core/src/main/java/rx/operators/README.txt @@ -1,3 +1,19 @@ +==== + Copyright 2013 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. +==== + This package "rx.operators" is for internal implementation details and can change at any time. It is excluded from the public Javadocs (http://netflix.github.io/RxJava/javadoc/) and should not be relied upon by any code. diff --git a/rxjava-core/src/main/java/rx/subjects/AbstractSubject.java b/rxjava-core/src/main/java/rx/subjects/AbstractSubject.java index 62c015c027..30db331ac2 100644 --- a/rxjava-core/src/main/java/rx/subjects/AbstractSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/AbstractSubject.java @@ -1,3 +1,18 @@ +/** + * Copyright 2013 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.subjects; import java.util.ArrayList; From b8e6ccec27fcf47badd8d604cad3ba40e9f07465 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Tue, 26 Nov 2013 20:01:09 +0000 Subject: [PATCH 323/333] [Gradle Release Plugin] - pre tag commit: '0.15.1'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 85ec9eaec9..fbe8d6cfae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.15.1-SNAPSHOT +version=0.15.1 From de9db7bb112d3745edd23e99f8336daafbcdc2f7 Mon Sep 17 00:00:00 2001 From: Bob T Builder Date: Tue, 26 Nov 2013 20:01:13 +0000 Subject: [PATCH 324/333] [Gradle Release Plugin] - new version commit: '0.15.2-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index fbe8d6cfae..92fc8fd0ca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.15.1 +version=0.15.2-SNAPSHOT From 13cd6a9b1813c5dbe5fdd538f18a5f3a2e31e46f Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 26 Nov 2013 12:17:23 -0800 Subject: [PATCH 325/333] Version 0.15.1 --- CHANGES.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index bed49e2bcb..8e30cb39fd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,19 @@ # RxJava Releases # +### Version 0.15.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.15.1%22)) ### + +This release should be additive functionality and bug fixes. + +* [Pull 510](https://github.com/Netflix/RxJava/pull/506) Operators: And, Then, When +* [Pull 514](https://github.com/Netflix/RxJava/pull/514) Operator: Join +* [Pull 525](https://github.com/Netflix/RxJava/pull/526) Operators: toMap/toMultiMap +* [Pull 510](https://github.com/Netflix/RxJava/pull/510) BugFix: Zip +* [Pull 512](https://github.com/Netflix/RxJava/pull/512) Scala Adaptor Details +* [Pull 512](https://github.com/Netflix/RxJava/pull/529) Scala fixes +* [Pull 508](https://github.com/Netflix/RxJava/pull/508) Empty subscribe +* [Pull 522](https://github.com/Netflix/RxJava/pull/522) Unsubscribe from takeLast +* [Pull 525](https://github.com/Netflix/RxJava/pull/525) BugFix: Handling of Terminal State for Behavior/Publish Subjects + ### Version 0.15.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.15.0%22)) ### This release contains a refactor of the Scala Bindings by @headinthebox that results in some breaking changes. From b8a4ba140c6feb3803f77e1d7b0df02ad9e2c464 Mon Sep 17 00:00:00 2001 From: jmhofer Date: Sat, 14 Sep 2013 11:40:29 +0200 Subject: [PATCH 326/333] added initial delay operation implementation --- .../java/rx/operators/OperationDelay.java | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/operators/OperationDelay.java diff --git a/rxjava-core/src/main/java/rx/operators/OperationDelay.java b/rxjava-core/src/main/java/rx/operators/OperationDelay.java new file mode 100644 index 0000000000..9f572c9c0b --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperationDelay.java @@ -0,0 +1,157 @@ +/** + * Copyright 2013 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.operators; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.initMocks; +import static rx.Observable.interval; + +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; + +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Scheduler; +import rx.Subscription; +import rx.concurrency.Schedulers; +import rx.concurrency.TestScheduler; +import rx.util.functions.Action0; + +/** + * Returns an Observable that emits the results of shifting the items emitted by the source + * Observable by a specified delay. + */ +public final class OperationDelay { + + /** + * Delays the observable sequence by the given time interval. + */ + public static OnSubscribeFunc delay(final Observable source, long delay, TimeUnit unit) { + return new Delay(source, delay, unit, Schedulers.executor(Executors.newSingleThreadScheduledExecutor())); + } + + /** + * Delays the observable sequence by the given time interval. + */ + public static OnSubscribeFunc delay(final Observable source, long period, TimeUnit unit, Scheduler scheduler) { + return new Delay(source, period, unit, scheduler); + } + + private static class Delay implements OnSubscribeFunc { + private final Observable source; + private final long delay; + private final TimeUnit unit; + private final Scheduler scheduler; + + private Delay(Observable source, long delay, TimeUnit unit, Scheduler scheduler) { + this.source = source; + this.delay = delay; + this.unit = unit; + this.scheduler = scheduler; + } + + @Override + public Subscription onSubscribe(final Observer observer) { + return source.subscribe(new Observer() { + @Override + public void onCompleted() { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onCompleted(); + } + }, delay, unit); + } + + @Override + public void onError(Throwable e) { + // errors get propagated without delay + observer.onError(e); + } + + @Override + public void onNext(final T value) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onNext(value); + } + }, delay, unit); + } + }); + } + } + + public static class UnitTest { + @Mock + private Observer observer; + + private TestScheduler scheduler; + + @Before + public void before() { + initMocks(this); + scheduler = new TestScheduler(); + } + + @Test + public void testDelay() { + Observable source = interval(1L, TimeUnit.SECONDS, scheduler).take(3); + Observable delayed = Observable.create(OperationDelay.delay(source, 500L, TimeUnit.MILLISECONDS, scheduler)); + delayed.subscribe(observer); + + InOrder inOrder = inOrder(observer); + scheduler.advanceTimeTo(1499L, TimeUnit.MILLISECONDS); + verify(observer, never()).onNext(any(Long.class)); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(1500L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(0L); + inOrder.verify(observer, never()).onNext(anyLong()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(2400L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyLong()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(2500L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(1L); + inOrder.verify(observer, never()).onNext(anyLong()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(3400L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyLong()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(3500L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(2L); + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + } +} From d2505bb7c6736612dad54ae42f9437044f1767d1 Mon Sep 17 00:00:00 2001 From: jmhofer Date: Sat, 14 Sep 2013 12:16:31 +0200 Subject: [PATCH 327/333] more tests against delay, found and fixed a bug with error handling in interval, too --- .../java/rx/operators/OperationDelay.java | 100 +++++++-- .../java/rx/operators/OperationInterval.java | 201 +++++++++++++++++- 2 files changed, 286 insertions(+), 15 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationDelay.java b/rxjava-core/src/main/java/rx/operators/OperationDelay.java index 9f572c9c0b..f3e60bcfb2 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDelay.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDelay.java @@ -22,6 +22,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Before; import org.junit.Test; @@ -36,6 +37,7 @@ import rx.concurrency.Schedulers; import rx.concurrency.TestScheduler; import rx.util.functions.Action0; +import rx.util.functions.Func1; /** * Returns an Observable that emits the results of shifting the items emitted by the source @@ -73,14 +75,18 @@ private Delay(Observable source, long delay, TimeUnit unit, Schedul @Override public Subscription onSubscribe(final Observer observer) { return source.subscribe(new Observer() { + private AtomicBoolean errorOccurred = new AtomicBoolean(); + @Override public void onCompleted() { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onCompleted(); - } - }, delay, unit); + if (!errorOccurred.get()) { + scheduler.schedule(new Action0() { + @Override + public void call() { + observer.onCompleted(); + } + }, delay, unit); + } } @Override @@ -91,12 +97,19 @@ public void onError(Throwable e) { @Override public void onNext(final T value) { - scheduler.schedule(new Action0() { - @Override - public void call() { - observer.onNext(value); - } - }, delay, unit); + if (!errorOccurred.get()) { + scheduler.schedule(new Action0() { + @Override + public void call() { + try { + observer.onNext(value); + } catch (Throwable t) { + errorOccurred.set(true); + observer.onError(t); + } + } + }, delay, unit); + } } }); } @@ -122,7 +135,7 @@ public void testDelay() { InOrder inOrder = inOrder(observer); scheduler.advanceTimeTo(1499L, TimeUnit.MILLISECONDS); - verify(observer, never()).onNext(any(Long.class)); + verify(observer, never()).onNext(anyLong()); verify(observer, never()).onCompleted(); verify(observer, never()).onError(any(Throwable.class)); @@ -153,5 +166,66 @@ public void testDelay() { verify(observer, times(1)).onCompleted(); verify(observer, never()).onError(any(Throwable.class)); } + + @Test + public void testLongDelay() { + Observable source = interval(1L, TimeUnit.SECONDS, scheduler).take(3); + Observable delayed = Observable.create(OperationDelay.delay(source, 5L, TimeUnit.SECONDS, scheduler)); + delayed.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(5999L, TimeUnit.MILLISECONDS); + verify(observer, never()).onNext(anyLong()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(6000L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(0L); + scheduler.advanceTimeTo(6999L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyLong()); + scheduler.advanceTimeTo(7000L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(1L); + scheduler.advanceTimeTo(7999L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyLong()); + scheduler.advanceTimeTo(8000L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(2L); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder.verify(observer, never()).onNext(anyLong()); + inOrder.verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + + @Test + public void testDelayWithError() { + Observable source = interval(1L, TimeUnit.SECONDS, scheduler).map(new Func1() { + @Override + public Long call(Long value) { + if (value == 1L) { + throw new RuntimeException("error!"); + } + return value; + } + }); + Observable delayed = Observable.create(OperationDelay.delay(source, 1L, TimeUnit.SECONDS, scheduler)); + delayed.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(1999L, TimeUnit.MILLISECONDS); + verify(observer, never()).onNext(anyLong()); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(2000L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onError(any(Throwable.class)); + inOrder.verify(observer, never()).onNext(anyLong()); + verify(observer, never()).onCompleted(); + + scheduler.advanceTimeTo(5000L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyLong()); + inOrder.verify(observer, never()).onError(any(Throwable.class)); + verify(observer, never()).onCompleted(); + } } } diff --git a/rxjava-core/src/main/java/rx/operators/OperationInterval.java b/rxjava-core/src/main/java/rx/operators/OperationInterval.java index 72d35d37de..d7fc61b9b2 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationInterval.java +++ b/rxjava-core/src/main/java/rx/operators/OperationInterval.java @@ -24,6 +24,7 @@ import rx.concurrency.Schedulers; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; +import rx.util.functions.Func1; /** * Returns an observable sequence that produces a value after each period. @@ -57,6 +58,7 @@ private static class Interval implements OnSubscribeFunc { private final Scheduler scheduler; private long currentValue; + private boolean errorOccurred; private Interval(long period, TimeUnit unit, Scheduler scheduler) { this.period = period; @@ -69,8 +71,15 @@ public Subscription onSubscribe(final Observer observer) { final Subscription wrapped = scheduler.schedulePeriodically(new Action0() { @Override public void call() { - observer.onNext(currentValue); - currentValue++; + if (!errorOccurred) { + try { + observer.onNext(currentValue); + currentValue++; + } catch (Throwable t) { + errorOccurred = true; + observer.onError(t); + } + } } }, period, period, unit); @@ -78,8 +87,196 @@ public void call() { @Override public void call() { wrapped.unsubscribe(); + if (!errorOccurred) { + observer.onCompleted(); + } } }); } } + + public static class UnitTest { + private TestScheduler scheduler; + private Observer observer; + private Observer observer2; + + @Before + @SuppressWarnings("unchecked") // due to mocking + public void before() { + scheduler = new TestScheduler(); + observer = mock(Observer.class); + observer2 = mock(Observer.class); + } + + @Test + public void testInterval() { + Observable w = Observable.create(interval(1, TimeUnit.SECONDS, scheduler)); + Subscription sub = w.subscribe(observer); + + verify(observer, never()).onNext(0L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(2, TimeUnit.SECONDS); + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer, times(1)).onNext(0L); + inOrder.verify(observer, times(1)).onNext(1L); + inOrder.verify(observer, never()).onNext(2L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + sub.unsubscribe(); + scheduler.advanceTimeTo(4, TimeUnit.SECONDS); + verify(observer, never()).onNext(2L); + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + } + + @Test + public void testIntervalWithError() { + Observable w = Observable.create(interval(1, TimeUnit.SECONDS, scheduler)).map(new Func1() { + @Override + public Long call(Long value) { + if (value == 2L) { + throw new RuntimeException("error!"); + } + return value; + } + }); + w.subscribe(observer); + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(2, TimeUnit.SECONDS); + inOrder.verify(observer, times(1)).onNext(0L); + inOrder.verify(observer, times(1)).onNext(1L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + scheduler.advanceTimeTo(3, TimeUnit.SECONDS); + inOrder.verify(observer, never()).onNext(anyLong()); + inOrder.verify(observer, times(1)).onError(any(RuntimeException.class)); + verify(observer, never()).onCompleted(); + } + + @Test + public void testWithMultipleSubscribersStartingAtSameTime() { + Observable w = Observable.create(interval(1, TimeUnit.SECONDS, scheduler)); + Subscription sub1 = w.subscribe(observer); + Subscription sub2 = w.subscribe(observer2); + + verify(observer, never()).onNext(anyLong()); + verify(observer2, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(2, TimeUnit.SECONDS); + + InOrder inOrder1 = inOrder(observer); + InOrder inOrder2 = inOrder(observer2); + + inOrder1.verify(observer, times(1)).onNext(0L); + inOrder1.verify(observer, times(1)).onNext(1L); + inOrder1.verify(observer, never()).onNext(2L); + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + inOrder2.verify(observer2, times(1)).onNext(0L); + inOrder2.verify(observer2, times(1)).onNext(1L); + inOrder2.verify(observer2, never()).onNext(2L); + verify(observer2, never()).onCompleted(); + verify(observer2, never()).onError(any(Throwable.class)); + + sub1.unsubscribe(); + sub2.unsubscribe(); + scheduler.advanceTimeTo(4, TimeUnit.SECONDS); + + verify(observer, never()).onNext(2L); + verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + verify(observer2, never()).onNext(2L); + verify(observer2, times(1)).onCompleted(); + verify(observer2, never()).onError(any(Throwable.class)); + } + + @Test + public void testWithMultipleStaggeredSubscribers() { + Observable w = Observable.create(interval(1, TimeUnit.SECONDS, scheduler)); + Subscription sub1 = w.subscribe(observer); + + verify(observer, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(2, TimeUnit.SECONDS); + Subscription sub2 = w.subscribe(observer2); + + InOrder inOrder1 = inOrder(observer); + inOrder1.verify(observer, times(1)).onNext(0L); + inOrder1.verify(observer, times(1)).onNext(1L); + inOrder1.verify(observer, never()).onNext(2L); + + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + verify(observer2, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(4, TimeUnit.SECONDS); + + inOrder1.verify(observer, times(1)).onNext(2L); + inOrder1.verify(observer, times(1)).onNext(3L); + + InOrder inOrder2 = inOrder(observer2); + inOrder2.verify(observer2, times(1)).onNext(0L); + inOrder2.verify(observer2, times(1)).onNext(1L); + + sub1.unsubscribe(); + sub2.unsubscribe(); + + inOrder1.verify(observer, never()).onNext(anyLong()); + inOrder1.verify(observer, times(1)).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + inOrder2.verify(observer2, never()).onNext(anyLong()); + inOrder2.verify(observer2, times(1)).onCompleted(); + verify(observer2, never()).onError(any(Throwable.class)); + } + + @Test + public void testWithMultipleStaggeredSubscribersAndPublish() { + ConnectableObservable w = Observable.create(interval(1, TimeUnit.SECONDS, scheduler)).publish(); + Subscription sub1 = w.subscribe(observer); + w.connect(); + + verify(observer, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(2, TimeUnit.SECONDS); + Subscription sub2 = w.subscribe(observer2); + + InOrder inOrder1 = inOrder(observer); + inOrder1.verify(observer, times(1)).onNext(0L); + inOrder1.verify(observer, times(1)).onNext(1L); + inOrder1.verify(observer, never()).onNext(2L); + + verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + verify(observer2, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(4, TimeUnit.SECONDS); + + inOrder1.verify(observer, times(1)).onNext(2L); + inOrder1.verify(observer, times(1)).onNext(3L); + + InOrder inOrder2 = inOrder(observer2); + inOrder2.verify(observer2, times(1)).onNext(2L); + inOrder2.verify(observer2, times(1)).onNext(3L); + + sub1.unsubscribe(); + sub2.unsubscribe(); + + inOrder1.verify(observer, never()).onNext(anyLong()); + inOrder1.verify(observer, never()).onCompleted(); + verify(observer, never()).onError(any(Throwable.class)); + + inOrder2.verify(observer2, never()).onNext(anyLong()); + inOrder2.verify(observer2, never()).onCompleted(); + verify(observer2, never()).onError(any(Throwable.class)); + } + } } From be33e383d4338ad8c972dcbbf5478e4d68c881d9 Mon Sep 17 00:00:00 2001 From: jmhofer Date: Sat, 14 Sep 2013 12:40:12 +0200 Subject: [PATCH 328/333] added test against multiple subscriptions --- .../java/rx/operators/OperationDelay.java | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationDelay.java b/rxjava-core/src/main/java/rx/operators/OperationDelay.java index f3e60bcfb2..c9b25d9bcc 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDelay.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDelay.java @@ -55,7 +55,7 @@ public static OnSubscribeFunc delay(final Observable source, /** * Delays the observable sequence by the given time interval. */ - public static OnSubscribeFunc delay(final Observable source, long period, TimeUnit unit, Scheduler scheduler) { + public static OnSubscribeFunc delay(final Observable source, final long period, final TimeUnit unit, final Scheduler scheduler) { return new Delay(source, period, unit, scheduler); } @@ -92,6 +92,7 @@ public void call() { @Override public void onError(Throwable e) { // errors get propagated without delay + errorOccurred.set(true); observer.onError(e); } @@ -118,6 +119,8 @@ public void call() { public static class UnitTest { @Mock private Observer observer; + @Mock + private Observer observer2; private TestScheduler scheduler; @@ -227,5 +230,46 @@ public Long call(Long value) { inOrder.verify(observer, never()).onError(any(Throwable.class)); verify(observer, never()).onCompleted(); } + + @Test + public void testDelayWithMultipleSubscriptions() { + Observable source = interval(1L, TimeUnit.SECONDS, scheduler).take(3); + Observable delayed = Observable.create(OperationDelay.delay(source, 500L, TimeUnit.MILLISECONDS, scheduler)); + delayed.subscribe(observer); + delayed.subscribe(observer2); + + InOrder inOrder = inOrder(observer); + InOrder inOrder2 = inOrder(observer2); + + scheduler.advanceTimeTo(1499L, TimeUnit.MILLISECONDS); + verify(observer, never()).onNext(anyLong()); + verify(observer2, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(1500L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(0L); + inOrder2.verify(observer2, times(1)).onNext(0L); + + scheduler.advanceTimeTo(2499L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, never()).onNext(anyLong()); + inOrder2.verify(observer2, never()).onNext(anyLong()); + + scheduler.advanceTimeTo(2500L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(1L); + inOrder2.verify(observer2, times(1)).onNext(1L); + + verify(observer, never()).onCompleted(); + verify(observer2, never()).onCompleted(); + + scheduler.advanceTimeTo(3500L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(2L); + inOrder2.verify(observer2, times(1)).onNext(2L); + inOrder.verify(observer, never()).onNext(anyLong()); + inOrder2.verify(observer2, never()).onNext(anyLong()); + inOrder.verify(observer, times(1)).onCompleted(); + inOrder2.verify(observer2, times(1)).onCompleted(); + + verify(observer, never()).onError(any(Throwable.class)); + verify(observer2, never()).onError(any(Throwable.class)); + } } } From b5e80fd88e7f14478489ec86f129654cdb35ab97 Mon Sep 17 00:00:00 2001 From: jmhofer Date: Sat, 14 Sep 2013 12:50:20 +0200 Subject: [PATCH 329/333] added due time delay and test against that --- .../java/rx/operators/OperationDelay.java | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationDelay.java b/rxjava-core/src/main/java/rx/operators/OperationDelay.java index c9b25d9bcc..a8605148a8 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDelay.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDelay.java @@ -20,6 +20,7 @@ import static org.mockito.MockitoAnnotations.initMocks; import static rx.Observable.interval; +import java.util.Date; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -49,7 +50,26 @@ public final class OperationDelay { * Delays the observable sequence by the given time interval. */ public static OnSubscribeFunc delay(final Observable source, long delay, TimeUnit unit) { - return new Delay(source, delay, unit, Schedulers.executor(Executors.newSingleThreadScheduledExecutor())); + return delay(source, delay, unit, Schedulers.executor(Executors.newSingleThreadScheduledExecutor())); + } + + /** + * Delays the observable sequence by a time interval so that it starts at the given due time. + */ + public static OnSubscribeFunc delay(final Observable source, Date dueTime) { + return delay(source, dueTime, Schedulers.executor(Executors.newSingleThreadScheduledExecutor())); + } + + /** + * Delays the observable sequence by a time interval so that it starts at the given due time. + */ + public static OnSubscribeFunc delay(final Observable source, Date dueTime, final Scheduler scheduler) { + long scheduledTime = dueTime.getTime(); + long delay = scheduledTime - scheduler.now(); + if (delay < 0L) { + delay = 0L; + } + return new Delay(source, delay, TimeUnit.MILLISECONDS, scheduler); } /** @@ -58,7 +78,7 @@ public static OnSubscribeFunc delay(final Observable source, public static OnSubscribeFunc delay(final Observable source, final long period, final TimeUnit unit, final Scheduler scheduler) { return new Delay(source, period, unit, scheduler); } - + private static class Delay implements OnSubscribeFunc { private final Observable source; private final long delay; @@ -170,6 +190,25 @@ public void testDelay() { verify(observer, never()).onError(any(Throwable.class)); } + @Test + public void testDelayWithDueTime() { + Observable source = interval(1L, TimeUnit.SECONDS, scheduler).first(); + Observable delayed = Observable.create(OperationDelay.delay(source, new Date(1500L), scheduler)); + delayed.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(2499L, TimeUnit.MILLISECONDS); + verify(observer, never()).onNext(anyLong()); + verify(observer, never()).onCompleted(); + + scheduler.advanceTimeTo(2500L, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext(0L); + inOrder.verify(observer, times(1)).onCompleted(); + + verify(observer, never()).onError(any(Throwable.class)); + } + @Test public void testLongDelay() { Observable source = interval(1L, TimeUnit.SECONDS, scheduler).take(3); From 874b5eed048169189df40daabfa73f986b2fb25b Mon Sep 17 00:00:00 2001 From: jmhofer Date: Sat, 14 Sep 2013 12:59:43 +0200 Subject: [PATCH 330/333] added delay methods to Observable and documented them --- rxjava-core/src/main/java/rx/Observable.java | 60 ++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 0cef17b1d3..18dbbf25e4 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Comparator; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -45,6 +46,7 @@ import rx.operators.OperationDebounce; import rx.operators.OperationDefaultIfEmpty; import rx.operators.OperationDefer; +import rx.operators.OperationDelay; import rx.operators.OperationDematerialize; import rx.operators.OperationDistinct; import rx.operators.OperationDistinctUntilChanged; @@ -4407,6 +4409,64 @@ public Observable scan(Func2 accumulator) { return create(OperationScan.scan(this, accumulator)); } + /** + * Returns an Observable that emits the results of shifting the items emitted by the source + * Observable by a specified delay. Only errors emitted by the source Observable are not delayed. + * @param delay + * the delay to shift the source by + * @param unit + * the {@link TimeUnit} in which period is defined + * @return the source Observable, but shifted by the specified delay + * @see MSDN: Observable.Delay + */ + public Observable delay(long delay, TimeUnit unit) { + return create(OperationDelay.delay(this, delay, unit)); + } + + /** + * Returns an Observable that emits the results of shifting the items emitted by the source + * Observable by a specified delay. Only errors emitted by the source Observable are not delayed. + * @param delay + * the delay to shift the source by + * @param unit + * the {@link TimeUnit} in which period is defined + * @param scheduler + * the {@link Scheduler} to use for delaying + * @return the source Observable, but shifted by the specified delay + * @see MSDN: Observable.Delay + */ + public Observable delay(long delay, TimeUnit unit, Scheduler scheduler) { + return create(OperationDelay.delay(this, delay, unit, scheduler)); + } + + /** + * Returns an Observable that emits the results of shifting the items emitted by the source + * Observable by a delay specified by the due time at which to begin emitting. + * Only errors emitted by the source Observable are not delayed. + * @param dueTime + * the due time at which to start emitting + * @return the source Observable, but shifted by the specified delay + * @see MSDN: Observable.Delay + */ + public Observable delay(Date dueTime) { + return create(OperationDelay.delay(this, dueTime)); + } + + /** + * Returns an Observable that emits the results of shifting the items emitted by the source + * Observable by a delay specified by the due time at which to begin emitting. + * Only errors emitted by the source Observable are not delayed. + * @param dueTime + * the due time at which to start emitting + * @param scheduler + * the {@link Scheduler} to use for delaying + * @return the source Observable, but shifted by the specified delay + * @see MSDN: Observable.Delay + */ + public Observable delay(Date dueTime, Scheduler scheduler) { + return create(OperationDelay.delay(this, dueTime, scheduler)); + } + /** * Returns an Observable that emits the results of sampling the items * emitted by the source Observable at a specified time interval. From 71cf5e7b5b2ade14bad0d8a807fab942b904aa55 Mon Sep 17 00:00:00 2001 From: jmhofer Date: Sun, 15 Sep 2013 09:46:36 +0200 Subject: [PATCH 331/333] now defaults to thread pool scheduling --- rxjava-core/src/main/java/rx/operators/OperationDelay.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationDelay.java b/rxjava-core/src/main/java/rx/operators/OperationDelay.java index a8605148a8..603be7080a 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDelay.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDelay.java @@ -50,14 +50,14 @@ public final class OperationDelay { * Delays the observable sequence by the given time interval. */ public static OnSubscribeFunc delay(final Observable source, long delay, TimeUnit unit) { - return delay(source, delay, unit, Schedulers.executor(Executors.newSingleThreadScheduledExecutor())); + return delay(source, delay, unit, Schedulers.threadPoolForComputation()); } /** * Delays the observable sequence by a time interval so that it starts at the given due time. */ public static OnSubscribeFunc delay(final Observable source, Date dueTime) { - return delay(source, dueTime, Schedulers.executor(Executors.newSingleThreadScheduledExecutor())); + return delay(source, dueTime, Schedulers.threadPoolForComputation()); } /** From 5a3fd2d141cd34a969237e943e22be2d8d49fd3a Mon Sep 17 00:00:00 2001 From: jmhofer Date: Sun, 15 Sep 2013 10:29:21 +0200 Subject: [PATCH 332/333] added a delayed scheduler --- .../java/rx/concurrency/DelayedScheduler.java | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 rxjava-core/src/main/java/rx/concurrency/DelayedScheduler.java diff --git a/rxjava-core/src/main/java/rx/concurrency/DelayedScheduler.java b/rxjava-core/src/main/java/rx/concurrency/DelayedScheduler.java new file mode 100644 index 0000000000..86aff00da0 --- /dev/null +++ b/rxjava-core/src/main/java/rx/concurrency/DelayedScheduler.java @@ -0,0 +1,114 @@ +/** + * Copyright 2013 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.concurrency; + +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; + +import rx.Scheduler; +import rx.Subscription; +import rx.util.functions.Action0; +import rx.util.functions.Func2; + +/** + * Scheduler that delays the underlying scheduler by a fixed time delay. + */ +public class DelayedScheduler extends Scheduler { + private final Scheduler underlying; + private final long delay; + private final TimeUnit unit; + + public DelayedScheduler(Scheduler underlying, long delay, TimeUnit unit) { + this.underlying = underlying; + this.delay = delay; + this.unit = unit; + } + + @Override + public Subscription schedule(T state, Func2 action) { + return underlying.schedule(state, action, delay, unit); + } + + @Override + public Subscription schedule(T state, Func2 action, long delay, TimeUnit unit) { + long newDelay = unit.toNanos(delay) + this.unit.toNanos(this.delay); + return underlying.schedule(state, action, newDelay, TimeUnit.NANOSECONDS); + } + + public static class UnitTest { + @Mock + Action0 action; + + private TestScheduler scheduler = new TestScheduler(); + + @Before + public void before() { + initMocks(this); + } + + @Test + public void testNotDelayingAnAction() { + Scheduler delayed = new DelayedScheduler(scheduler, 0, TimeUnit.SECONDS); + delayed.schedule(action); + delayed.schedule(action, 1L, TimeUnit.SECONDS); + + InOrder inOrder = inOrder(action); + + scheduler.triggerActions(); + inOrder.verify(action, times(1)).call(); + + scheduler.advanceTimeTo(999L, TimeUnit.MILLISECONDS); + inOrder.verify(action, never()).call(); + + scheduler.advanceTimeTo(1L, TimeUnit.SECONDS); + inOrder.verify(action, times(1)).call(); + + scheduler.advanceTimeTo(5L, TimeUnit.SECONDS); + inOrder.verify(action, never()).call(); + } + + @Test + public void testdelayingAnAction() { + Scheduler delayed = new DelayedScheduler(scheduler, 500, TimeUnit.MILLISECONDS); + delayed.schedule(action); + delayed.schedule(action, 1L, TimeUnit.SECONDS); + + InOrder inOrder = inOrder(action); + + scheduler.advanceTimeTo(499L, TimeUnit.MILLISECONDS); + inOrder.verify(action, never()).call(); + + scheduler.advanceTimeTo(500L, TimeUnit.MILLISECONDS); + inOrder.verify(action, times(1)).call(); + + scheduler.advanceTimeTo(1499L, TimeUnit.MILLISECONDS); + inOrder.verify(action, never()).call(); + + scheduler.advanceTimeTo(1500L, TimeUnit.MILLISECONDS); + inOrder.verify(action, times(1)).call(); + + scheduler.advanceTimeTo(5L, TimeUnit.SECONDS); + inOrder.verify(action, never()).call(); + } + } +} From e285e34af5dee34522566f69e38c52d6660f29fd Mon Sep 17 00:00:00 2001 From: Jon Nolen Date: Wed, 27 Nov 2013 13:56:28 -0500 Subject: [PATCH 333/333] fixing OperationInterval due to rebase error. --- .../java/rx/operators/OperationInterval.java | 193 +----------------- 1 file changed, 8 insertions(+), 185 deletions(-) diff --git a/rxjava-core/src/main/java/rx/operators/OperationInterval.java b/rxjava-core/src/main/java/rx/operators/OperationInterval.java index d7fc61b9b2..75e50c845d 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationInterval.java +++ b/rxjava-core/src/main/java/rx/operators/OperationInterval.java @@ -18,6 +18,8 @@ import java.util.concurrent.TimeUnit; import rx.Observable.OnSubscribeFunc; +import rx.Observable; +import rx.observables.ConnectableObservable; import rx.Observer; import rx.Scheduler; import rx.Subscription; @@ -25,6 +27,12 @@ import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; import rx.util.functions.Func1; +import rx.concurrency.TestScheduler; + +import org.junit.Before; +import org.junit.Test; +import static org.mockito.Mockito.*; +import org.mockito.InOrder; /** * Returns an observable sequence that produces a value after each period. @@ -94,189 +102,4 @@ public void call() { }); } } - - public static class UnitTest { - private TestScheduler scheduler; - private Observer observer; - private Observer observer2; - - @Before - @SuppressWarnings("unchecked") // due to mocking - public void before() { - scheduler = new TestScheduler(); - observer = mock(Observer.class); - observer2 = mock(Observer.class); - } - - @Test - public void testInterval() { - Observable w = Observable.create(interval(1, TimeUnit.SECONDS, scheduler)); - Subscription sub = w.subscribe(observer); - - verify(observer, never()).onNext(0L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - - InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(0L); - inOrder.verify(observer, times(1)).onNext(1L); - inOrder.verify(observer, never()).onNext(2L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - sub.unsubscribe(); - scheduler.advanceTimeTo(4, TimeUnit.SECONDS); - verify(observer, never()).onNext(2L); - verify(observer, times(1)).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - } - - @Test - public void testIntervalWithError() { - Observable w = Observable.create(interval(1, TimeUnit.SECONDS, scheduler)).map(new Func1() { - @Override - public Long call(Long value) { - if (value == 2L) { - throw new RuntimeException("error!"); - } - return value; - } - }); - w.subscribe(observer); - InOrder inOrder = inOrder(observer); - - scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - inOrder.verify(observer, times(1)).onNext(0L); - inOrder.verify(observer, times(1)).onNext(1L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - scheduler.advanceTimeTo(3, TimeUnit.SECONDS); - inOrder.verify(observer, never()).onNext(anyLong()); - inOrder.verify(observer, times(1)).onError(any(RuntimeException.class)); - verify(observer, never()).onCompleted(); - } - - @Test - public void testWithMultipleSubscribersStartingAtSameTime() { - Observable w = Observable.create(interval(1, TimeUnit.SECONDS, scheduler)); - Subscription sub1 = w.subscribe(observer); - Subscription sub2 = w.subscribe(observer2); - - verify(observer, never()).onNext(anyLong()); - verify(observer2, never()).onNext(anyLong()); - - scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - - InOrder inOrder1 = inOrder(observer); - InOrder inOrder2 = inOrder(observer2); - - inOrder1.verify(observer, times(1)).onNext(0L); - inOrder1.verify(observer, times(1)).onNext(1L); - inOrder1.verify(observer, never()).onNext(2L); - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - inOrder2.verify(observer2, times(1)).onNext(0L); - inOrder2.verify(observer2, times(1)).onNext(1L); - inOrder2.verify(observer2, never()).onNext(2L); - verify(observer2, never()).onCompleted(); - verify(observer2, never()).onError(any(Throwable.class)); - - sub1.unsubscribe(); - sub2.unsubscribe(); - scheduler.advanceTimeTo(4, TimeUnit.SECONDS); - - verify(observer, never()).onNext(2L); - verify(observer, times(1)).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - verify(observer2, never()).onNext(2L); - verify(observer2, times(1)).onCompleted(); - verify(observer2, never()).onError(any(Throwable.class)); - } - - @Test - public void testWithMultipleStaggeredSubscribers() { - Observable w = Observable.create(interval(1, TimeUnit.SECONDS, scheduler)); - Subscription sub1 = w.subscribe(observer); - - verify(observer, never()).onNext(anyLong()); - - scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - Subscription sub2 = w.subscribe(observer2); - - InOrder inOrder1 = inOrder(observer); - inOrder1.verify(observer, times(1)).onNext(0L); - inOrder1.verify(observer, times(1)).onNext(1L); - inOrder1.verify(observer, never()).onNext(2L); - - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - verify(observer2, never()).onNext(anyLong()); - - scheduler.advanceTimeTo(4, TimeUnit.SECONDS); - - inOrder1.verify(observer, times(1)).onNext(2L); - inOrder1.verify(observer, times(1)).onNext(3L); - - InOrder inOrder2 = inOrder(observer2); - inOrder2.verify(observer2, times(1)).onNext(0L); - inOrder2.verify(observer2, times(1)).onNext(1L); - - sub1.unsubscribe(); - sub2.unsubscribe(); - - inOrder1.verify(observer, never()).onNext(anyLong()); - inOrder1.verify(observer, times(1)).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - inOrder2.verify(observer2, never()).onNext(anyLong()); - inOrder2.verify(observer2, times(1)).onCompleted(); - verify(observer2, never()).onError(any(Throwable.class)); - } - - @Test - public void testWithMultipleStaggeredSubscribersAndPublish() { - ConnectableObservable w = Observable.create(interval(1, TimeUnit.SECONDS, scheduler)).publish(); - Subscription sub1 = w.subscribe(observer); - w.connect(); - - verify(observer, never()).onNext(anyLong()); - - scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - Subscription sub2 = w.subscribe(observer2); - - InOrder inOrder1 = inOrder(observer); - inOrder1.verify(observer, times(1)).onNext(0L); - inOrder1.verify(observer, times(1)).onNext(1L); - inOrder1.verify(observer, never()).onNext(2L); - - verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - verify(observer2, never()).onNext(anyLong()); - - scheduler.advanceTimeTo(4, TimeUnit.SECONDS); - - inOrder1.verify(observer, times(1)).onNext(2L); - inOrder1.verify(observer, times(1)).onNext(3L); - - InOrder inOrder2 = inOrder(observer2); - inOrder2.verify(observer2, times(1)).onNext(2L); - inOrder2.verify(observer2, times(1)).onNext(3L); - - sub1.unsubscribe(); - sub2.unsubscribe(); - - inOrder1.verify(observer, never()).onNext(anyLong()); - inOrder1.verify(observer, never()).onCompleted(); - verify(observer, never()).onError(any(Throwable.class)); - - inOrder2.verify(observer2, never()).onNext(anyLong()); - inOrder2.verify(observer2, never()).onCompleted(); - verify(observer2, never()).onError(any(Throwable.class)); - } - } }