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/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 cb3430c1f3..b01872f226 100644 --- a/rxjava-core/src/main/java/rx/Scheduler.java +++ b/rxjava-core/src/main/java/rx/Scheduler.java @@ -15,24 +15,15 @@ */ 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; /** @@ -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..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,18 +15,12 @@ */ package rx.concurrency; -import static org.mockito.Mockito.*; - 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; /** @@ -41,7 +35,7 @@ public static CurrentThreadScheduler getInstance() { private static final ThreadLocal> QUEUE = new ThreadLocal>(); - private CurrentThreadScheduler() { + /* package accessible for unit tests */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/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 f3f4353bb7..c5a4cef9d4 100644 --- a/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java +++ b/rxjava-core/src/main/java/rx/concurrency/ImmediateScheduler.java @@ -15,16 +15,10 @@ */ 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; /** @@ -37,7 +31,7 @@ public static ImmediateScheduler getInstance() { return INSTANCE; } - private ImmediateScheduler() { + /* package accessible for unit tests */ImmediateScheduler() { } @Override @@ -52,59 +46,4 @@ public Subscription schedule(T state, Func2(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(); - } - }; - - 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/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 32e2db7400..36f372685e 100644 --- a/rxjava-core/src/main/java/rx/observables/BlockingObservable.java +++ b/rxjava-core/src/main/java/rx/observables/BlockingObservable.java @@ -15,20 +15,12 @@ */ 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; @@ -37,8 +29,6 @@ import rx.operators.OperationToIterator; import rx.operators.SafeObservableSubscription; import rx.operators.SafeObserver; -import rx.subscriptions.BooleanSubscription; -import rx.subscriptions.Subscriptions; import rx.util.functions.Action1; import rx.util.functions.Func1; @@ -367,235 +357,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/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 e0a18154c0..f3e0f0ebcd 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAll.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAll.java @@ -15,12 +15,8 @@ */ 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; @@ -45,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))); @@ -94,79 +88,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..a06058bece 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAny.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAny.java @@ -15,13 +15,10 @@ */ 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; @@ -44,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); } @@ -64,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); } @@ -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..3fbbd1f05e 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationAverage.java +++ b/rxjava-core/src/main/java/rx/operators/OperationAverage.java @@ -15,30 +15,26 @@ */ 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; /** * 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 @@ -100,99 +96,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..c9aba14411 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationBuffer.java +++ b/rxjava-core/src/main/java/rx/operators/OperationBuffer.java @@ -15,32 +15,17 @@ */ 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; @@ -80,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)); } @@ -116,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)); } @@ -171,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)); } @@ -226,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)); } @@ -287,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)); } @@ -348,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)); } @@ -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..71c681a179 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; /** * 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..dc54c204f6 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationCast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationCast.java @@ -1,15 +1,22 @@ +/** + * 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.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 +32,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..8a581667ae 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java +++ b/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java @@ -15,10 +15,6 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -26,15 +22,10 @@ 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; @@ -59,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) { @@ -77,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)); @@ -89,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)); @@ -102,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)); @@ -116,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)); @@ -131,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)); @@ -147,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)); @@ -164,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)); @@ -179,7 +172,7 @@ public static OnSubscribeFunc combine return a; } - private static class CombineObserver implements Observer { + /* package accessible for unit tests */static class CombineObserver implements Observer { final Observable w; final Aggregator a; private Subscription subscription; @@ -213,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. */ - private 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. @@ -245,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); @@ -254,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(); @@ -298,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); } } @@ -323,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() { @@ -351,603 +346,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..9838a8073b 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; /** * 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..3dbdd81f20 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDebounce.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDebounce.java @@ -15,24 +15,15 @@ */ 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; @@ -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..7bc74ac156 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDefaultIfEmpty.java @@ -1,12 +1,20 @@ +/** + * 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.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; @@ -21,7 +29,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 @@ -82,41 +90,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..d2db484254 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDistinct.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDistinct.java @@ -15,24 +15,12 @@ */ 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; @@ -45,13 +33,14 @@ /** * 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. @@ -59,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 @@ -69,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 @@ -83,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; @@ -107,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(); @@ -127,7 +119,7 @@ public void onNext(T next) { } } }); - + return Subscriptions.create(new Action0() { @Override public void call() { @@ -136,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; @@ -151,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(); @@ -173,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; } @@ -183,7 +175,7 @@ private boolean alreadyEmitted(U newKey) { return false; } }); - + return Subscriptions.create(new Action0() { @Override public void call() { @@ -192,166 +184,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..eb1d6ed083 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDistinctUntilChanged.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDistinctUntilChanged.java @@ -15,20 +15,8 @@ */ 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; @@ -45,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 @@ -52,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 @@ -68,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 @@ -80,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 @@ -102,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; @@ -119,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(); @@ -143,7 +135,7 @@ public void onNext(T next) { } } }); - + return Subscriptions.create(new Action0() { @Override public void call() { @@ -152,169 +144,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..3001043c80 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationElementAt.java +++ b/rxjava-core/src/main/java/rx/operators/OperationElementAt.java @@ -15,20 +15,8 @@ */ 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; @@ -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..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. @@ -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; @@ -38,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), @@ -105,31 +102,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..1a37d68d73 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationFirstOrDefault.java +++ b/rxjava-core/src/main/java/rx/operators/OperationFirstOrDefault.java @@ -15,20 +15,10 @@ */ 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 static rx.util.functions.Functions.*; 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; @@ -45,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 @@ -59,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. @@ -89,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()) { @@ -117,7 +107,7 @@ public void onNext(T next) { } } }); - + return Subscriptions.create(new Action0() { @Override public void call() { @@ -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..a30b97523f 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java +++ b/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java @@ -15,29 +15,16 @@ */ 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; @@ -174,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(); @@ -190,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); } @@ -223,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 @@ -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..7e1be94991 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationInterval.java +++ b/rxjava-core/src/main/java/rx/operators/OperationInterval.java @@ -15,24 +15,13 @@ */ 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; @@ -66,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) { @@ -84,7 +73,7 @@ public void call() { currentValue++; } }, period, period, unit); - + return Subscriptions.create(new Action0() { @Override public void call() { @@ -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..d78b5dc6ef 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; @@ -65,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); + } + }); } /** @@ -79,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. @@ -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..12619e9176 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; /** * 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..dbca17522d 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java @@ -15,21 +15,11 @@ */ 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; @@ -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..15406aeece 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMostRecent.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMostRecent.java @@ -15,18 +15,12 @@ */ package rx.operators; -import static org.junit.Assert.*; - 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; /** @@ -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..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. @@ -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 { @@ -72,7 +67,6 @@ public void onNext(T args) { } } - return new Subscription() { @Override public void unsubscribe() { @@ -86,94 +80,5 @@ 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..4f5e9dfd31 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationNext.java +++ b/rxjava-core/src/main/java/rx/operators/OperationNext.java @@ -15,31 +15,15 @@ */ 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 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; /** @@ -79,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; } @@ -99,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; @@ -107,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); } @@ -124,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; } @@ -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..0122a8c5e1 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaFunction.java +++ b/rxjava-core/src/main/java/rx/operators/OperationOnErrorResumeNextViaFunction.java @@ -15,21 +15,13 @@ */ 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; @@ -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..6c14d3618a 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; /** * Instruct an Observable to pass control to another Observable rather than invoking @@ -75,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); } @@ -100,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(); } })); @@ -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..d5d6a9fbc7 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationOnErrorReturn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationOnErrorReturn.java @@ -15,16 +15,9 @@ */ 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; @@ -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..864ba0730e 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; /** * 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..5a584ce370 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationParallel.java +++ b/rxjava-core/src/main/java/rx/operators/OperationParallel.java @@ -15,17 +15,12 @@ */ 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; @@ -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/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 0f664ee3ec..430ef53ce4 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationRetry.java +++ b/rxjava-core/src/main/java/rx/operators/OperationRetry.java @@ -15,14 +15,9 @@ * 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; import rx.Observer; @@ -31,7 +26,6 @@ import rx.concurrency.Schedulers; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.MultipleAssignmentSubscription; -import rx.subscriptions.Subscriptions; import rx.util.functions.Func2; public class OperationRetry { @@ -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..89da00e5fb 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSample.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSample.java @@ -15,25 +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 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; @@ -58,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; @@ -80,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()) { @@ -92,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() { @@ -122,79 +115,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..a5eedfee46 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; @@ -53,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); @@ -77,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) { @@ -99,7 +93,7 @@ public synchronized void onNext(T value) { accumulatingObserver.onNext(value); } } - + @Override public void onError(Throwable e) { observer.onError(e); @@ -112,7 +106,7 @@ public void onCompleted() { }); } } - + private static class Accumulator implements OnSubscribeFunc { private final Observable sequence; private final R initialValue; @@ -140,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; } @@ -160,103 +154,15 @@ public synchronized void onNext(T value) { observer.onError(ex); } } - + @Override public void onError(Throwable e) { observer.onError(e); } - + @Override 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..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,8 @@ */ 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; @@ -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..f3cb462e55 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSkipLast.java @@ -15,20 +15,10 @@ */ 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; @@ -47,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 @@ -55,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. */ @@ -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..6bc747e9bf 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSkipWhile.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSkipWhile.java @@ -15,16 +15,9 @@ */ 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; @@ -40,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 @@ -55,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; @@ -93,7 +86,7 @@ public void onNext(T next) { observer.onNext(next); } else { } - } catch(Throwable t) { + } catch (Throwable t) { observer.onError(t); } } @@ -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..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. @@ -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..fef81a2625 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSum.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSum.java @@ -15,16 +15,12 @@ */ package rx.operators; -import static org.mockito.Mockito.*; - -import org.junit.Test; - import rx.Observable; -import rx.Observer; import rx.util.functions.Func2; /** * A few operators for implementing the sum operation. + * * @see MSDN: Observable.Sum */ public final class OperationSum { @@ -63,103 +59,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..9094a2affd 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; /** @@ -50,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) { @@ -93,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) { @@ -182,355 +162,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..5ba8c0a98e 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; @@ -64,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 @@ -92,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 { @@ -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..877ba4d5f3 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; /** * 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..d324f7ce42 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTakeLast.java @@ -15,20 +15,10 @@ */ 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; @@ -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..479badf6a6 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTakeWhile.java +++ b/rxjava-core/src/main/java/rx/operators/OperationTakeWhile.java @@ -15,21 +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.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; @@ -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..e21617a82c 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java +++ b/rxjava-core/src/main/java/rx/operators/OperationThrottleFirst.java @@ -15,25 +15,15 @@ */ 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; /** @@ -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..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. @@ -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/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/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 e7dcc308eb..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; /** @@ -36,6 +29,7 @@ *

* *

+ * * @see Issue #50 */ public class OperationToIterator { @@ -107,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/main/java/rx/operators/OperationToObservableFuture.java b/rxjava-core/src/main/java/rx/operators/OperationToObservableFuture.java index 958aa9ad3f..875a0dc0d8 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableFuture.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToObservableFuture.java @@ -15,13 +15,9 @@ */ 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; @@ -39,7 +35,7 @@ * Observable.subscribe(Observer) does nothing. */ public class OperationToObservableFuture { - private 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; @@ -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..b87a49fdd1 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java @@ -15,17 +15,10 @@ */ 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; @@ -63,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); @@ -93,44 +87,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..cfc7ba35fa 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java @@ -15,19 +15,12 @@ */ package rx.operators; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - 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; @@ -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..d7ff20e061 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationWindow.java +++ b/rxjava-core/src/main/java/rx/operators/OperationWindow.java @@ -15,29 +15,16 @@ */ 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; @@ -50,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 @@ -76,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)); } @@ -113,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)); } @@ -168,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)); } @@ -223,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)); } @@ -284,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)); } @@ -345,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)); } @@ -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..1837f6e58b 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationZip.java +++ b/rxjava-core/src/main/java/rx/operators/OperationZip.java @@ -15,23 +15,14 @@ */ 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; @@ -159,7 +150,7 @@ public static OnSubscribeFunc zip(Iterable> ws, F /* * ThreadSafe */ - private 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(); @@ -204,7 +195,7 @@ public void onNext(T args) { * * @param */ - private static class Aggregator implements OnSubscribeFunc { + /* package accessible for unit tests */static class Aggregator implements OnSubscribeFunc { private volatile SynchronizedObserver observer; private final FuncN zipFunction; @@ -228,7 +219,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 +352,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..ef33ebd3d3 100644 --- a/rxjava-core/src/main/java/rx/operators/SafeObservableSubscription.java +++ b/rxjava-core/src/main/java/rx/operators/SafeObservableSubscription.java @@ -15,12 +15,8 @@ */ package rx.operators; -import static org.mockito.Mockito.*; - import java.util.concurrent.atomic.AtomicReference; -import org.junit.Test; - import rx.Subscription; /** @@ -54,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) */ @@ -82,15 +79,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/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 9a6b14d09f..2daef3e280 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. @@ -82,7 +62,7 @@ public SynchronizedObserver(Observer Observer, SafeObservableSubscrip this.subscription = subscription; this.lock = lock; } - + /** * Used when synchronizing an Observer without access to the subscription. * @@ -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/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 e27c343815..2eb806dd6b 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() { + /* package accessible for unit tests */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..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,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; /** * Subject that publishes only the last event to each {@link Observer} that has subscribed when the @@ -40,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");
    @@ -56,15 +48,14 @@
       subject.onCompleted();
     
       } 
    - *
    + * 
      * @param 
      */
     public class AsyncSubject extends Subject {
     
    -
         /**
          * Create a new AsyncSubject
    -     *
    +     * 
          * @return a new AsyncSubject
          */
         public static  AsyncSubject create() {
    @@ -123,135 +114,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..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,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;
     
     /**
      * Subject that publishes the most recent and all subsequent events to each subscribed {@link Observer}.
    @@ -38,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");
    @@ -54,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) {
    @@ -127,137 +118,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..fd32bd6e42 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 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..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,23 +15,15 @@
      */
     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;
     
     /**
    @@ -43,7 +35,7 @@
      * 

    *

     {@code
     
    -  ReplaySubject subject = ReplaySubject.create();
    + * eplaySubject subject = ReplaySubject.create();
       subject.onNext("one");
       subject.onNext("two");
       subject.onNext("three");
    @@ -54,7 +46,7 @@
       subject.subscribe(observer2);
     
       } 
    - *
    + * 
      * @param 
      */
     public final class ReplaySubject extends Subject
    @@ -178,172 +170,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/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 6ca5c8a699..326de38471 100644
    --- a/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java
    +++ b/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java
    @@ -15,16 +15,11 @@
      */
     package rx.subscriptions;
     
    -import static org.junit.Assert.*;
    -
     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;
    @@ -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/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/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 5febe4f46a..61fd6b3f6c 100644
    --- a/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java
    +++ b/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java
    @@ -15,14 +15,8 @@
      */
     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;
    @@ -108,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.
          * 
    @@ -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/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/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/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/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 29a51d8dfe..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.*;
    @@ -60,7 +75,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 +95,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 +127,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/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 ef2af97549..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;
    @@ -10,33 +25,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..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.*;
    @@ -43,14 +58,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 +90,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/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 ff99e60e1c..b83e94fcae 100644
    --- a/rxjava-core/src/test/java/rx/RefCountTests.java
    +++ b/rxjava-core/src/test/java/rx/RefCountTests.java
    @@ -1,5 +1,28 @@
    +/**
    + * 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 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 +32,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 +66,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/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/concurrency/TestSchedulers.java b/rxjava-core/src/test/java/rx/SchedulersTest.java
    similarity index 91%
    rename from rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java
    rename to rxjava-core/src/test/java/rx/SchedulersTest.java
    index e1df828237..c9d97054d0 100644
    --- a/rxjava-core/src/test/java/rx/concurrency/TestSchedulers.java
    +++ b/rxjava-core/src/test/java/rx/SchedulersTest.java
    @@ -13,9 +13,11 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package rx.concurrency;
    +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;
    @@ -25,12 +27,12 @@
     import java.util.concurrent.atomic.AtomicReference;
     
     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.BooleanSubscription;
     import rx.subscriptions.Subscriptions;
     import rx.util.functions.Action0;
    @@ -38,7 +40,47 @@
     import rx.util.functions.Func1;
     import rx.util.functions.Func2;
     
    -public class TestSchedulers {
    +public class SchedulersTest {
    +
    +    @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());
    +    }
     
         @Test
         public void testComputationThreadPool1() {
    @@ -474,7 +516,7 @@ public Subscription onSubscribe(final Observer observer) {
                 fail("Error: " + observer.error.get().getMessage());
             }
         }
    -    
    +
         @Test
         public void testRecursion() {
             TestScheduler s = new TestScheduler();
    @@ -494,7 +536,6 @@ public void call(Action0 self) {
             subscription.unsubscribe();
             assertEquals(0, counter.get());
         }
    -    
     
         /**
          * Used to determine if onNext is being invoked concurrently.
    diff --git a/rxjava-core/src/test/java/rx/StartWithTests.java b/rxjava-core/src/test/java/rx/StartWithTests.java
    index de7b03e6ef..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.*;
    @@ -16,7 +31,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/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/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/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
    new file mode 100644
    index 0000000000..3613b7c592
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/concurrency/CurrentThreadSchedulerTest.java
    @@ -0,0 +1,143 @@
    +/**
    + * 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 java.util.concurrent.TimeUnit;
    +
    +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() {
    +                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
    new file mode 100644
    index 0000000000..593f6b6a52
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/concurrency/ImmediateSchedulerTest.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.concurrency;
    +
    +import static org.mockito.Mockito.*;
    +
    +import org.junit.Test;
    +import org.mockito.InOrder;
    +
    +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();
    +    }
    +}
    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..b546fd64bb
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/observables/BlockingObservableTest.java
    @@ -0,0 +1,264 @@
    +/**
    + * 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.*;
    +
    +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;
    +import rx.subscriptions.BooleanSubscription;
    +import rx.subscriptions.Subscriptions;
    +import rx.util.functions.Action1;
    +import rx.util.functions.Func1;
    +
    +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
    new file mode 100644
    index 0000000000..0b05ee1d4e
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationAllTest.java
    @@ -0,0 +1,100 @@
    +/**
    + * 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.OperationAll.*;
    +
    +import org.junit.Test;
    +
    +import rx.Observable;
    +import rx.Observer;
    +import rx.util.functions.Func1;
    +
    +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
    new file mode 100644
    index 0000000000..f98149376a
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationAnyTest.java
    @@ -0,0 +1,197 @@
    +/**
    + * 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.OperationAny.*;
    +
    +import org.junit.Test;
    +
    +import rx.Observable;
    +import rx.Observer;
    +import rx.util.functions.Func1;
    +
    +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
    new file mode 100644
    index 0000000000..a8ffa4706b
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationAverageTest.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.operators;
    +
    +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();
    +    }
    +}
    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..9318af8fd6
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationBufferTest.java
    @@ -0,0 +1,366 @@
    +/**
    + * 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 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;
    +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;
    +
    +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
    new file mode 100644
    index 0000000000..79a811b6de
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationCacheTest.java
    @@ -0,0 +1,87 @@
    +/**
    + * 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 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;
    +
    +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
    new file mode 100644
    index 0000000000..782fe6bc90
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationCastTest.java
    @@ -0,0 +1,56 @@
    +/**
    + * 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.OperationCast.*;
    +
    +import org.junit.Test;
    +
    +import rx.Observable;
    +import rx.Observer;
    +
    +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
    new file mode 100644
    index 0000000000..c576337e1e
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationCombineLatestTest.java
    @@ -0,0 +1,636 @@
    +/**
    + * 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 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;
    +
    +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
    new file mode 100644
    index 0000000000..f42d8b81ad
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationConcatTest.java
    @@ -0,0 +1,559 @@
    +/**
    + * 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.OperationConcat.*;
    +
    +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 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);
    +
    +        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
    new file mode 100644
    index 0000000000..e831c7b829
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDebounceTest.java
    @@ -0,0 +1,161 @@
    +/**
    + * 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 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.subscriptions.Subscriptions;
    +import rx.util.functions.Action0;
    +
    +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
    new file mode 100644
    index 0000000000..68003ceb01
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDefaultIfEmptyTest.java
    @@ -0,0 +1,60 @@
    +/**
    + * 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.OperationDefaultIfEmpty.*;
    +
    +import org.junit.Test;
    +
    +import rx.Observable;
    +import rx.Observer;
    +
    +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
    new file mode 100644
    index 0000000000..550f428134
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDeferTest.java
    @@ -0,0 +1,63 @@
    +/**
    + * 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 org.junit.Test;
    +
    +import rx.Observable;
    +import rx.Observer;
    +import rx.util.functions.Func0;
    +
    +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
    new file mode 100644
    index 0000000000..1465b4f3d2
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDematerializeTest.java
    @@ -0,0 +1,74 @@
    +/**
    + * 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 rx.operators.OperationDematerialize.*;
    +
    +import org.junit.Test;
    +
    +import rx.Notification;
    +import rx.Observable;
    +import rx.Observer;
    +
    +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
    new file mode 100644
    index 0000000000..de57e8fc1e
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctTest.java
    @@ -0,0 +1,195 @@
    +/**
    + * 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.*;
    +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;
    +
    +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 = 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
    new file mode 100644
    index 0000000000..b4568baaf7
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationDistinctUntilChangedTest.java
    @@ -0,0 +1,198 @@
    +/**
    + * 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.*;
    +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;
    +
    +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 = 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
    new file mode 100644
    index 0000000000..a28de23d08
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationElementAtTest.java
    @@ -0,0 +1,127 @@
    +/**
    + * 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.OperationElementAt.*;
    +
    +import java.util.Iterator;
    +import java.util.concurrent.ExecutionException;
    +
    +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 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
    new file mode 100644
    index 0000000000..c22c3d46b8
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationFilterTest.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.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;
    +
    +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
    new file mode 100644
    index 0000000000..1ce2111fe9
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationFinallyTest.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.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;
    +
    +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
    new file mode 100644
    index 0000000000..026caa1319
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationFirstOrDefaultTest.java
    @@ -0,0 +1,91 @@
    +/**
    + * 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.*;
    +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;
    +
    +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 = 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();
    +    }
    +
    +    @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();
    +    }
    +
    +    @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();
    +    }
    +
    +    @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();
    +    }
    +}
    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..cf47d9e683
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java
    @@ -0,0 +1,347 @@
    +/**
    + * 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 rx.operators.OperationGroupBy.*;
    +
    +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 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();
    +        }
    +    };
    +
    +    @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
    new file mode 100644
    index 0000000000..5f64452e3b
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationIntervalTest.java
    @@ -0,0 +1,192 @@
    +/**
    + * 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 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;
    +
    +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
    new file mode 100644
    index 0000000000..6d639d5235
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMapTest.java
    @@ -0,0 +1,292 @@
    +/**
    + * 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 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
    new file mode 100644
    index 0000000000..54d1ab0474
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMaterializeTest.java
    @@ -0,0 +1,166 @@
    +/**
    + * 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 rx.operators.OperationMaterialize.*;
    +
    +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.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");
    +
    +        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
    new file mode 100644
    index 0000000000..693b9d4801
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMergeDelayErrorTest.java
    @@ -0,0 +1,515 @@
    +/**
    + * 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.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;
    +
    +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
    new file mode 100644
    index 0000000000..311a838691
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java
    @@ -0,0 +1,474 @@
    +/**
    + * 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.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;
    +import rx.subscriptions.Subscriptions;
    +import rx.util.functions.Action0;
    +import rx.util.functions.Action1;
    +
    +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
    new file mode 100644
    index 0000000000..d20f79e819
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMostRecentTest.java
    @@ -0,0 +1,74 @@
    +/**
    + * 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 rx.operators.OperationMostRecent.*;
    +
    +import java.util.Iterator;
    +
    +import org.junit.Test;
    +
    +import rx.subjects.PublishSubject;
    +import rx.subjects.Subject;
    +
    +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
    new file mode 100644
    index 0000000000..8f824f1484
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationMulticastTest.java
    @@ -0,0 +1,113 @@
    +/**
    + * 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 org.junit.Test;
    +
    +import rx.Observer;
    +import rx.Subscription;
    +import rx.observables.ConnectableObservable;
    +import rx.subjects.PublishSubject;
    +import rx.subjects.Subject;
    +
    +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
    new file mode 100644
    index 0000000000..140cc0560c
    --- /dev/null
    +++ b/rxjava-core/src/test/java/rx/operators/OperationNextTest.java
    @@ -0,0 +1,296 @@
    +/**
    + * 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 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;
    +import rx.concurrency.Schedulers;
    +import rx.subjects.PublishSubject;
    +import rx.subjects.Subject;
    +import rx.subscriptions.Subscriptions;
    +
    +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 new file mode 100644 index 0000000000..e114f637ff --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationObserveOnTest.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.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; + +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 new file mode 100644 index 0000000000..ab9293251e --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaFunctionTest.java @@ -0,0 +1,183 @@ +/** + * 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.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; + +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 new file mode 100644 index 0000000000..dcbbdc3fb4 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorResumeNextViaObservableTest.java @@ -0,0 +1,143 @@ +/** + * 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.OperationOnErrorResumeNextViaObservable.*; + +import org.junit.Test; +import org.mockito.Mockito; + +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Func1; + +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 new file mode 100644 index 0000000000..e436dd29ee --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationOnErrorReturnTest.java @@ -0,0 +1,145 @@ +/** + * 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.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; + +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 new file mode 100644 index 0000000000..8b5cc9271a --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationOnExceptionResumeNextViaObservableTest.java @@ -0,0 +1,240 @@ +/** + * 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.OperationOnExceptionResumeNextViaObservable.*; + +import org.junit.Test; +import org.mockito.Mockito; + +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.util.functions.Func1; + +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 new file mode 100644 index 0000000000..d52c8bc0b9 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java @@ -0,0 +1,61 @@ +/** + * 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.util.functions.Action1; +import rx.util.functions.Func1; + +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 new file mode 100644 index 0000000000..1b6006b923 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationRetryTest.java @@ -0,0 +1,130 @@ +/** + * 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 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; + +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 new file mode 100644 index 0000000000..d868697f9f --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSampleTest.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.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.subscriptions.Subscriptions; +import rx.util.functions.Action0; + +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 new file mode 100644 index 0000000000..0dedef787c --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationScanTest.java @@ -0,0 +1,115 @@ +/** + * 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 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; + +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 new file mode 100644 index 0000000000..9391424e07 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipLastTest.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.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; + +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 new file mode 100644 index 0000000000..16bc76820f --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipTest.java @@ -0,0 +1,58 @@ +/** + * 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 rx.operators.OperationSkip.*; + +import org.junit.Test; + +import rx.Observable; +import rx.Observer; + +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 new file mode 100644 index 0000000000..153c9fb14e --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSkipWhileTest.java @@ -0,0 +1,118 @@ +/** + * 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 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; + +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); + 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 new file mode 100644 index 0000000000..635d4d8755 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSubscribeOnTest.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.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; +import rx.Subscription; +import rx.concurrency.Schedulers; +import rx.test.OperatorTester; +import rx.util.functions.Action0; +import rx.util.functions.Func2; + +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 new file mode 100644 index 0000000000..e124ad13d5 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSumTest.java @@ -0,0 +1,125 @@ +/** + * 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 rx.operators.OperationSum.*; + +import org.junit.Test; + +import rx.Observable; +import rx.Observer; + +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 new file mode 100644 index 0000000000..fa38f02ff8 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSwitchTest.java @@ -0,0 +1,383 @@ +/** + * 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 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.subscriptions.Subscriptions; +import rx.util.functions.Action0; + +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 new file mode 100644 index 0000000000..73db076000 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationSynchronizeTest.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.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; + +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 new file mode 100644 index 0000000000..5ec876368b --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeLastTest.java @@ -0,0 +1,113 @@ +/** + * 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 rx.operators.OperationTakeLast.*; + +import org.junit.Test; +import org.mockito.InOrder; + +import rx.Observable; +import rx.Observer; + +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 new file mode 100644 index 0000000000..773154eda0 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java @@ -0,0 +1,227 @@ +/** + * 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.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; + +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 new file mode 100644 index 0000000000..c5182a71ba --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeUntilTest.java @@ -0,0 +1,180 @@ +/** + * 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.OperationTakeUntil.*; + +import org.junit.Test; + +import rx.Observable; +import rx.Observer; +import rx.Subscription; + +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..08c6d8e83f --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationTakeWhileTest.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.*; +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; +import rx.subjects.PublishSubject; +import rx.subjects.Subject; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +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 new file mode 100644 index 0000000000..ad58e13604 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationThrottleFirstTest.java @@ -0,0 +1,130 @@ +/** + * 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 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.subscriptions.Subscriptions; +import rx.util.functions.Action0; + +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..056b97bf11 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationTimeIntervalTest.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.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; + +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/OperationToFutureTest.java b/rxjava-core/src/test/java/rx/operators/OperationToFutureTest.java new file mode 100644 index 0000000000..ffaf775f8b --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToFutureTest.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.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..b40dd9b62b --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToIteratorTest.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.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; + } + +} 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..dcc6efcc6a --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableFutureTest.java @@ -0,0 +1,63 @@ +/** + * 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 java.util.concurrent.Future; + +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); + } +} 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..8d8be93dcb --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.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.operators; + +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; + +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 new file mode 100644 index 0000000000..1124ea6211 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java @@ -0,0 +1,69 @@ +/** + * 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 rx.operators.OperationToObservableList.*; + +import java.util.Arrays; +import java.util.List; + +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(); + } +} 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..8c572a31b6 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.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 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; + +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 new file mode 100644 index 0000000000..b26cf42cae --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationWindowTest.java @@ -0,0 +1,330 @@ +/** + * 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 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; +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; + +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 new file mode 100644 index 0000000000..43cca02327 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperationZipTest.java @@ -0,0 +1,598 @@ +/** + * 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 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; + +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/OperatorTesterTest.java b/rxjava-core/src/test/java/rx/operators/OperatorTesterTest.java new file mode 100644 index 0000000000..645cea8e3b --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperatorTesterTest.java @@ -0,0 +1,22 @@ +/** + * 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; + +@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..bb317884bc --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/SafeObservableSubscriptionTest.java @@ -0,0 +1,34 @@ +/** + * 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 org.junit.Test; + +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(); + } +} 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..f1c40c3674 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/SynchronizedObserverTest.java @@ -0,0 +1,772 @@ +/** + * 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 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; + +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/operators/TakeWhileTest.java b/rxjava-core/src/test/java/rx/operators/TakeWhileTest.java new file mode 100644 index 0000000000..25e17ba13b --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/TakeWhileTest.java @@ -0,0 +1,22 @@ +/** + * 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; + +@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..29337619ff --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/TimeIntervalObserverTest.java @@ -0,0 +1,22 @@ +/** + * 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; + +@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..ce96b43edc --- /dev/null +++ b/rxjava-core/src/test/java/rx/plugins/RxJavaPluginsTest.java @@ -0,0 +1,92 @@ +/** + * 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.*; + +import org.junit.Test; + +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 new file mode 100644 index 0000000000..a0b82c7167 --- /dev/null +++ b/rxjava-core/src/test/java/rx/subjects/AsyncSubjectTest.java @@ -0,0 +1,149 @@ +/** + * 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.*; +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; + +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 new file mode 100644 index 0000000000..ac543b7a18 --- /dev/null +++ b/rxjava-core/src/test/java/rx/subjects/BehaviorSubjectTest.java @@ -0,0 +1,150 @@ +/** + * 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.*; +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; + +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 new file mode 100644 index 0000000000..9e93c89e01 --- /dev/null +++ b/rxjava-core/src/test/java/rx/subjects/PublishSubjectTest.java @@ -0,0 +1,381 @@ +/** + * 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.*; +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; +import rx.Subscription; +import rx.util.functions.Action1; +import rx.util.functions.Func0; +import rx.util.functions.Func1; + +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 new file mode 100644 index 0000000000..fef45ca63d --- /dev/null +++ b/rxjava-core/src/test/java/rx/subjects/ReplaySubjectTest.java @@ -0,0 +1,185 @@ +/** + * 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.*; +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; + +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 new file mode 100644 index 0000000000..ce23bf1d68 --- /dev/null +++ b/rxjava-core/src/test/java/rx/subscriptions/CompositeSubscriptionTest.java @@ -0,0 +1,85 @@ +/** + * 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.*; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import rx.Subscription; +import rx.util.CompositeException; + +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/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 new file mode 100644 index 0000000000..abeab16833 --- /dev/null +++ b/rxjava-core/src/test/java/rx/subscriptions/SubscriptionsTest.java @@ -0,0 +1,36 @@ +/** + * 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.*; +import static rx.subscriptions.Subscriptions.*; + +import org.junit.Test; + +import rx.Subscription; +import rx.util.functions.Action0; + +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..6bd905c264 --- /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 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. + */ +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 new file mode 100644 index 0000000000..297e7a769a --- /dev/null +++ b/rxjava-core/src/test/java/rx/util/RangeTest.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.util; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +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); + } + return result; + } +}