From 38eda0c02c2fcca33caa77399d818e846a319ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Juli=C3=A1n=20Garc=C3=ADa=20Granado?= Date: Sun, 9 Feb 2020 17:31:19 +0100 Subject: [PATCH 001/521] 3.x: Add 'Error handling' section to Observable.blockingFirst documentation (#6900) (#6903) --- src/main/java/io/reactivex/rxjava3/core/Observable.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index 033d12bdff..c37da859fd 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -5397,6 +5397,10 @@ public final Single any(@NonNull Predicate predicate) { *
*
Scheduler:
*
{@code blockingFirst} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the source signals an error, the operator wraps a checked {@link Exception} + * into {@link RuntimeException} and throws that. Otherwise, {@code RuntimeException}s and + * {@link Error}s are rethrown as they are.
*
* * @return the first item emitted by the current {@code Observable} @@ -5425,6 +5429,10 @@ public final T blockingFirst() { *
*
Scheduler:
*
{@code blockingFirst} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the source signals an error, the operator wraps a checked {@link Exception} + * into {@link RuntimeException} and throws that. Otherwise, {@code RuntimeException}s and + * {@link Error}s are rethrown as they are.
*
* * @param defaultItem From b3ad0752de398f4536bb88a7b1c77b0b9fbecb87 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 12 Feb 2020 16:26:52 +0100 Subject: [PATCH 002/521] 3.x: Add missing coverage, fix unused/inconsistent ops (#6901) * 3.x: Add missing coverage, fix unused/inconsistent ops * More coverage improvements and cleanup * Some more coverage * Observable coverage and cleanup * Improve Flowable internals and coverage * More Flowable operator coverage and fixes * Last set of coverage & cleanup for Flowable operators * Fix wrong use of j.u.Observable --- .../reactivex/rxjava3/core/Completable.java | 2 +- .../rxjava3/internal/functions/Functions.java | 16 +- .../internal/observers/FutureObserver.java | 36 +- .../observers/InnerQueuedObserver.java | 4 - .../observers/QueueDrainObserver.java | 4 - .../completable/CompletableConcat.java | 5 +- .../completable/CompletableMergeArray.java | 5 +- ...a => CompletableMergeArrayDelayError.java} | 4 +- .../CompletableMergeDelayErrorIterable.java | 2 +- .../completable/CompletableMergeIterable.java | 5 +- .../flowable/BlockingFlowableNext.java | 7 +- .../operators/flowable/FlowableBuffer.java | 6 +- .../flowable/FlowableCombineLatest.java | 88 ++--- .../operators/flowable/FlowableConcatMap.java | 34 +- .../flowable/FlowableConcatMapEager.java | 2 +- .../flowable/FlowableConcatMapScheduler.java | 12 +- .../operators/flowable/FlowableFlatMap.java | 98 ++---- .../flowable/FlowableFlatMapCompletable.java | 4 +- ...FlowableFlatMapCompletableCompletable.java | 4 +- .../flowable/FlowableFlatMapMaybe.java | 25 +- .../flowable/FlowableFlatMapSingle.java | 17 +- .../flowable/FlowableFromIterable.java | 29 +- .../operators/flowable/FlowableGroupJoin.java | 2 +- .../operators/flowable/FlowableJoin.java | 2 +- .../operators/flowable/FlowableObserveOn.java | 41 +-- .../operators/flowable/FlowablePublish.java | 8 - .../operators/flowable/FlowableReplay.java | 121 ++----- .../operators/flowable/FlowableSwitchMap.java | 51 ++- .../operators/flowable/FlowableTakeLast.java | 8 +- .../operators/flowable/FlowableWindow.java | 16 +- .../operators/flowable/FlowableZip.java | 58 +-- .../operators/observable/ObservableAmb.java | 3 +- .../observable/ObservableBufferTimed.java | 6 +- .../observable/ObservableCombineLatest.java | 24 +- .../observable/ObservableCreate.java | 6 +- .../observable/ObservableFlatMap.java | 61 +--- .../ObservableFlatMapCompletable.java | 4 +- ...servableFlatMapCompletableCompletable.java | 4 +- .../observable/ObservableFlatMapMaybe.java | 17 +- .../observable/ObservableFlatMapSingle.java | 17 +- .../observable/ObservableGroupJoin.java | 2 +- .../observable/ObservableIntervalRange.java | 4 +- .../observable/ObservableReplay.java | 29 +- .../observable/ObservableSwitchMap.java | 28 +- .../observable/ObservableTakeLast.java | 4 +- .../ObservableThrottleFirstTimed.java | 21 +- .../observable/ObservableToList.java | 7 - .../parallel/ParallelFromPublisher.java | 4 - .../operators/parallel/ParallelJoin.java | 37 +- .../operators/parallel/ParallelRunOn.java | 17 +- .../parallel/ParallelSortedJoin.java | 59 ++-- .../single/SingleDelayWithObservable.java | 2 +- .../operators/single/SingleEquals.java | 19 +- .../single/SingleFlatMapIterableFlowable.java | 4 +- .../single/SingleInternalHelper.java | 10 +- .../schedulers/ComputationScheduler.java | 12 +- .../schedulers/ExecutorScheduler.java | 4 +- .../internal/schedulers/IoScheduler.java | 21 +- .../internal/schedulers/SchedulerWhen.java | 16 +- .../internal/schedulers/SingleScheduler.java | 7 +- .../subscribers/FutureSubscriber.java | 29 +- .../subscribers/InnerQueuedSubscriber.java | 12 - .../rxjava3/internal/util/HalfSerializer.java | 9 +- .../rxjava3/processors/BehaviorProcessor.java | 11 +- .../processors/MulticastProcessor.java | 47 +-- .../rxjava3/processors/ReplayProcessor.java | 4 - .../rxjava3/subjects/BehaviorSubject.java | 26 +- .../rxjava3/subjects/ReplaySubject.java | 31 +- .../rxjava3/subscribers/TestSubscriber.java | 7 +- .../rxjava3/core/NotificationTest.java | 11 + .../internal/functions/FunctionsTest.java | 6 +- .../CancellableQueueFuseableTest.java | 10 + .../DeferredScalarDisposableTest.java | 35 ++ .../observers/FutureMultiObserverTest.java | 46 +++ .../observers/FutureObserverTest.java | 46 ++- .../observers/InnerQueuedObserverTest.java | 27 ++ ...=> CompletableAndThenCompletableTest.java} | 7 +- .../completable/CompletableConcatTest.java | 5 + .../completable/CompletableDisposeOnTest.java | 7 +- .../CompletableFromActionTest.java | 29 ++ .../CompletableFromRunnableTest.java | 29 ++ .../CompletableMergeIterableTest.java | 8 + .../completable/CompletableMergeTest.java | 32 ++ .../operators/flowable/FlowableAmbTest.java | 31 ++ .../flowable/FlowableBufferTest.java | 110 +++++- .../operators/flowable/FlowableCacheTest.java | 14 + .../flowable/FlowableCombineLatestTest.java | 233 +++++++++++- .../flowable/FlowableConcatMapEagerTest.java | 36 ++ .../FlowableConcatMapSchedulerTest.java | 142 +++++++- .../flowable/FlowableConcatMapTest.java | 20 ++ .../flowable/FlowableConcatTest.java | 27 +- .../flowable/FlowableCreateTest.java | 66 +++- .../FlowableDelaySubscriptionOtherTest.java | 15 + .../flowable/FlowableDematerializeTest.java | 18 +- .../flowable/FlowableDoFinallyTest.java | 37 +- .../FlowableFlatMapCompletableTest.java | 78 +++- .../flowable/FlowableFlatMapMaybeTest.java | 56 ++- .../flowable/FlowableFlatMapSingleTest.java | 38 +- .../flowable/FlowableFlatMapTest.java | 332 +++++++++++++++++- .../flowable/FlowableForEachTest.java | 8 + .../flowable/FlowableFromCompletableTest.java | 9 +- .../flowable/FlowableFromIterableTest.java | 258 +++++++++++++- .../flowable/FlowableGenerateTest.java | 13 + .../flowable/FlowableGroupByTest.java | 244 ++++++++----- .../flowable/FlowableGroupJoinTest.java | 53 ++- .../flowable/FlowableIntervalRangeTest.java | 8 + .../operators/flowable/FlowableJoinTest.java | 31 ++ .../flowable/FlowableMergeWithMaybeTest.java | 20 +- .../flowable/FlowableMergeWithSingleTest.java | 18 + .../flowable/FlowableObserveOnTest.java | 126 +++++++ ...wableOnBackpressureBufferStrategyTest.java | 19 + .../FlowableOnBackpressureBufferTest.java | 10 + .../flowable/FlowablePublishTest.java | 79 +++++ .../flowable/FlowableRangeLongTest.java | 40 +++ .../operators/flowable/FlowableRangeTest.java | 24 ++ .../flowable/FlowableReduceTest.java | 5 + .../flowable/FlowableRefCountTest.java | 20 +- .../FlowableReplayEagerTruncateTest.java | 9 +- .../flowable/FlowableReplayTest.java | 205 ++++++++++- .../flowable/FlowableSampleTest.java | 6 + .../flowable/FlowableScalarXMapTest.java | 8 + .../operators/flowable/FlowableScanTest.java | 23 ++ .../flowable/FlowableSequenceEqualTest.java | 22 ++ .../flowable/FlowableSingleTest.java | 5 + .../flowable/FlowableSkipLastTimedTest.java | 23 ++ .../flowable/FlowableSubscribeOnTest.java | 6 + .../flowable/FlowableSwitchTest.java | 110 +++++- .../flowable/FlowableTakeLastTest.java | 53 ++- .../flowable/FlowableThrottleFirstTest.java | 10 + .../flowable/FlowableTimeoutTests.java | 7 +- .../FlowableTimeoutWithSelectorTest.java | 10 + .../operators/flowable/FlowableTimerTest.java | 5 + .../flowable/FlowableUnsubscribeOnTest.java | 6 + .../flowable/FlowableWindowWithSizeTest.java | 116 ++++++ ...lowableWindowWithStartEndFlowableTest.java | 186 +++++++++- .../flowable/FlowableWindowWithTimeTest.java | 22 ++ .../operators/flowable/FlowableZipTest.java | 26 ++ .../operators/maybe/MaybeConcatArrayTest.java | 78 +++- .../maybe/MaybeConcatIterableTest.java | 9 + .../maybe/MaybeDelaySubscriptionTest.java | 5 + .../MaybeFlatMapIterableFlowableTest.java | 45 ++- .../maybe/MaybeFromCallableTest.java | 14 + .../maybe/MaybeFromSupplierTest.java | 19 + .../operators/maybe/MaybeMergeArrayTest.java | 25 +- .../maybe/MaybeTimeoutPublisherTest.java | 27 ++ .../operators/maybe/MaybeTimeoutTest.java | 20 ++ .../operators/maybe/MaybeZipArrayTest.java | 55 ++- .../BlockingObservableToIteratorTest.java | 11 +- .../observable/ObservableAllTest.java | 10 + .../observable/ObservableBufferTest.java | 29 +- .../observable/ObservableCacheTest.java | 16 +- .../ObservableCombineLatestTest.java | 74 +++- .../ObservableConcatMapEagerTest.java | 9 + .../ObservableConcatMapSchedulerTest.java | 70 +++- .../observable/ObservableConcatMapTest.java | 107 +++++- .../observable/ObservableCreateTest.java | 97 ++++- .../observable/ObservableDebounceTest.java | 5 + .../ObservableDematerializeTest.java | 9 +- .../observable/ObservableDoFinallyTest.java | 12 + .../ObservableFlatMapCompletableTest.java | 57 ++- .../ObservableFlatMapMaybeTest.java | 64 +++- .../ObservableFlatMapSingleTest.java | 69 +++- .../observable/ObservableFlatMapTest.java | 127 +++++++ .../ObservableFlattenIterableTest.java | 5 + .../ObservableFromCompletableTest.java | 14 +- .../ObservableFromIterableTest.java | 24 ++ .../observable/ObservableGenerateTest.java | 13 + .../observable/ObservableGroupByTest.java | 68 +++- .../observable/ObservableGroupJoinTest.java | 38 ++ .../ObservableIntervalRangeTest.java | 8 + .../observable/ObservableJoinTest.java | 23 ++ .../observable/ObservableRefCountTest.java | 20 +- .../observable/ObservableReplayTest.java | 28 +- .../observable/ObservableSampleTest.java | 4 + .../observable/ObservableScalarXMapTest.java | 10 + .../ObservableSequenceEqualTest.java | 73 ++++ .../ObservableSkipLastTimedTest.java | 201 ++++++++++- .../observable/ObservableSwitchTest.java | 150 +++++++- .../ObservableTakeLastTimedTest.java | 5 + .../ObservableThrottleFirstTest.java | 29 +- .../ObservableUnsubscribeOnTest.java | 6 + ...vableWindowWithStartEndObservableTest.java | 175 ++++++++- .../ObservableWindowWithTimeTest.java | 22 ++ .../operators/single/SingleDelayTest.java | 16 +- .../operators/single/SingleEqualsTest.java | 28 ++ .../SingleFlatMapIterableFlowableTest.java | 50 ++- .../single/SingleInternalHelperTest.java | 4 +- .../operators/single/SingleTimeoutTest.java | 38 +- .../operators/single/SingleZipArrayTest.java | 20 ++ .../schedulers/IoSchedulerInternalTest.java | 95 +++++ .../schedulers/SingleSchedulerTest.java | 22 +- .../TrampolineSchedulerInternalTest.java | 30 +- ...asicFuseableConditionalSubscriberTest.java | 180 +++++++++- .../BasicFuseableSubscriberTest.java | 36 +- .../subscribers/BoundedSubscriberTest.java | 33 ++ .../subscribers/FutureSubscriberTest.java | 16 + .../rxjava3/internal/util/MiscUtilTest.java | 4 + .../observers/SerializedObserverTest.java | 26 +- .../rxjava3/observers/TestObserverTest.java | 30 ++ .../rxjava3/parallel/ParallelCollectTest.java | 7 + .../parallel/ParallelDoOnNextTryTest.java | 19 + .../rxjava3/parallel/ParallelFilterTest.java | 41 +++ .../parallel/ParallelFilterTryTest.java | 41 +++ .../parallel/ParallelFlatMapIterableTest.java | 3 +- .../parallel/ParallelFromPublisherTest.java | 106 +++++- .../rxjava3/parallel/ParallelJoinTest.java | 197 +++++++++++ .../rxjava3/parallel/ParallelMapTest.java | 27 +- .../rxjava3/parallel/ParallelPeekTest.java | 9 + .../parallel/ParallelReduceFullTest.java | 9 +- .../rxjava3/parallel/ParallelReduceTest.java | 7 + .../rxjava3/parallel/ParallelRunOnTest.java | 56 +++ .../parallel/ParallelSortedJoinTest.java | 29 ++ .../processors/AsyncProcessorTest.java | 5 + .../processors/BehaviorProcessorTest.java | 30 ++ .../processors/ReplayProcessorTest.java | 47 +++ .../processors/SerializedProcessorTest.java | 24 ++ .../schedulers/ExecutorSchedulerTest.java | 42 ++- .../rxjava3/subjects/AsyncSubjectTest.java | 7 +- .../rxjava3/subjects/BehaviorSubjectTest.java | 15 + .../subjects/SerializedSubjectTest.java | 50 ++- .../rxjava3/subjects/UnicastSubjectTest.java | 16 + .../subscribers/SerializedSubscriberTest.java | 24 ++ .../subscribers/TestSubscriberTest.java | 36 ++ .../rxjava3/testsupport/TestHelper.java | 260 +++++++++++++- .../validators/OperatorsUseInterfaces.java | 2 +- 225 files changed, 7358 insertions(+), 1093 deletions(-) rename src/main/java/io/reactivex/rxjava3/internal/operators/completable/{CompletableMergeDelayErrorArray.java => CompletableMergeArrayDelayError.java} (96%) create mode 100644 src/test/java/io/reactivex/rxjava3/internal/observers/DeferredScalarDisposableTest.java create mode 100644 src/test/java/io/reactivex/rxjava3/internal/observers/FutureMultiObserverTest.java create mode 100644 src/test/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserverTest.java rename src/test/java/io/reactivex/rxjava3/internal/operators/completable/{CompletableAndThenCompletableabTest.java => CompletableAndThenCompletableTest.java} (96%) create mode 100644 src/test/java/io/reactivex/rxjava3/internal/schedulers/IoSchedulerInternalTest.java diff --git a/src/main/java/io/reactivex/rxjava3/core/Completable.java b/src/main/java/io/reactivex/rxjava3/core/Completable.java index 682a493849..708818db92 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Completable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Completable.java @@ -981,7 +981,7 @@ private static Completable merge0(@NonNull Publisher<@NonNull ? extends Completa @SafeVarargs public static Completable mergeArrayDelayError(@NonNull CompletableSource... sources) { Objects.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new CompletableMergeDelayErrorArray(sources)); + return RxJavaPlugins.onAssembly(new CompletableMergeArrayDelayError(sources)); } /** diff --git a/src/main/java/io/reactivex/rxjava3/internal/functions/Functions.java b/src/main/java/io/reactivex/rxjava3/internal/functions/Functions.java index 1d3055eb2b..659bcbcb2e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/functions/Functions.java +++ b/src/main/java/io/reactivex/rxjava3/internal/functions/Functions.java @@ -281,13 +281,8 @@ public static Predicate equalsWith(T value) { return new EqualsPredicate<>(value); } - enum HashSetCallable implements Supplier>, Callable> { + enum HashSetSupplier implements Supplier> { INSTANCE; - @Override - public Set call() { - return new HashSet<>(); - } - @Override public Set get() { return new HashSet<>(); @@ -296,7 +291,7 @@ public Set get() { @SuppressWarnings({ "rawtypes", "unchecked" }) public static Supplier> createHashSet() { - return (Supplier)HashSetCallable.INSTANCE; + return (Supplier)HashSetSupplier.INSTANCE; } static final class NotificationOnNext implements Consumer { @@ -742,12 +737,7 @@ public boolean test(Object o) { } } - static final class NullProvider implements Callable, Supplier { - @Override - public Object call() { - return null; - } - + static final class NullProvider implements Supplier { @Override public Object get() { return null; diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/FutureObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/FutureObserver.java index 515e23e6bf..5c25e3f59c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/FutureObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/FutureObserver.java @@ -128,22 +128,15 @@ public void onNext(T t) { @Override public void onError(Throwable t) { if (error == null) { - error = t; - - for (;;) { - Disposable a = upstream.get(); - if (a == this || a == DisposableHelper.DISPOSED) { - RxJavaPlugins.onError(t); - return; - } - if (upstream.compareAndSet(a, this)) { - countDown(); - return; - } + Disposable a = upstream.get(); + if (a != this && a != DisposableHelper.DISPOSED + && upstream.compareAndSet(a, this)) { + error = t; + countDown(); + return; } - } else { - RxJavaPlugins.onError(t); } + RxJavaPlugins.onError(t); } @Override @@ -152,15 +145,12 @@ public void onComplete() { onError(new NoSuchElementException("The source is empty")); return; } - for (;;) { - Disposable a = upstream.get(); - if (a == this || a == DisposableHelper.DISPOSED) { - return; - } - if (upstream.compareAndSet(a, this)) { - countDown(); - return; - } + Disposable a = upstream.get(); + if (a == this || a == DisposableHelper.DISPOSED) { + return; + } + if (upstream.compareAndSet(a, this)) { + countDown(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserver.java index db89a98f6e..f7ad3e3350 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserver.java @@ -114,8 +114,4 @@ public void setDone() { public SimpleQueue queue() { return queue; } - - public int fusionMode() { - return fusionMode; - } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserver.java index 67bc900d17..6f1cad3c34 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserver.java @@ -57,10 +57,6 @@ public final boolean enter() { return wip.getAndIncrement() == 0; } - public final boolean fastEnter() { - return wip.get() == 0 && wip.compareAndSet(0, 1); - } - protected final void fastPathEmit(U value, boolean delayError, Disposable dispose) { final Observer observer = downstream; final SimplePlainQueue q = queue; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcat.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcat.java index c16b847c58..c3458e4cd3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcat.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcat.java @@ -182,9 +182,8 @@ void drain() { boolean empty = cs == null; if (d && empty) { - if (once.compareAndSet(false, true)) { - downstream.onComplete(); - } + // errors never set done or call drain. + downstream.onComplete(); return; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArray.java index ec9c675210..49037b4c65 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArray.java @@ -86,9 +86,8 @@ public void onError(Throwable e) { @Override public void onComplete() { if (decrementAndGet() == 0) { - if (once.compareAndSet(false, true)) { - downstream.onComplete(); - } + // errors don't decrement this + downstream.onComplete(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArrayDelayError.java similarity index 96% rename from src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorArray.java rename to src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArrayDelayError.java index 549fd10a48..b939396c9f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArrayDelayError.java @@ -19,11 +19,11 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.internal.util.AtomicThrowable; -public final class CompletableMergeDelayErrorArray extends Completable { +public final class CompletableMergeArrayDelayError extends Completable { final CompletableSource[] sources; - public CompletableMergeDelayErrorArray(CompletableSource[] sources) { + public CompletableMergeArrayDelayError(CompletableSource[] sources) { this.sources = sources; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorIterable.java index 55efbd8915..f7f4c068a6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorIterable.java @@ -20,7 +20,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.exceptions.Exceptions; -import io.reactivex.rxjava3.internal.operators.completable.CompletableMergeDelayErrorArray.*; +import io.reactivex.rxjava3.internal.operators.completable.CompletableMergeArrayDelayError.*; import io.reactivex.rxjava3.internal.util.AtomicThrowable; public final class CompletableMergeDelayErrorIterable extends Completable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterable.java index b0e5b87bf7..728b372a30 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterable.java @@ -128,9 +128,8 @@ public void onError(Throwable e) { @Override public void onComplete() { if (wip.decrementAndGet() == 0) { - if (compareAndSet(false, true)) { - downstream.onComplete(); - } + // errors don't decrement wip + downstream.onComplete(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java index 57ca264f0c..6972bcc777 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java @@ -99,11 +99,8 @@ private boolean moveToNext() { if (nextNotification.isOnComplete()) { return false; } - if (nextNotification.isOnError()) { - error = nextNotification.getError(); - throw ExceptionHelper.wrapOrThrow(error); - } - throw new IllegalStateException("Should not reach here"); + error = nextNotification.getError(); + throw ExceptionHelper.wrapOrThrow(error); } catch (InterruptedException e) { subscriber.dispose(); error = e; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBuffer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBuffer.java index 04f5ae182e..3fe2fb1a96 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBuffer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBuffer.java @@ -133,6 +133,7 @@ public void onError(Throwable t) { RxJavaPlugins.onError(t); return; } + buffer = null; done = true; downstream.onError(t); } @@ -145,8 +146,9 @@ public void onComplete() { done = true; C b = buffer; + buffer = null; - if (b != null && !b.isEmpty()) { + if (b != null) { downstream.onNext(b); } downstream.onComplete(); @@ -390,7 +392,7 @@ public void onNext(T t) { C b = bs.peek(); - if (b != null && b.size() + 1 == size) { + if (b.size() + 1 == size) { bs.poll(); b.add(t); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest.java index 392de62d22..8a1fea8346 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest.java @@ -13,7 +13,6 @@ package io.reactivex.rxjava3.internal.operators.flowable; -import java.util.Iterator; import java.util.Objects; import java.util.concurrent.atomic.*; @@ -73,75 +72,46 @@ public FlowableCombineLatest(@NonNull Iterable> @SuppressWarnings("unchecked") @Override public void subscribeActual(Subscriber s) { - Publisher[] a = array; - int n; - if (a == null) { - n = 0; - a = new Publisher[8]; - - Iterator> it; + Publisher[] sources = array; + int count; + if (sources == null) { + count = 0; + sources = new Publisher[8]; try { - it = Objects.requireNonNull(iterable.iterator(), "The iterator returned is null"); - } catch (Throwable e) { - Exceptions.throwIfFatal(e); - EmptySubscription.error(e, s); - return; - } - - for (;;) { - - boolean b; - - try { - b = it.hasNext(); - } catch (Throwable e) { - Exceptions.throwIfFatal(e); - EmptySubscription.error(e, s); - return; - } - - if (!b) { - break; - } - - Publisher p; - - try { - p = Objects.requireNonNull(it.next(), "The publisher returned by the iterator is null"); - } catch (Throwable e) { - Exceptions.throwIfFatal(e); - EmptySubscription.error(e, s); - return; - } - - if (n == a.length) { - Publisher[] c = new Publisher[n + (n >> 2)]; - System.arraycopy(a, 0, c, 0, n); - a = c; + for (Publisher p : iterable) { + if (count == sources.length) { + Publisher[] b = new Publisher[count + (count >> 2)]; + System.arraycopy(sources, 0, b, 0, count); + sources = b; + } + sources[count++] = Objects.requireNonNull(p, "The Iterator returned a null Publisher"); } - a[n++] = p; + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptySubscription.error(ex, s); + return; } } else { - n = a.length; + count = sources.length; } - if (n == 0) { + if (count == 0) { EmptySubscription.complete(s); return; } - if (n == 1) { - a[0].subscribe(new MapSubscriber<>(s, new SingletonArrayFunc())); + if (count == 1) { + sources[0].subscribe(new MapSubscriber<>(s, new SingletonArrayFunc())); return; } CombineLatestCoordinator coordinator = - new CombineLatestCoordinator<>(s, combiner, n, bufferSize, delayErrors); + new CombineLatestCoordinator<>(s, combiner, count, bufferSize, delayErrors); s.onSubscribe(coordinator); - coordinator.subscribe(a, n); + coordinator.subscribe(sources, count); } static final class CombineLatestCoordinator @@ -173,7 +143,7 @@ static final class CombineLatestCoordinator volatile boolean done; - final AtomicReference error; + final AtomicThrowable error; CombineLatestCoordinator(Subscriber actual, Function combiner, int n, @@ -189,7 +159,7 @@ static final class CombineLatestCoordinator this.latest = new Object[n]; this.queue = new SpscLinkedArrayQueue<>(bufferSize); this.requested = new AtomicLong(); - this.error = new AtomicReference<>(); + this.error = new AtomicThrowable(); this.delayErrors = delayErrors; } @@ -205,6 +175,7 @@ public void request(long n) { public void cancel() { cancelled = true; cancelAll(); + drain(); } void subscribe(Publisher[] sources, int n) { @@ -411,6 +382,7 @@ boolean checkTerminated(boolean d, boolean empty, Subscriber a, SpscLinkedArr if (cancelled) { cancelAll(); q.clear(); + error.tryTerminateAndReport(); return true; } @@ -418,13 +390,7 @@ boolean checkTerminated(boolean d, boolean empty, Subscriber a, SpscLinkedArr if (delayErrors) { if (empty) { cancelAll(); - Throwable e = ExceptionHelper.terminate(error); - - if (e != null && e != ExceptionHelper.TERMINATED) { - a.onError(e); - } else { - a.onComplete(); - } + error.tryTerminateConsumer(a); return true; } } else { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java index 98d8ea4410..aea261f5aa 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java @@ -195,35 +195,19 @@ void subscribeActual() { @Override public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - inner.cancel(); - - if (getAndIncrement() == 0) { - errors.tryTerminateConsumer(downstream); - } - } + inner.cancel(); + HalfSerializer.onError(downstream, t, this, errors); } @Override public void innerNext(R value) { - if (get() == 0 && compareAndSet(0, 1)) { - downstream.onNext(value); - if (compareAndSet(1, 0)) { - return; - } - errors.tryTerminateConsumer(downstream); - } + HalfSerializer.onNext(downstream, value, this, errors); } @Override public void innerError(Throwable e) { - if (errors.tryAddThrowableOrReport(e)) { - upstream.cancel(); - - if (getAndIncrement() == 0) { - errors.tryTerminateConsumer(downstream); - } - } + upstream.cancel(); + HalfSerializer.onError(downstream, e, this, errors); } @Override @@ -318,12 +302,8 @@ void drain() { } if (inner.isUnbounded()) { - if (get() == 0 && compareAndSet(0, 1)) { - downstream.onNext(vr); - if (!compareAndSet(1, 0)) { - errors.tryTerminateConsumer(downstream); - return; - } + if (!HalfSerializer.onNext(downstream, vr, this, errors)) { + return; } continue; } else { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEager.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEager.java index ea39af45a2..ead625a829 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEager.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEager.java @@ -318,7 +318,7 @@ public void drain() { e++; - inner.requestOne(); + inner.request(1L); } if (e == r) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java index d59677efa1..3dfcd38b9d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java @@ -204,9 +204,13 @@ public void onError(Throwable t) { } } + boolean tryEnter() { + return get() == 0 && compareAndSet(0, 1); + } + @Override public void innerNext(R value) { - if (get() == 0 && compareAndSet(0, 1)) { + if (tryEnter()) { downstream.onNext(value); if (compareAndSet(1, 0)) { return; @@ -325,12 +329,12 @@ public void run() { return; } - if (vr == null) { + if (vr == null || cancelled) { continue; } if (inner.isUnbounded()) { - if (get() == 0 && compareAndSet(0, 1)) { + if (tryEnter()) { downstream.onNext(vr); if (!compareAndSet(1, 0)) { errors.tryTerminateConsumer(downstream); @@ -515,7 +519,7 @@ public void run() { vr = null; } - if (vr == null) { + if (vr == null || cancelled) { continue; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMap.java index 0a671cf88a..184efcc203 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMap.java @@ -150,14 +150,14 @@ public void onNext(T t) { if (u != null) { tryEmitScalar(u); } else { - if (maxConcurrency != Integer.MAX_VALUE && !cancelled - && ++scalarEmitted == scalarLimit) { + if (maxConcurrency != Integer.MAX_VALUE + && !cancelled && ++scalarEmitted == scalarLimit) { scalarEmitted = 0; upstream.request(scalarLimit); } } } else { - InnerSubscriber inner = new InnerSubscriber<>(this, uniqueId++); + InnerSubscriber inner = new InnerSubscriber<>(this, bufferSize, uniqueId++); if (addInner(inner)) { p.subscribe(inner); } @@ -234,8 +234,8 @@ void tryEmitScalar(U value) { if (r != Long.MAX_VALUE) { requested.decrementAndGet(); } - if (maxConcurrency != Integer.MAX_VALUE && !cancelled - && ++scalarEmitted == scalarLimit) { + if (maxConcurrency != Integer.MAX_VALUE + && !cancelled && ++scalarEmitted == scalarLimit) { scalarEmitted = 0; upstream.request(scalarLimit); } @@ -244,8 +244,7 @@ void tryEmitScalar(U value) { q = getMainQueue(); } if (!q.offer(value)) { - onError(new IllegalStateException("Scalar queue full?!")); - return; + onError(new MissingBackpressureException("Scalar queue full?!")); } } if (decrementAndGet() == 0) { @@ -254,7 +253,7 @@ void tryEmitScalar(U value) { } else { SimpleQueue q = getMainQueue(); if (!q.offer(value)) { - onError(new IllegalStateException("Scalar queue full?!")); + onError(new MissingBackpressureException("Scalar queue full?!")); return; } if (getAndIncrement() != 0) { @@ -264,15 +263,6 @@ void tryEmitScalar(U value) { drainLoop(); } - SimpleQueue getInnerQueue(InnerSubscriber inner) { - SimpleQueue q = inner.queue; - if (q == null) { - q = new SpscArrayQueue<>(bufferSize); - inner.queue = q; - } - return q; - } - void tryEmit(U value, InnerSubscriber inner) { if (get() == 0 && compareAndSet(0, 1)) { long r = requested.get(); @@ -285,11 +275,11 @@ void tryEmit(U value, InnerSubscriber inner) { inner.requestMore(1); } else { if (q == null) { - q = getInnerQueue(inner); + q = new SpscArrayQueue<>(bufferSize); + inner.queue = q; } if (!q.offer(value)) { onError(new MissingBackpressureException("Inner queue full?!")); - return; } } if (decrementAndGet() == 0) { @@ -384,35 +374,30 @@ void drainLoop() { long replenishMain = 0; if (svq != null) { - for (;;) { - long scalarEmission = 0; - U o = null; - while (r != 0L) { - o = svq.poll(); + long scalarEmission = 0; + U o = null; + while (r != 0L) { + o = svq.poll(); - if (checkTerminate()) { - return; - } - if (o == null) { - break; - } - - child.onNext(o); - - replenishMain++; - scalarEmission++; - r--; - } - if (scalarEmission != 0L) { - if (unbounded) { - r = Long.MAX_VALUE; - } else { - r = requested.addAndGet(-scalarEmission); - } + if (checkTerminate()) { + return; } - if (r == 0L || o == null) { + if (o == null) { break; } + + child.onNext(o); + + replenishMain++; + scalarEmission++; + r--; + } + if (scalarEmission != 0L) { + if (unbounded) { + r = Long.MAX_VALUE; + } else { + r = requested.addAndGet(-scalarEmission); + } } } @@ -462,15 +447,15 @@ void drainLoop() { U o = null; for (;;) { - if (checkTerminate()) { - return; - } SimpleQueue q = is.queue; if (q == null) { break; } long produced = 0; while (r != 0L) { + if (checkTerminate()) { + return; + } try { o = q.poll(); @@ -495,10 +480,6 @@ void drainLoop() { child.onNext(o); - if (checkTerminate()) { - return; - } - r--; produced++; } @@ -571,15 +552,12 @@ void clearScalarQueue() { } void disposeAll() { - InnerSubscriber[] a = subscribers.get(); + InnerSubscriber[] a = subscribers.getAndSet(CANCELLED); if (a != CANCELLED) { - a = subscribers.getAndSet(CANCELLED); - if (a != CANCELLED) { - for (InnerSubscriber inner : a) { - inner.dispose(); - } - errors.tryTerminateAndReport(); + for (InnerSubscriber inner : a) { + inner.dispose(); } + errors.tryTerminateAndReport(); } } @@ -611,10 +589,10 @@ static final class InnerSubscriber extends AtomicReference long produced; int fusionMode; - InnerSubscriber(MergeSubscriber parent, long id) { + InnerSubscriber(MergeSubscriber parent, int bufferSize, long id) { this.id = id; this.parent = parent; - this.bufferSize = parent.bufferSize; + this.bufferSize = bufferSize; this.limit = bufferSize >> 2; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletable.java index 945e6a877e..2960cfc510 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletable.java @@ -138,9 +138,7 @@ public void onError(Throwable e) { cancelled = true; upstream.cancel(); set.dispose(); - if (getAndSet(0) > 0) { - errors.tryTerminateConsumer(downstream); - } + errors.tryTerminateConsumer(downstream); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableCompletable.java index 00445c8018..6032dcdd33 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableCompletable.java @@ -146,9 +146,7 @@ public void onError(Throwable e) { disposed = true; upstream.cancel(); set.dispose(); - if (getAndSet(0) > 0) { - errors.tryTerminateConsumer(downstream); - } + errors.tryTerminateConsumer(downstream); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybe.java index 1036e73973..ffb5763f14 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybe.java @@ -174,7 +174,7 @@ void innerSuccess(InnerObserver inner, R value) { SpscLinkedArrayQueue q = queue.get(); - if (d && (q == null || q.isEmpty())) { + if (checkTerminate(d, q)) { errors.tryTerminateConsumer(downstream); return; } @@ -205,16 +205,15 @@ void innerSuccess(InnerObserver inner, R value) { } SpscLinkedArrayQueue getOrCreateQueue() { - for (;;) { - SpscLinkedArrayQueue current = queue.get(); - if (current != null) { - return current; - } - current = new SpscLinkedArrayQueue<>(Flowable.bufferSize()); - if (queue.compareAndSet(null, current)) { - return current; - } + SpscLinkedArrayQueue current = queue.get(); + if (current != null) { + return current; } + current = new SpscLinkedArrayQueue<>(Flowable.bufferSize()); + if (queue.compareAndSet(null, current)) { + return current; + } + return queue.get(); } void innerError(InnerObserver inner, Throwable e) { @@ -240,7 +239,7 @@ void innerComplete(InnerObserver inner) { boolean d = active.decrementAndGet() == 0; SpscLinkedArrayQueue q = queue.get(); - if (d && (q == null || q.isEmpty())) { + if (checkTerminate(d, q)) { errors.tryTerminateConsumer(downstream); return; } @@ -261,6 +260,10 @@ void innerComplete(InnerObserver inner) { } } + static boolean checkTerminate(boolean d, SpscLinkedArrayQueue q) { + return d && (q == null || q.isEmpty()); + } + void drain() { if (getAndIncrement() == 0) { drainLoop(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingle.java index d0c9002669..f733eb7fa5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingle.java @@ -205,16 +205,15 @@ void innerSuccess(InnerObserver inner, R value) { } SpscLinkedArrayQueue getOrCreateQueue() { - for (;;) { - SpscLinkedArrayQueue current = queue.get(); - if (current != null) { - return current; - } - current = new SpscLinkedArrayQueue<>(Flowable.bufferSize()); - if (queue.compareAndSet(null, current)) { - return current; - } + SpscLinkedArrayQueue current = queue.get(); + if (current != null) { + return current; + } + current = new SpscLinkedArrayQueue<>(Flowable.bufferSize()); + if (queue.compareAndSet(null, current)) { + return current; } + return queue.get(); } void innerError(InnerObserver inner, Throwable e) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterable.java index 7ae3f3e7d1..5c0642021d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterable.java @@ -73,14 +73,14 @@ public static void subscribe(Subscriber s, Iterator abstract static class BaseRangeSubscription extends BasicQueueSubscription { private static final long serialVersionUID = -2252972430506210021L; - Iterator it; + Iterator iterator; volatile boolean cancelled; boolean once; BaseRangeSubscription(Iterator it) { - this.it = it; + this.iterator = it; } @Override @@ -91,27 +91,34 @@ public final int requestFusion(int mode) { @Nullable @Override public final T poll() { - if (it == null) { + if (iterator == null) { return null; } if (!once) { once = true; } else { - if (!it.hasNext()) { + if (!iterator.hasNext()) { return null; } } - return Objects.requireNonNull(it.next(), "Iterator.next() returned a null value"); + return Objects.requireNonNull(iterator.next(), "Iterator.next() returned a null value"); } @Override public final boolean isEmpty() { - return it == null || !it.hasNext(); + Iterator it = this.iterator; + if (it != null) { + if (!once || it.hasNext()) { + return false; + } + clear(); + } + return true; } @Override public final void clear() { - it = null; + iterator = null; } @Override @@ -150,7 +157,7 @@ static final class IteratorSubscription extends BaseRangeSubscription { @Override void fastPath() { - Iterator it = this.it; + Iterator it = this.iterator; Subscriber a = downstream; for (;;) { if (cancelled) { @@ -204,7 +211,7 @@ void fastPath() { @Override void slowPath(long r) { long e = 0L; - Iterator it = this.it; + Iterator it = this.iterator; Subscriber a = downstream; for (;;) { @@ -286,7 +293,7 @@ static final class IteratorConditionalSubscription extends BaseRangeSubscript @Override void fastPath() { - Iterator it = this.it; + Iterator it = this.iterator; ConditionalSubscriber a = downstream; for (;;) { if (cancelled) { @@ -340,7 +347,7 @@ void fastPath() { @Override void slowPath(long r) { long e = 0L; - Iterator it = this.it; + Iterator it = this.iterator; ConditionalSubscriber a = downstream; for (;;) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoin.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoin.java index a950de7a96..e832cf8950 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoin.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoin.java @@ -330,7 +330,7 @@ else if (mode == LEFT_CLOSE) { up.onComplete(); } } - else if (mode == RIGHT_CLOSE) { + else { LeftRightEndSubscriber end = (LeftRightEndSubscriber)val; rights.remove(end.index); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoin.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoin.java index 01cf71023f..3406b7411d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoin.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoin.java @@ -339,7 +339,7 @@ else if (mode == LEFT_CLOSE) { lefts.remove(end.index); disposables.remove(end); } - else if (mode == RIGHT_CLOSE) { + else { LeftRightEndSubscriber end = (LeftRightEndSubscriber)val; rights.remove(end.index); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn.java index 3a4f237e5b..1ec41a6374 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn.java @@ -350,15 +350,10 @@ void runSync() { return; } - int w = get(); - if (missed == w) { - produced = e; - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + produced = e; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } @@ -593,15 +588,10 @@ void runSync() { return; } - int w = get(); - if (missed == w) { - produced = e; - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + produced = e; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } @@ -662,16 +652,11 @@ void runAsync() { return; } - int w = get(); - if (missed == w) { - produced = emitted; - consumed = polled; - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + produced = emitted; + consumed = polled; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublish.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublish.java index 49e75fc1fd..c1d5675cf1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublish.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublish.java @@ -60,14 +60,6 @@ public Publisher source() { return source; } - /** - * The internal buffer size of this FlowablePublishAlt operator. - * @return The internal buffer size of this FlowablePublishAlt operator. - */ - public int publishBufferSize() { - return bufferSize; - } - @Override public void connect(Consumer connection) { PublishConnection conn; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java index db8718a433..258db5f3bc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java @@ -246,10 +246,8 @@ static final class ReplaySubscriber final AtomicInteger management; - /** Contains the maximum element index the child Subscribers requested so far. Accessed while emitting is true. */ - long maxChildRequested; - /** Counts the outstanding upstream requests until the producer arrives. */ - long maxUpstreamRequested; + /** Tracks the amount already requested from the upstream. */ + long requestedFromUpstream; @SuppressWarnings("unchecked") ReplaySubscriber(ReplayBuffer buffer) { @@ -284,9 +282,6 @@ public void dispose() { */ @SuppressWarnings("unchecked") boolean add(InnerSubscription producer) { - if (producer == null) { - throw new NullPointerException(); - } // the state can change so we do a CAS loop to achieve atomicity for (;;) { // get the current producer array @@ -415,7 +410,8 @@ public void onComplete() { * Coordinates the request amounts of various child Subscribers. */ void manageRequests() { - if (management.getAndIncrement() != 0) { + AtomicInteger m = management; + if (m.getAndIncrement() != 0) { return; } int missed = 1; @@ -424,46 +420,29 @@ void manageRequests() { if (isDisposed()) { return; } + Subscription p = get(); - InnerSubscription[] a = subscribers.get(); - - long ri = maxChildRequested; - long maxTotalRequests = ri; - - for (InnerSubscription rp : a) { - maxTotalRequests = Math.max(maxTotalRequests, rp.totalRequested.get()); - } + // only request when there is an upstream Subscription available + if (p != null) { + // how many items were requested so far + long alreadyRequested = requestedFromUpstream; + long downstreamMaxRequest = alreadyRequested; - long ur = maxUpstreamRequested; - Subscription p = get(); + // find out the maximum total requested of the current subscribers + for (InnerSubscription rp : subscribers.get()) { + downstreamMaxRequest = Math.max(downstreamMaxRequest, rp.totalRequested.get()); + } - long diff = maxTotalRequests - ri; - if (diff != 0L) { - maxChildRequested = maxTotalRequests; - if (p != null) { - if (ur != 0L) { - maxUpstreamRequested = 0L; - p.request(ur + diff); - } else { - p.request(diff); - } - } else { - // collect upstream request amounts until there is a producer for them - long u = ur + diff; - if (u < 0) { - u = Long.MAX_VALUE; - } - maxUpstreamRequested = u; + // how much more to request from the upstream + long diff = downstreamMaxRequest - alreadyRequested; + if (diff != 0L) { + // save the new maximum requested + requestedFromUpstream = downstreamMaxRequest; + p.request(diff); } - } else - // if there were outstanding upstream requests and we have a producer - if (ur != 0L && p != null) { - maxUpstreamRequested = 0L; - // fire the accumulated requests - p.request(ur); } - missed = management.addAndGet(-missed); + missed = m.addAndGet(-missed); if (missed == 0) { break; } @@ -668,6 +647,8 @@ public void replay(InnerSubscription output) { output.dispose(); if (!NotificationLite.isError(o) && !NotificationLite.isComplete(o)) { child.onError(err); + } else { + RxJavaPlugins.onError(err); } return; } @@ -717,7 +698,7 @@ static final class Node extends AtomicReference { * * @param the value type */ - static class BoundedReplayBuffer extends AtomicReference implements ReplayBuffer { + abstract static class BoundedReplayBuffer extends AtomicReference implements ReplayBuffer { private static final long serialVersionUID = 2346567790059478686L; @@ -829,11 +810,6 @@ public final void replay(InnerSubscription output) { output.emitting = true; } for (;;) { - if (output.isDisposed()) { - output.index = null; - return; - } - long r = output.get(); boolean unbounded = r == Long.MAX_VALUE; // NOPMD long e = 0L; @@ -847,6 +823,11 @@ public final void replay(InnerSubscription output) { } while (r != 0) { + if (output.isDisposed()) { + output.index = null; + return; + } + Node v = node.get(); if (v != null) { Object o = leaveTransform(v.value); @@ -861,6 +842,8 @@ public final void replay(InnerSubscription output) { output.dispose(); if (!NotificationLite.isError(o) && !NotificationLite.isComplete(o)) { output.child.onError(err); + } else { + RxJavaPlugins.onError(err); } return; } @@ -870,10 +853,11 @@ public final void replay(InnerSubscription output) { } else { break; } - if (output.isDisposed()) { - output.index = null; - return; - } + } + + if (r == 0 && output.isDisposed()) { + output.index = null; + return; } if (e != 0L) { @@ -917,9 +901,7 @@ Object leaveTransform(Object value) { * Override this method to truncate a non-terminated buffer * based on its current properties. */ - void truncate() { - - } + abstract void truncate(); /** * Override this method to truncate a terminated buffer * based on its properties (i.e., truncate but the very last node). @@ -1021,7 +1003,7 @@ void truncate() { int e = 0; for (;;) { - if (next != null && size > 1) { // never truncate the very last item just added + if (size > 1) { // never truncate the very last item just added if (size > limit) { e++; size--; @@ -1056,7 +1038,7 @@ void truncateFinal() { int e = 0; for (;;) { - if (next != null && size > 1) { + if (size > 1) { Timed v = (Timed)next.value; if (v.time() <= timeLimit) { e++; @@ -1149,31 +1131,6 @@ public void accept(Disposable r) { } } - static final class ConnectableFlowableReplay extends ConnectableFlowable { - private final ConnectableFlowable cf; - private final Flowable flowable; - - ConnectableFlowableReplay(ConnectableFlowable cf, Flowable flowable) { - this.cf = cf; - this.flowable = flowable; - } - - @Override - public void connect(Consumer connection) { - cf.connect(connection); - } - - @Override - public void reset() { - cf.reset(); - } - - @Override - protected void subscribeActual(Subscriber s) { - flowable.subscribe(s); - } - } - static final class ReplayBufferSupplier implements Supplier> { final int bufferSize; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchMap.java index 7c6c1006ca..8e8ff3f0a5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchMap.java @@ -180,12 +180,9 @@ public void cancel() { @SuppressWarnings("unchecked") void disposeInner() { - SwitchMapInnerSubscriber a = active.get(); - if (a != CANCELLED) { - a = active.getAndSet((SwitchMapInnerSubscriber)CANCELLED); - if (a != CANCELLED && a != null) { - a.cancel(); - } + SwitchMapInnerSubscriber a = active.getAndSet((SwitchMapInnerSubscriber)CANCELLED); + if (a != CANCELLED && a != null) { + a.cancel(); } } @@ -228,26 +225,6 @@ void drain() { SwitchMapInnerSubscriber inner = active.get(); SimpleQueue q = inner != null ? inner.queue : null; if (q != null) { - if (inner.done) { - if (!delayErrors) { - Throwable err = errors.get(); - if (err != null) { - disposeInner(); - errors.tryTerminateConsumer(a); - return; - } else - if (q.isEmpty()) { - active.compareAndSet(inner, null); - continue; - } - } else { - if (q.isEmpty()) { - active.compareAndSet(inner, null); - continue; - } - } - } - long r = requested.get(); long e = 0L; boolean retry = false; @@ -306,6 +283,28 @@ void drain() { e++; } + if (e == r) { + if (inner.done) { + if (!delayErrors) { + Throwable err = errors.get(); + if (err != null) { + disposeInner(); + errors.tryTerminateConsumer(a); + return; + } else + if (q.isEmpty()) { + active.compareAndSet(inner, null); + continue; + } + } else { + if (q.isEmpty()) { + active.compareAndSet(inner, null); + continue; + } + } + } + } + if (e != 0L) { if (!cancelled) { if (r != Long.MAX_VALUE) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLast.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLast.java index 062135b47c..5098b589fd 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLast.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLast.java @@ -119,8 +119,12 @@ void drain() { a.onNext(v); e++; } - if (e != 0L && r != Long.MAX_VALUE) { - r = requested.addAndGet(-e); + if (isEmpty()) { + a.onComplete(); + return; + } + if (e != 0L) { + r = BackpressureHelper.produced(requested, e); } } } while (wip.decrementAndGet() != 0); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindow.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindow.java index e00ed99edf..1f139bad1c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindow.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindow.java @@ -22,8 +22,7 @@ import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.*; +import io.reactivex.rxjava3.processors.UnicastProcessor; public final class FlowableWindow extends AbstractFlowableWithUpstream> { final long size; @@ -358,10 +357,6 @@ public void onSubscribe(Subscription s) { @Override public void onNext(T t) { - if (done) { - return; - } - long i = index; UnicastProcessor newWindow = null; @@ -407,11 +402,6 @@ public void onNext(T t) { @Override public void onError(Throwable t) { - if (done) { - RxJavaPlugins.onError(t); - return; - } - for (Processor w : windows) { w.onError(t); } @@ -424,10 +414,6 @@ public void onError(Throwable t) { @Override public void onComplete() { - if (done) { - return; - } - for (Processor w : windows) { w.onComplete(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZip.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZip.java index 77011a5a2d..d3da152716 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZip.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZip.java @@ -191,23 +191,12 @@ void drain() { for (int j = 0; j < n; j++) { ZipSubscriber inner = qs[j]; if (values[j] == null) { + boolean d = inner.done; + SimpleQueue q = inner.queue; + T v = null; try { - boolean d = inner.done; - SimpleQueue q = inner.queue; - T v = q != null ? q.poll() : null; - - boolean sourceEmpty = v == null; - if (d && sourceEmpty) { - cancelAll(); - errors.tryTerminateConsumer(a); - return; - } - if (!sourceEmpty) { - values[j] = v; - } else { - empty = true; - } + v = q != null ? q.poll() : null; } catch (Throwable ex) { Exceptions.throwIfFatal(ex); @@ -217,6 +206,18 @@ void drain() { errors.tryTerminateConsumer(a); return; } + d = true; + } + + boolean sourceEmpty = v == null; + if (d && sourceEmpty) { + cancelAll(); + errors.tryTerminateConsumer(a); + return; + } + if (!sourceEmpty) { + values[j] = v; + } else { empty = true; } } @@ -259,20 +260,11 @@ void drain() { for (int j = 0; j < n; j++) { ZipSubscriber inner = qs[j]; if (values[j] == null) { + boolean d = inner.done; + SimpleQueue q = inner.queue; + T v = null; try { - boolean d = inner.done; - SimpleQueue q = inner.queue; - T v = q != null ? q.poll() : null; - - boolean empty = v == null; - if (d && empty) { - cancelAll(); - errors.tryTerminateConsumer(a); - return; - } - if (!empty) { - values[j] = v; - } + v = q != null ? q.poll() : null; } catch (Throwable ex) { Exceptions.throwIfFatal(ex); errors.tryAddThrowableOrReport(ex); @@ -281,6 +273,16 @@ void drain() { errors.tryTerminateConsumer(a); return; } + d = true; + } + boolean empty = v == null; + if (d && empty) { + cancelAll(); + errors.tryTerminateConsumer(a); + return; + } + if (!empty) { + values[j] = v; } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmb.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmb.java index 8bc43aa41c..ffab752652 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmb.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmb.java @@ -115,9 +115,8 @@ public boolean win(int index) { } return true; } - return false; } - return w == index; + return false; } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTimed.java index 8a7becc241..e5e4383956 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTimed.java @@ -121,11 +121,9 @@ public void onSubscribe(Disposable d) { downstream.onSubscribe(this); - if (!cancelled) { + if (!DisposableHelper.isDisposed(timer.get())) { Disposable task = scheduler.schedulePeriodicallyDirect(this, timespan, timespan, unit); - if (!timer.compareAndSet(null, task)) { - task.dispose(); - } + DisposableHelper.set(timer, task); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest.java index 34dce80431..646ab80690 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest.java @@ -49,13 +49,19 @@ public void subscribeActual(Observer observer) { int count = 0; if (sources == null) { sources = new ObservableSource[8]; - for (ObservableSource p : sourcesIterable) { - if (count == sources.length) { - ObservableSource[] b = new ObservableSource[count + (count >> 2)]; - System.arraycopy(sources, 0, b, 0, count); - sources = b; + try { + for (ObservableSource p : sourcesIterable) { + if (count == sources.length) { + ObservableSource[] b = new ObservableSource[count + (count >> 2)]; + System.arraycopy(sources, 0, b, 0, count); + sources = b; + } + sources[count++] = Objects.requireNonNull(p, "The Iterator returned a null ObservableSource"); } - sources[count++] = p; + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptyDisposable.error(ex, observer); + return; } } else { count = sources.length; @@ -122,9 +128,7 @@ public void dispose() { if (!cancelled) { cancelled = true; cancelSources(); - if (getAndIncrement() == 0) { - clear(queue); - } + drain(); } } @@ -161,6 +165,7 @@ void drain() { for (;;) { if (cancelled) { clear(q); + errors.tryTerminateAndReport(); return; } @@ -240,7 +245,6 @@ void innerError(int index, Throwable ex) { if (latest == null) { return; } - cancelOthers = latest[index] == null; if (cancelOthers || ++complete == latest.length) { done = true; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreate.java index 66445bc8d1..f05faf7eda 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreate.java @@ -159,7 +159,7 @@ static final class SerializedEmitter @Override public void onNext(T t) { - if (emitter.isDisposed() || done) { + if (done || emitter.isDisposed()) { return; } if (t == null) { @@ -192,7 +192,7 @@ public void onError(Throwable t) { @Override public boolean tryOnError(Throwable t) { - if (emitter.isDisposed() || done) { + if (done || emitter.isDisposed()) { return false; } if (t == null) { @@ -208,7 +208,7 @@ public boolean tryOnError(Throwable t) { @Override public void onComplete() { - if (emitter.isDisposed() || done) { + if (done || emitter.isDisposed()) { return; } done = true; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java index a169627ed3..d2ca19c31a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java @@ -69,7 +69,7 @@ static final class MergeObserver extends AtomicInteger implements Disposab final AtomicThrowable errors = new AtomicThrowable(); - volatile boolean cancelled; + volatile boolean disposed; final AtomicReference[]> observers; @@ -80,7 +80,6 @@ static final class MergeObserver extends AtomicInteger implements Disposab Disposable upstream; long uniqueId; - long lastId; int lastIndex; Queue> sources; @@ -188,9 +187,6 @@ void removeInner(InnerObserver inner) { for (;;) { InnerObserver[] a = observers.get(); int n = a.length; - if (n == 0) { - return; - } int j = -1; for (int i = 0; i < n; i++) { if (a[i] == inner) { @@ -246,10 +242,7 @@ boolean tryEmitScalar(Supplier value) { queue = q; } - if (!q.offer(u)) { - onError(new IllegalStateException("Scalar queue full?!")); - return true; - } + q.offer(u); if (getAndIncrement() != 0) { return false; } @@ -301,17 +294,15 @@ public void onComplete() { @Override public void dispose() { - if (!cancelled) { - cancelled = true; - if (disposeAll()) { - errors.tryTerminateAndReport(); - } + disposed = true; + if (disposeAll()) { + errors.tryTerminateAndReport(); } } @Override public boolean isDisposed() { - return cancelled; + return disposed; } void drain() { @@ -364,29 +355,8 @@ void drainLoop() { int innerCompleted = 0; if (n != 0) { - long startId = lastId; - int index = lastIndex; - - if (n <= index || inner[index].id != startId) { - if (n <= index) { - index = 0; - } - int j = index; - for (int i = 0; i < n; i++) { - if (inner[j].id == startId) { - break; - } - j++; - if (j == n) { - j = 0; - } - } - index = j; - lastIndex = j; - lastId = inner[j].id; - } + int j = Math.min(n - 1, lastIndex); - int j = index; sourceLoop: for (int i = 0; i < n; i++) { if (checkTerminate()) { @@ -432,9 +402,6 @@ void drainLoop() { SimpleQueue innerQueue = is.queue; if (innerDone && (innerQueue == null || innerQueue.isEmpty())) { removeInner(is); - if (checkTerminate()) { - return; - } innerCompleted++; } @@ -444,7 +411,6 @@ void drainLoop() { } } lastIndex = j; - lastId = inner[j].id; } if (innerCompleted != 0) { @@ -471,7 +437,7 @@ void drainLoop() { } boolean checkTerminate() { - if (cancelled) { + if (disposed) { return true; } Throwable e = errors.get(); @@ -485,15 +451,12 @@ boolean checkTerminate() { boolean disposeAll() { upstream.dispose(); - InnerObserver[] a = observers.get(); + InnerObserver[] a = observers.getAndSet(CANCELLED); if (a != CANCELLED) { - a = observers.getAndSet(CANCELLED); - if (a != CANCELLED) { - for (InnerObserver inner : a) { - inner.dispose(); - } - return true; + for (InnerObserver inner : a) { + inner.dispose(); } + return true; } return false; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletable.java index 729e8cf6c5..1db5b140e5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletable.java @@ -116,9 +116,7 @@ public void onError(Throwable e) { disposed = true; upstream.dispose(); set.dispose(); - if (getAndSet(0) > 0) { - errors.tryTerminateConsumer(downstream); - } + errors.tryTerminateConsumer(downstream); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableCompletable.java index 2d488ac2c5..039be4ffb7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableCompletable.java @@ -122,9 +122,7 @@ public void onError(Throwable e) { disposed = true; upstream.dispose(); set.dispose(); - if (getAndSet(0) > 0) { - errors.tryTerminateConsumer(downstream); - } + errors.tryTerminateConsumer(downstream); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe.java index 7ac5ca6f3c..3547769267 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe.java @@ -172,16 +172,15 @@ void innerSuccess(InnerObserver inner, R value) { } SpscLinkedArrayQueue getOrCreateQueue() { - for (;;) { - SpscLinkedArrayQueue current = queue.get(); - if (current != null) { - return current; - } - current = new SpscLinkedArrayQueue<>(Observable.bufferSize()); - if (queue.compareAndSet(null, current)) { - return current; - } + SpscLinkedArrayQueue current = queue.get(); + if (current != null) { + return current; + } + current = new SpscLinkedArrayQueue<>(Observable.bufferSize()); + if (queue.compareAndSet(null, current)) { + return current; } + return queue.get(); } void innerError(InnerObserver inner, Throwable e) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingle.java index 8ed45d5f39..e93051fd0c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingle.java @@ -172,16 +172,15 @@ void innerSuccess(InnerObserver inner, R value) { } SpscLinkedArrayQueue getOrCreateQueue() { - for (;;) { - SpscLinkedArrayQueue current = queue.get(); - if (current != null) { - return current; - } - current = new SpscLinkedArrayQueue<>(Observable.bufferSize()); - if (queue.compareAndSet(null, current)) { - return current; - } + SpscLinkedArrayQueue current = queue.get(); + if (current != null) { + return current; + } + current = new SpscLinkedArrayQueue<>(Observable.bufferSize()); + if (queue.compareAndSet(null, current)) { + return current; } + return queue.get(); } void innerError(InnerObserver inner, Throwable e) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoin.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoin.java index 3605ee5203..2a553fd1b1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoin.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoin.java @@ -319,7 +319,7 @@ else if (mode == LEFT_CLOSE) { up.onComplete(); } } - else if (mode == RIGHT_CLOSE) { + else { LeftRightEndObserver end = (LeftRightEndObserver)val; rights.remove(end.index); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRange.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRange.java index 96d113663e..54576298a7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRange.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRange.java @@ -90,8 +90,10 @@ public void run() { downstream.onNext(c); if (c == end) { + if (!isDisposed()) { + downstream.onComplete(); + } DisposableHelper.dispose(this); - downstream.onComplete(); return; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java index 7dabfcc72d..f7937b0509 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java @@ -846,7 +846,7 @@ void truncate() { int e = 0; for (;;) { - if (next != null && size > 1) { // never truncate the very last item just added + if (size > 1) { // never truncate the very last item just added if (size > limit) { e++; size--; @@ -881,7 +881,7 @@ void truncateFinal() { int e = 0; for (;;) { - if (next != null && size > 1) { + if (size > 1) { Timed v = (Timed)next.value; if (v.time() <= timeLimit) { e++; @@ -1069,29 +1069,4 @@ protected void subscribeActual(Observer child) { co.connect(new DisposeConsumer<>(srw)); } } - - static final class Replay extends ConnectableObservable { - private final ConnectableObservable co; - private final Observable observable; - - Replay(ConnectableObservable co, Observable observable) { - this.co = co; - this.observable = observable; - } - - @Override - public void connect(Consumer connection) { - co.connect(connection); - } - - @Override - public void reset() { - co.reset(); - } - - @Override - protected void subscribeActual(Observer observer) { - observable.subscribe(observer); - } - } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap.java index e533834541..dbbb3da35a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap.java @@ -169,12 +169,9 @@ public boolean isDisposed() { @SuppressWarnings("unchecked") void disposeInner() { - SwitchMapInnerObserver a = active.get(); - if (a != CANCELLED) { - a = active.getAndSet((SwitchMapInnerObserver)CANCELLED); - if (a != CANCELLED && a != null) { - a.cancel(); - } + SwitchMapInnerObserver a = active.getAndSet((SwitchMapInnerObserver)CANCELLED); + if (a != null) { + a.cancel(); } } @@ -226,25 +223,6 @@ void drain() { SimpleQueue q = inner.queue; if (q != null) { - if (inner.done) { - boolean empty = q.isEmpty(); - if (delayErrors) { - if (empty) { - active.compareAndSet(inner, null); - continue; - } - } else { - Throwable ex = errors.get(); - if (ex != null) { - errors.tryTerminateConsumer(a); - return; - } - if (empty) { - active.compareAndSet(inner, null); - continue; - } - } - } boolean retry = false; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLast.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLast.java index bfa2ee0bdd..80f408de32 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLast.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLast.java @@ -77,9 +77,7 @@ public void onComplete() { } T v = poll(); if (v == null) { - if (!cancelled) { - a.onComplete(); - } + a.onComplete(); return; } a.onNext(v); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed.java index 041ce80840..138cb3f869 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed.java @@ -21,7 +21,6 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; import io.reactivex.rxjava3.observers.SerializedObserver; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableThrottleFirstTimed extends AbstractObservableWithUpstream { final long timeout; @@ -57,8 +56,6 @@ static final class DebounceTimedObserver volatile boolean gate; - boolean done; - DebounceTimedObserver(Observer actual, long timeout, TimeUnit unit, Worker worker) { this.downstream = actual; this.timeout = timeout; @@ -76,7 +73,7 @@ public void onSubscribe(Disposable d) { @Override public void onNext(T t) { - if (!gate && !done) { + if (!gate) { gate = true; downstream.onNext(t); @@ -96,22 +93,14 @@ public void run() { @Override public void onError(Throwable t) { - if (done) { - RxJavaPlugins.onError(t); - } else { - done = true; - downstream.onError(t); - worker.dispose(); - } + downstream.onError(t); + worker.dispose(); } @Override public void onComplete() { - if (!done) { - done = true; - downstream.onComplete(); - worker.dispose(); - } + downstream.onComplete(); + worker.dispose(); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToList.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToList.java index 6a3c5322f2..ed64de962a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToList.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToList.java @@ -20,7 +20,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.util.ExceptionHelper; public final class ObservableToList> @@ -28,12 +27,6 @@ public final class ObservableToList> final Supplier collectionSupplier; - @SuppressWarnings({ "unchecked", "rawtypes" }) - public ObservableToList(ObservableSource source, final int defaultCapacityHint) { - super(source); - this.collectionSupplier = (Supplier)Functions.createArrayList(defaultCapacityHint); - } - public ObservableToList(ObservableSource source, Supplier collectionSupplier) { super(source); this.collectionSupplier = collectionSupplier; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromPublisher.java index 0d4fb9a4c5..1d9bab143b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromPublisher.java @@ -150,10 +150,6 @@ void setupSubscribers() { final int m = subs.length; for (int i = 0; i < m; i++) { - if (cancelled) { - return; - } - subscriberCount.lazySet(i + 1); subs[i].onSubscribe(new RailSubscription(i, m)); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelJoin.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelJoin.java index 4545b133f5..57f82230a0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelJoin.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelJoin.java @@ -298,18 +298,13 @@ void drainLoop() { } } - if (e != 0 && r != Long.MAX_VALUE) { - requested.addAndGet(-e); + if (e != 0) { + BackpressureHelper.produced(requested, e); } - int w = get(); - if (w == missed) { - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } @@ -350,10 +345,9 @@ void onNext(JoinInnerSubscriber inner, T value) { SimplePlainQueue q = inner.getQueue(); if (!q.offer(value)) { - if (inner.cancel()) { - errors.tryAddThrowableOrReport(new MissingBackpressureException("Queue full?!")); - done.decrementAndGet(); - } + inner.cancel(); + errors.tryAddThrowableOrReport(new MissingBackpressureException("Queue full?!")); + done.decrementAndGet(); } if (getAndIncrement() != 0) { @@ -464,18 +458,13 @@ void drainLoop() { } } - if (e != 0 && r != Long.MAX_VALUE) { - requested.addAndGet(-e); + if (e != 0) { + BackpressureHelper.produced(requested, e); } - int w = get(); - if (w == missed) { - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelRunOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelRunOn.java index ceb2a7665a..46d3bcdc1a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelRunOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelRunOn.java @@ -430,19 +430,14 @@ public void run() { } } - if (e != 0L && r != Long.MAX_VALUE) { - requested.addAndGet(-e); + if (e != 0L) { + BackpressureHelper.produced(requested, e); } - int w = get(); - if (w == missed) { - consumed = c; - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + consumed = c; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelSortedJoin.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelSortedJoin.java index 7994abf697..3d4347d283 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelSortedJoin.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelSortedJoin.java @@ -215,48 +215,41 @@ void drain() { e++; } - if (e == r) { - if (cancelled) { - Arrays.fill(lists, null); - return; - } + if (cancelled) { + Arrays.fill(lists, null); + return; + } - Throwable ex = error.get(); - if (ex != null) { - cancelAll(); - Arrays.fill(lists, null); - a.onError(ex); - return; - } + Throwable ex = error.get(); + if (ex != null) { + cancelAll(); + Arrays.fill(lists, null); + a.onError(ex); + return; + } - boolean empty = true; + boolean empty = true; - for (int i = 0; i < n; i++) { - if (indexes[i] != lists[i].size()) { - empty = false; - break; - } + for (int i = 0; i < n; i++) { + if (indexes[i] != lists[i].size()) { + empty = false; + break; } + } - if (empty) { - Arrays.fill(lists, null); - a.onComplete(); - return; - } + if (empty) { + Arrays.fill(lists, null); + a.onComplete(); + return; } - if (e != 0 && r != Long.MAX_VALUE) { - requested.addAndGet(-e); + if (e != 0) { + BackpressureHelper.produced(requested, e); } - int w = get(); - if (w == missed) { - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithObservable.java index a0eb8ab860..44dbc50ecf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithObservable.java @@ -56,7 +56,7 @@ static final class OtherSubscriber @Override public void onSubscribe(Disposable d) { - if (DisposableHelper.set(this, d)) { + if (DisposableHelper.setOnce(this, d)) { downstream.onSubscribe(this); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleEquals.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleEquals.java index 0a839ab747..5d56487570 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleEquals.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleEquals.java @@ -74,20 +74,13 @@ public void onSuccess(T value) { @Override public void onError(Throwable e) { - for (;;) { - int state = count.get(); - if (state >= 2) { - RxJavaPlugins.onError(e); - return; - } - if (count.compareAndSet(state, 2)) { - set.dispose(); - downstream.onError(e); - return; - } + int state = count.getAndSet(-1); + if (state == 0 || state == 1) { + set.dispose(); + downstream.onError(e); + } else { + RxJavaPlugins.onError(e); } } - } - } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowable.java index 9e05a25ef8..cd16312620 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowable.java @@ -154,7 +154,7 @@ void drain() { long e = 0L; if (r == Long.MAX_VALUE) { - slowPath(a, iterator); + fastPath(a, iterator); return; } @@ -213,7 +213,7 @@ void drain() { } } - void slowPath(Subscriber a, Iterator iterator) { + void fastPath(Subscriber a, Iterator iterator) { for (;;) { if (cancelled) { return; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelper.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelper.java index 47f8bf1e88..30d1192a17 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelper.java @@ -14,7 +14,6 @@ package io.reactivex.rxjava3.internal.operators.single; import java.util.*; -import java.util.concurrent.Callable; import org.reactivestreams.Publisher; @@ -31,14 +30,9 @@ private SingleInternalHelper() { throw new IllegalStateException("No instances!"); } - enum NoSuchElementCallable implements Supplier, Callable { + enum NoSuchElementSupplier implements Supplier { INSTANCE; - @Override - public NoSuchElementException call() { - return new NoSuchElementException(); - } - @Override public NoSuchElementException get() { return new NoSuchElementException(); @@ -46,7 +40,7 @@ public NoSuchElementException get() { } public static Supplier emptyThrower() { - return NoSuchElementCallable.INSTANCE; + return NoSuchElementSupplier.INSTANCE; } @SuppressWarnings("rawtypes") diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ComputationScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ComputationScheduler.java index e0bf388772..cdbdda3d83 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ComputationScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ComputationScheduler.java @@ -175,15 +175,9 @@ public void start() { @Override public void shutdown() { - for (;;) { - FixedSchedulerPool curr = pool.get(); - if (curr == NONE) { - return; - } - if (pool.compareAndSet(curr, NONE)) { - curr.shutdown(); - return; - } + FixedSchedulerPool curr = pool.getAndSet(NONE); + if (curr != NONE) { + curr.shutdown(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java index a9547e64e1..6387069548 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java @@ -257,9 +257,7 @@ void runFair() { } Runnable run = q.poll(); - if (run != null) { - run.run(); - } + run.run(); // never null because of offer + increment happens first if (disposed) { q.clear(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java index 3a4bac58b2..6d63715770 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java @@ -93,7 +93,7 @@ static final class CachedWorkerPool implements Runnable { @Override public void run() { - evictExpiredWorkers(); + evictExpiredWorkers(expiringWorkerQueue, allWorkers); } ThreadWorker get() { @@ -120,7 +120,7 @@ void release(ThreadWorker threadWorker) { expiringWorkerQueue.offer(threadWorker); } - void evictExpiredWorkers() { + static void evictExpiredWorkers(ConcurrentLinkedQueue expiringWorkerQueue, CompositeDisposable allWorkers) { if (!expiringWorkerQueue.isEmpty()) { long currentTimestamp = now(); @@ -138,7 +138,7 @@ void evictExpiredWorkers() { } } - long now() { + static long now() { return System.nanoTime(); } @@ -178,15 +178,9 @@ public void start() { @Override public void shutdown() { - for (;;) { - CachedWorkerPool curr = pool.get(); - if (curr == NONE) { - return; - } - if (pool.compareAndSet(curr, NONE)) { - curr.shutdown(); - return; - } + CachedWorkerPool curr = pool.getAndSet(NONE); + if (curr != NONE) { + curr.shutdown(); } } @@ -241,7 +235,8 @@ public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull Ti } static final class ThreadWorker extends NewThreadWorker { - private long expirationTime; + + long expirationTime; ThreadWorker(ThreadFactory threadFactory) { super(threadFactory); diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java index ddff75388b..57033f4563 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java @@ -187,21 +187,7 @@ public boolean isDisposed() { @Override public void dispose() { - Disposable oldState; - // no matter what the current state is the new state is going to be - Disposable newState = DISPOSED; - do { - oldState = get(); - if (oldState == DISPOSED) { - // the action has already been unsubscribed - return; - } - } while (!compareAndSet(oldState, newState)); - - if (oldState != SUBSCRIBED) { - // the action was scheduled. stop it. - oldState.dispose(); - } + getAndSet(DISPOSED).dispose(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SingleScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SingleScheduler.java index 13c7430f94..2f42acbf35 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SingleScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SingleScheduler.java @@ -90,12 +90,9 @@ public void start() { @Override public void shutdown() { - ScheduledExecutorService current = executor.get(); + ScheduledExecutorService current = executor.getAndSet(SHUTDOWN); if (current != SHUTDOWN) { - current = executor.getAndSet(SHUTDOWN); - if (current != SHUTDOWN) { - current.shutdownNow(); - } + current.shutdownNow(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriber.java index 59edd4f80d..d44e4a0dd0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriber.java @@ -19,9 +19,9 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; -import io.reactivex.rxjava3.annotations.NonNull; import org.reactivestreams.Subscription; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BlockingHelper; @@ -128,18 +128,16 @@ public void onNext(T t) { @Override public void onError(Throwable t) { - for (;;) { + if (error == null) { Subscription a = upstream.get(); - if (a == this || a == SubscriptionHelper.CANCELLED) { - RxJavaPlugins.onError(t); - return; - } - error = t; - if (upstream.compareAndSet(a, this)) { + if (a != this && a != SubscriptionHelper.CANCELLED + && upstream.compareAndSet(a, this)) { + error = t; countDown(); return; } } + RxJavaPlugins.onError(t); } @Override @@ -148,15 +146,12 @@ public void onComplete() { onError(new NoSuchElementException("The source is empty")); return; } - for (;;) { - Subscription a = upstream.get(); - if (a == this || a == SubscriptionHelper.CANCELLED) { - return; - } - if (upstream.compareAndSet(a, this)) { - countDown(); - return; - } + Subscription a = upstream.get(); + if (a == this || a == SubscriptionHelper.CANCELLED) { + return; + } + if (upstream.compareAndSet(a, this)) { + countDown(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriber.java index c53138e3d7..955f9d87b1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriber.java @@ -115,18 +115,6 @@ public void request(long n) { } } - public void requestOne() { - if (fusionMode != QueueSubscription.SYNC) { - long p = produced + 1; - if (p == limit) { - produced = 0L; - get().request(p); - } else { - produced = p; - } - } - } - @Override public void cancel() { SubscriptionHelper.cancel(this); diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/HalfSerializer.java b/src/main/java/io/reactivex/rxjava3/internal/util/HalfSerializer.java index 54f787f55a..378327ea4c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/HalfSerializer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/HalfSerializer.java @@ -37,15 +37,18 @@ private HalfSerializer() { * @param value the value to emit * @param wip the serialization work-in-progress counter/indicator * @param errors the holder of Throwables + * @return true if the operation succeeded, false if there sequence completed */ - public static void onNext(Subscriber subscriber, T value, + public static boolean onNext(Subscriber subscriber, T value, AtomicInteger wip, AtomicThrowable errors) { if (wip.get() == 0 && wip.compareAndSet(0, 1)) { subscriber.onNext(value); - if (wip.decrementAndGet() != 0) { - errors.tryTerminateConsumer(subscriber); + if (wip.decrementAndGet() == 0) { + return true; } + errors.tryTerminateConsumer(subscriber); } + return false; } /** diff --git a/src/main/java/io/reactivex/rxjava3/processors/BehaviorProcessor.java b/src/main/java/io/reactivex/rxjava3/processors/BehaviorProcessor.java index de5d744f3f..54f16af80d 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/BehaviorProcessor.java +++ b/src/main/java/io/reactivex/rxjava3/processors/BehaviorProcessor.java @@ -455,16 +455,9 @@ void remove(BehaviorSubscription rs) { @SuppressWarnings("unchecked") BehaviorSubscription[] terminate(Object terminalValue) { - BehaviorSubscription[] a = subscribers.get(); - if (a != TERMINATED) { - a = subscribers.getAndSet(TERMINATED); - if (a != TERMINATED) { - // either this or atomics with lots of allocation - setCurrent(terminalValue); - } - } + setCurrent(terminalValue); - return a; + return subscribers.getAndSet(TERMINATED); } void setCurrent(Object o) { diff --git a/src/main/java/io/reactivex/rxjava3/processors/MulticastProcessor.java b/src/main/java/io/reactivex/rxjava3/processors/MulticastProcessor.java index 7ef61d7959..c1e47500a1 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/MulticastProcessor.java +++ b/src/main/java/io/reactivex/rxjava3/processors/MulticastProcessor.java @@ -23,7 +23,7 @@ import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.queue.*; import io.reactivex.rxjava3.internal.subscriptions.*; -import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -138,8 +138,6 @@ public final class MulticastProcessor extends FlowableProcessor { final AtomicReference[]> subscribers; - final AtomicBoolean once; - final int bufferSize; final int limit; @@ -233,7 +231,6 @@ public static MulticastProcessor create(int bufferSize, boolean refCount) this.subscribers = new AtomicReference<>(EMPTY); this.upstream = new AtomicReference<>(); this.refcount = refCount; - this.once = new AtomicBoolean(); } /** @@ -292,7 +289,7 @@ public void onSubscribe(@NonNull Subscription s) { @Override public void onNext(@NonNull T t) { - if (once.get()) { + if (done) { return; } if (fusionMode == QueueSubscription.NONE) { @@ -317,7 +314,7 @@ public void onNext(@NonNull T t) { @CheckReturnValue public boolean offer(@NonNull T t) { ExceptionHelper.nullCheck(t, "offer called with a null value."); - if (once.get()) { + if (done) { return false; } if (fusionMode == QueueSubscription.NONE) { @@ -333,21 +330,19 @@ public boolean offer(@NonNull T t) { @Override public void onError(@NonNull Throwable t) { ExceptionHelper.nullCheck(t, "onError called with a null Throwable."); - if (once.compareAndSet(false, true)) { + if (!done) { error = t; done = true; drain(); - } else { - RxJavaPlugins.onError(t); + return; } + RxJavaPlugins.onError(t); } @Override public void onComplete() { - if (once.compareAndSet(false, true)) { - done = true; - drain(); - } + done = true; + drain(); } @Override @@ -359,19 +354,19 @@ public boolean hasSubscribers() { @Override @CheckReturnValue public boolean hasThrowable() { - return once.get() && error != null; + return done && error != null; } @Override @CheckReturnValue public boolean hasComplete() { - return once.get() && error == null; + return done && error == null; } @Override @CheckReturnValue public Throwable getThrowable() { - return once.get() ? error : null; + return done ? error : null; } @Override @@ -385,7 +380,7 @@ protected void subscribeActual(@NonNull Subscriber<@NonNull ? super T> s) { drain(); } } else { - if (once.get() || !refcount) { + if (done) { Throwable ex = error; if (ex != null) { s.onError(ex); @@ -438,7 +433,7 @@ void remove(MulticastSubscription inner) { if (refcount) { if (subscribers.compareAndSet(a, TERMINATED)) { SubscriptionHelper.cancel(upstream); - once.set(true); + done = true; break; } } else { @@ -607,19 +602,9 @@ static final class MulticastSubscription extends AtomicLong implements Subscr @Override public void request(long n) { if (SubscriptionHelper.validate(n)) { - for (;;) { - long r = get(); - if (r == Long.MIN_VALUE || r == Long.MAX_VALUE) { - break; - } - long u = r + n; - if (u < 0L) { - u = Long.MAX_VALUE; - } - if (compareAndSet(r, u)) { - parent.drain(); - break; - } + long r = BackpressureHelper.addCancel(this, n); + if (r != Long.MIN_VALUE && r != Long.MAX_VALUE) { + parent.drain(); } } } diff --git a/src/main/java/io/reactivex/rxjava3/processors/ReplayProcessor.java b/src/main/java/io/reactivex/rxjava3/processors/ReplayProcessor.java index aa3402ec13..1c92e9d0ef 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/ReplayProcessor.java +++ b/src/main/java/io/reactivex/rxjava3/processors/ReplayProcessor.java @@ -1101,10 +1101,6 @@ void trim() { break; } TimedNode next = h.get(); - if (next == null) { - head = h; - break; - } if (next.time > limit) { head = h; diff --git a/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java b/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java index abc51887f8..61cf92cba3 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java @@ -151,7 +151,7 @@ public final class BehaviorSubject extends Subject { final AtomicReference value; - final AtomicReference[]> subscribers; + final AtomicReference[]> observers; @SuppressWarnings("rawtypes") static final BehaviorDisposable[] EMPTY = new BehaviorDisposable[0]; @@ -208,7 +208,7 @@ public static BehaviorSubject create() { this.lock = new ReentrantReadWriteLock(); this.readLock = lock.readLock(); this.writeLock = lock.writeLock(); - this.subscribers = new AtomicReference<>(EMPTY); + this.observers = new AtomicReference<>(EMPTY); this.value = new AtomicReference<>(defaultValue); this.terminalEvent = new AtomicReference<>(); } @@ -249,7 +249,7 @@ public void onNext(T t) { } Object o = NotificationLite.next(t); setCurrent(o); - for (BehaviorDisposable bs : subscribers.get()) { + for (BehaviorDisposable bs : observers.get()) { bs.emitNext(o, index); } } @@ -281,12 +281,12 @@ public void onComplete() { @Override @CheckReturnValue public boolean hasObservers() { - return subscribers.get().length != 0; + return observers.get().length != 0; } @CheckReturnValue /* test support*/ int subscriberCount() { - return subscribers.get().length; + return observers.get().length; } @Override @@ -342,7 +342,7 @@ public boolean hasValue() { boolean add(BehaviorDisposable rs) { for (;;) { - BehaviorDisposable[] a = subscribers.get(); + BehaviorDisposable[] a = observers.get(); if (a == TERMINATED) { return false; } @@ -351,7 +351,7 @@ boolean add(BehaviorDisposable rs) { BehaviorDisposable[] b = new BehaviorDisposable[len + 1]; System.arraycopy(a, 0, b, 0, len); b[len] = rs; - if (subscribers.compareAndSet(a, b)) { + if (observers.compareAndSet(a, b)) { return true; } } @@ -360,7 +360,7 @@ boolean add(BehaviorDisposable rs) { @SuppressWarnings("unchecked") void remove(BehaviorDisposable rs) { for (;;) { - BehaviorDisposable[] a = subscribers.get(); + BehaviorDisposable[] a = observers.get(); int len = a.length; if (len == 0) { return; @@ -384,7 +384,7 @@ void remove(BehaviorDisposable rs) { System.arraycopy(a, 0, b, 0, j); System.arraycopy(a, j + 1, b, j, len - j - 1); } - if (subscribers.compareAndSet(a, b)) { + if (observers.compareAndSet(a, b)) { return; } } @@ -393,13 +393,9 @@ void remove(BehaviorDisposable rs) { @SuppressWarnings("unchecked") BehaviorDisposable[] terminate(Object terminalValue) { - BehaviorDisposable[] a = subscribers.getAndSet(TERMINATED); - if (a != TERMINATED) { - // either this or atomics with lots of allocation - setCurrent(terminalValue); - } + setCurrent(terminalValue); - return a; + return observers.getAndSet(TERMINATED); } void setCurrent(Object o) { diff --git a/src/main/java/io/reactivex/rxjava3/subjects/ReplaySubject.java b/src/main/java/io/reactivex/rxjava3/subjects/ReplaySubject.java index a956035c3d..dfa3032ad3 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/ReplaySubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/ReplaySubject.java @@ -335,15 +335,13 @@ protected void subscribeActual(Observer observer) { ReplayDisposable rs = new ReplayDisposable<>(observer, this); observer.onSubscribe(rs); - if (!rs.cancelled) { - if (add(rs)) { - if (rs.cancelled) { - remove(rs); - return; - } + if (add(rs)) { + if (rs.cancelled) { + remove(rs); + return; } - buffer.replay(rs); } + buffer.replay(rs); } @Override @@ -571,10 +569,8 @@ void remove(ReplayDisposable rs) { @SuppressWarnings("unchecked") ReplayDisposable[] terminate(Object terminalValue) { - if (buffer.compareAndSet(null, terminalValue)) { - return observers.getAndSet(TERMINATED); - } - return TERMINATED; + buffer.compareAndSet(null, terminalValue); + return observers.getAndSet(TERMINATED); } /** @@ -1101,10 +1097,6 @@ void trim() { break; } TimedNode next = h.get(); - if (next == null) { - head = h; - break; - } if (next.time > limit) { head = h; @@ -1284,11 +1276,6 @@ public void replay(ReplayDisposable rs) { for (;;) { - if (rs.cancelled) { - rs.index = null; - return; - } - for (;;) { if (rs.cancelled) { rs.index = null; @@ -1322,10 +1309,6 @@ public void replay(ReplayDisposable rs) { index = n; } - if (index.get() != null) { - continue; - } - rs.index = index; missed = rs.addAndGet(-missed); diff --git a/src/main/java/io/reactivex/rxjava3/subscribers/TestSubscriber.java b/src/main/java/io/reactivex/rxjava3/subscribers/TestSubscriber.java index cbc9d10320..b3a67a89fe 100644 --- a/src/main/java/io/reactivex/rxjava3/subscribers/TestSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/subscribers/TestSubscriber.java @@ -179,15 +179,16 @@ public void onError(@NonNull Throwable t) { if (!checkSubscriptionOnce) { checkSubscriptionOnce = true; if (upstream.get() == null) { - errors.add(new NullPointerException("onSubscribe not called in proper order")); + errors.add(new IllegalStateException("onSubscribe not called in proper order")); } } try { lastThread = Thread.currentThread(); - errors.add(t); if (t == null) { - errors.add(new IllegalStateException("onError received a null Throwable")); + errors.add(new NullPointerException("onError received a null Throwable")); + } else { + errors.add(t); } downstream.onError(t); diff --git a/src/test/java/io/reactivex/rxjava3/core/NotificationTest.java b/src/test/java/io/reactivex/rxjava3/core/NotificationTest.java index 1c1a6adf59..1cebd84f0d 100644 --- a/src/test/java/io/reactivex/rxjava3/core/NotificationTest.java +++ b/src/test/java/io/reactivex/rxjava3/core/NotificationTest.java @@ -42,10 +42,21 @@ public void valueOfOnCompleteIsNull() { public void notEqualsToObject() { Notification n1 = Notification.createOnNext(0); assertNotEquals(0, n1); + assertNotEquals(n1, 0); Notification n2 = Notification.createOnError(new TestException()); assertNotEquals(0, n2); + assertNotEquals(n2, 0); Notification n3 = Notification.createOnComplete(); assertNotEquals(0, n3); + assertNotEquals(n3, 0); + } + + @Test + public void twoEqual() { + Notification n1 = Notification.createOnNext(0); + Notification n2 = Notification.createOnNext(0); + assertEquals(n1, n2); + assertEquals(n2, n1); } @Test diff --git a/src/test/java/io/reactivex/rxjava3/internal/functions/FunctionsTest.java b/src/test/java/io/reactivex/rxjava3/internal/functions/FunctionsTest.java index 72e188fe99..d919e8dfc8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/functions/FunctionsTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/functions/FunctionsTest.java @@ -39,12 +39,12 @@ public void utilityClass() { public void hashSetCallableEnum() { // inlined TestHelper.checkEnum due to access restrictions try { - Method m = Functions.HashSetCallable.class.getMethod("values"); + Method m = Functions.HashSetSupplier.class.getMethod("values"); m.setAccessible(true); - Method e = Functions.HashSetCallable.class.getMethod("valueOf", String.class); + Method e = Functions.HashSetSupplier.class.getMethod("valueOf", String.class); e.setAccessible(true); - for (Enum o : (Enum[])m.invoke(null)) { + for (Enum o : (Enum[])m.invoke(null)) { assertSame(o, e.invoke(null, o.name())); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/fuseable/CancellableQueueFuseableTest.java b/src/test/java/io/reactivex/rxjava3/internal/fuseable/CancellableQueueFuseableTest.java index e87bfd315a..abc3332381 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/fuseable/CancellableQueueFuseableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/fuseable/CancellableQueueFuseableTest.java @@ -25,6 +25,16 @@ public void offer() { TestHelper.assertNoOffer(new CancellableQueueFuseable<>()); } + @Test + public void pollClear() throws Throwable { + CancellableQueueFuseable qs = new CancellableQueueFuseable<>(); + + assertNull(qs.poll()); + + qs.clear(); + assertNull(qs.poll()); + } + @Test public void cancel() { CancellableQueueFuseable qs = new CancellableQueueFuseable<>(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/DeferredScalarDisposableTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/DeferredScalarDisposableTest.java new file mode 100644 index 0000000000..5604005992 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/DeferredScalarDisposableTest.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.observers.TestObserver; + +public class DeferredScalarDisposableTest extends RxJavaTest { + + @Test + public void tryDispose() { + TestObserver to = new TestObserver<>(); + + DeferredScalarDisposable d = new DeferredScalarDisposable<>(to); + to.onSubscribe(d); + + assertTrue(d.tryDispose()); + assertFalse(d.tryDispose()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/FutureMultiObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/FutureMultiObserverTest.java new file mode 100644 index 0000000000..69b4524123 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/FutureMultiObserverTest.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; +import org.junit.Test; + +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.disposables.Disposable; + +public class FutureMultiObserverTest extends RxJavaTest { + + @Test + public void cancelBeforeOnSubscribe() { + FutureMultiObserver f = new FutureMultiObserver<>(); + + assertTrue(f.cancel(true)); + + Disposable d = Disposable.empty(); + + f.onSubscribe(d); + + assertTrue(d.isDisposed()); + } + + @Test + public void onCompleteJustAfterDispose() { + FutureMultiObserver f = new FutureMultiObserver<>(); + Disposable d = Disposable.empty(); + f.onSubscribe(d); + assertTrue(f.cancel(true)); + + f.onComplete(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/FutureObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/FutureObserverTest.java index a4a8f353c2..e74efba8bd 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/FutureObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/FutureObserverTest.java @@ -22,11 +22,9 @@ import org.junit.*; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.subscribers.FutureSubscriber; -import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -157,7 +155,7 @@ public void onSubscribe() throws Exception { @Test public void cancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final FutureSubscriber fo = new FutureSubscriber<>(); + final FutureObserver fo = new FutureObserver<>(); Runnable r = new Runnable() { @Override @@ -188,7 +186,7 @@ public void onErrorCancelRace() { RxJavaPlugins.setErrorHandler(Functions.emptyConsumer()); try { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final FutureSubscriber fo = new FutureSubscriber<>(); + final FutureObserver fo = new FutureObserver<>(); final TestException ex = new TestException(); @@ -218,10 +216,10 @@ public void onCompleteCancelRace() { RxJavaPlugins.setErrorHandler(Functions.emptyConsumer()); try { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final FutureSubscriber fo = new FutureSubscriber<>(); + final FutureObserver fo = new FutureObserver<>(); if (i % 3 == 0) { - fo.onSubscribe(new BooleanSubscription()); + fo.onSubscribe(Disposable.empty()); } if (i % 2 == 0) { @@ -288,6 +286,22 @@ public void onCompleteOnError() throws Exception { } } + @Test + public void onNextCompleteOnError() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + fo.onNext(1); + fo.onComplete(); + fo.onError(new TestException("One")); + + assertEquals((Integer)1, fo.get(5, TimeUnit.MILLISECONDS)); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } finally { + RxJavaPlugins.reset(); + } + } + @Test public void cancelOnError() throws Exception { List errors = TestHelper.trackPluginErrors(); @@ -364,4 +378,22 @@ public void getTimedOut() throws Exception { assertEquals(timeoutMessage(1, TimeUnit.NANOSECONDS), expected.getMessage()); } } + + @Test + public void cancelOnSubscribeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + final FutureObserver fo = new FutureObserver<>(); + + Runnable r = new Runnable() { + @Override + public void run() { + fo.cancel(false); + } + }; + + Disposable d = Disposable.empty(); + + TestHelper.race(r, () -> fo.onSubscribe(d)); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserverTest.java new file mode 100644 index 0000000000..84b9d14360 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserverTest.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.observers; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class InnerQueuedObserverTest extends RxJavaTest { + + @Test + public void dispose() { + TestHelper.checkDisposed(new InnerQueuedObserver<>(null, 1)); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableabTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableTest.java similarity index 96% rename from src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableabTest.java rename to src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableTest.java index 051dd4e364..f32196364d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableabTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableTest.java @@ -27,7 +27,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.TestHelper; -public class CompletableAndThenCompletableabTest extends RxJavaTest { +public class CompletableAndThenCompletableTest extends RxJavaTest { @Test public void andThenCompletableCompleteComplete() { Completable.complete() @@ -177,4 +177,9 @@ public void run() throws Exception { assertFalse("The second Completable was interrupted!", interrupted[0]); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeCompletable(c -> c.andThen(c)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatTest.java index b41086ce3b..aaf925df78 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatTest.java @@ -300,4 +300,9 @@ public void run() throws Exception { assertFalse("The second Completable was interrupted!", interrupted[0]); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowableToCompletable(f -> Completable.concat(f)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOnTest.java index 32b5a85aa9..3ef469b121 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOnTest.java @@ -25,7 +25,7 @@ import io.reactivex.rxjava3.functions.Action; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.schedulers.TestScheduler; +import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subjects.PublishSubject; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -138,4 +138,9 @@ public void run() throws Exception { assertEquals(0, call[0]); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeCompletable(c -> c.unsubscribeOn(Schedulers.computation())); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromActionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromActionTest.java index 1f4df16f44..7c979d4881 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromActionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromActionTest.java @@ -23,6 +23,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Action; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.testsupport.TestHelper; public class CompletableFromActionTest extends RxJavaTest { @Test @@ -138,4 +140,31 @@ public void disposedUpfront() throws Throwable { verify(run, never()).run(); } + + @Test + public void disposeWhileRunningComplete() { + TestObserver to = new TestObserver<>(); + + Completable.fromAction(() -> { + to.dispose(); + }) + .subscribeWith(to) + .assertEmpty(); + } + + @Test + public void disposeWhileRunningError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestObserver to = new TestObserver<>(); + + Completable.fromAction(() -> { + to.dispose(); + throw new TestException(); + }) + .subscribeWith(to) + .assertEmpty(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnableTest.java index 2559f09187..a1a7298e03 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnableTest.java @@ -22,6 +22,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.testsupport.TestHelper; public class CompletableFromRunnableTest extends RxJavaTest { @Test @@ -137,4 +139,31 @@ public void disposedUpfront() throws Throwable { verify(run, never()).run(); } + + @Test + public void disposeWhileRunningComplete() { + TestObserver to = new TestObserver<>(); + + Completable.fromRunnable(() -> { + to.dispose(); + }) + .subscribeWith(to) + .assertEmpty(); + } + + @Test + public void disposeWhileRunningError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestObserver to = new TestObserver<>(); + + Completable.fromRunnable(() -> { + to.dispose(); + throw new TestException(); + }) + .subscribeWith(to) + .assertEmpty(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterableTest.java index bacfc8521a..1f392a9f7c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterableTest.java @@ -14,11 +14,14 @@ package io.reactivex.rxjava3.internal.operators.completable; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.internal.operators.completable.CompletableMergeIterable.MergeCompletableObserver; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.subjects.PublishSubject; @@ -124,4 +127,9 @@ public void remove() { to.assertEmpty(); } + + @Test + public void dispose() { + TestHelper.checkDisposed(new MergeCompletableObserver(new TestObserver(), new CompositeDisposable(), new AtomicInteger())); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeTest.java index 3482a53409..80f43a7a35 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeTest.java @@ -16,6 +16,7 @@ import static org.junit.Assert.*; import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.reactivestreams.Subscriber; @@ -25,9 +26,11 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.internal.util.AtomicThrowable; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subjects.CompletableSubject; import io.reactivex.rxjava3.testsupport.*; public class CompletableMergeTest extends RxJavaTest { @@ -593,4 +596,33 @@ public Completable apply(Flowable upstream) { } }); } + + @Test + public void iterableCompleteLater() { + CompletableSubject cs = CompletableSubject.create(); + + TestObserver to = Completable.mergeDelayError(Arrays.asList(cs, cs, cs)) + .test(); + + to.assertEmpty(); + + cs.onComplete(); + + to.assertResult(); + } + + @Test + public void terminalDisposed() { + TestHelper.checkDisposed(new CompletableMergeArrayDelayError.TryTerminateAndReportDisposable(new AtomicThrowable())); + } + + @Test + public void innerDisposed() { + TestHelper.checkDisposed(new CompletableMergeArray.InnerCompletableObserver(new TestObserver(), new AtomicBoolean(), new CompositeDisposable(), 1)); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowableToCompletable(f -> Completable.merge(f)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmbTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmbTest.java index 0657a15474..bae0033d30 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmbTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmbTest.java @@ -25,6 +25,7 @@ import org.mockito.InOrder; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.exceptions.TestException; @@ -660,4 +661,34 @@ public void subscribe(Subscriber subscriber) { .test() .assertResult(1); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.amb(Arrays.asList(Flowable.never(), Flowable.never()))); + } + + @Test + public void requestAfterCancel() { + Flowable.amb(Arrays.asList(Flowable.never(), Flowable.never())) + .subscribe(new FlowableSubscriber() { + + @Override + public void onNext(@NonNull Object t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + public void onSubscribe(@NonNull Subscription s) { + s.cancel(); + s.request(1); + } + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTest.java index 87a0eeaf38..5abdd7bf9d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTest.java @@ -34,7 +34,7 @@ import io.reactivex.rxjava3.internal.operators.flowable.FlowableBufferTimed.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subscribers.*; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -2388,4 +2388,112 @@ public List get() throws Exception { .assertFailure(TestException.class) ; } + + @Test + public void exactBadRequest() { + TestHelper.assertBadRequestReported(Flowable.never().buffer(1)); + } + + @Test + public void skipBadRequest() { + TestHelper.assertBadRequestReported(Flowable.never().buffer(1, 2)); + } + + @Test + public void overlapBadRequest() { + TestHelper.assertBadRequestReported(Flowable.never().buffer(2, 1)); + } + + @Test + public void bufferExactBoundedOnNextAfterDispose() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.unsafeCreate(s -> { + s.onSubscribe(new BooleanSubscription()); + ts.cancel(); + s.onNext(1); + }) + .buffer(1, TimeUnit.MINUTES, 2) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void boundaryCloseCompleteRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + BehaviorProcessor bp = BehaviorProcessor.createDefault(1); + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = bp + .buffer(BehaviorProcessor.createDefault(0), v -> pp) + .test(); + + TestHelper.race( + () -> bp.onComplete(), + () -> pp.onComplete() + ); + + ts.assertResult(Arrays.asList(1)); + } + } + + @Test + public void doubleOnSubscribeStartEnd() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.buffer(Flowable.never(), v -> Flowable.never())); + } + + @Test + public void cancel() { + TestHelper.checkDisposed(Flowable.never().buffer(Flowable.never(), v -> Flowable.never())); + } + + @Test + public void startEndCancelAfterOneBuffer() { + BehaviorProcessor.createDefault(1) + .buffer(BehaviorProcessor.createDefault(2), v -> Flowable.just(1)) + .takeUntil(v -> true) + .test() + .assertResult(Arrays.asList()); + } + + @Test + public void startEndCompleteOnBoundary() { + Flowable.empty() + .buffer(Flowable.never(), v -> Flowable.just(1)) + .take(1) + .test() + .assertResult(); + } + + @Test + public void startEndBackpressure() { + BehaviorProcessor.createDefault(1) + .buffer(BehaviorProcessor.createDefault(2), v -> Flowable.just(1)) + .test(1L) + .assertValuesOnly(Arrays.asList()); + } + + @Test + public void startEndBackpressureMoreWork() { + PublishProcessor bp = PublishProcessor.create(); + PublishProcessor pp = PublishProcessor.create(); + AtomicInteger counter = new AtomicInteger(); + + TestSubscriber> ts = bp + .buffer(pp, v -> Flowable.just(1)) + .doOnNext(v -> { + if (counter.getAndIncrement() == 0) { + pp.onNext(2); + pp.onComplete(); + } + }) + .test(1L); + + pp.onNext(1); + bp.onNext(1); + + ts + .assertValuesOnly(Arrays.asList()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCacheTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCacheTest.java index a317055889..b736b48122 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCacheTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCacheTest.java @@ -511,4 +511,18 @@ public void backpressure() { .requestMore(3) .assertResult(1, 2, 3, 4, 5); } + + @Test + public void addRemoveRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + Flowable f = Flowable.never().cache(); + + TestSubscriber ts = f.test(); + + TestHelper.race( + () -> ts.cancel(), + () -> f.test() + ); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatestTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatestTest.java index 302ae3757b..06b29eadbe 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatestTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatestTest.java @@ -32,6 +32,7 @@ import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.operators.flowable.FlowableZipTest.ArgsToString; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.*; @@ -1394,7 +1395,7 @@ public Object apply(Object[] a) throws Exception { public void combine2Flowable2Errors() throws Exception { List errors = TestHelper.trackPluginErrors(); try { - TestSubscriber testObserver = TestSubscriber.create(); + TestSubscriber testSubscriber = TestSubscriber.create(); TestScheduler testScheduler = new TestScheduler(); @@ -1459,11 +1460,11 @@ public void run() throws Exception { System.out.println("combineLatestDelayError: doFinally"); } }) - .subscribe(testObserver); + .subscribe(testSubscriber); testScheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); - testObserver.awaitDone(5, TimeUnit.SECONDS); + testSubscriber.awaitDone(5, TimeUnit.SECONDS); assertTrue(errors.toString(), errors.isEmpty()); } finally { @@ -1558,4 +1559,230 @@ public Integer apply(Object[] t) throws Throwable { .test() .assertResult(2); } + + @Test + public void FlowableSourcesInIterable() { + Flowable source = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + Flowable.just(1).subscribe(s); + } + }; + + Flowable.combineLatest(Arrays.asList(source, source), new Function() { + @Override + public Integer apply(Object[] t) throws Throwable { + return 2; + } + }) + .test() + .assertResult(2); + } + + @Test + public void onCompleteDisposeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + TestSubscriber ts = new TestSubscriber<>(); + PublishProcessor pp = PublishProcessor.create(); + + Flowable.combineLatest(pp, Flowable.never(), (a, b) -> a) + .subscribe(ts); + + TestHelper.race(() -> pp.onComplete(), () -> ts.cancel()); + } + } + + @Test + public void onErrorDisposeDelayErrorRace() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestException ex = new TestException(); + + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + TestSubscriberEx ts = new TestSubscriberEx<>(); + AtomicReference> ref = new AtomicReference<>(); + Flowable f = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + ref.set(s); + } + }; + + Flowable.combineLatestDelayError(Arrays.asList(f, Flowable.never()), (a) -> a) + .subscribe(ts); + + ref.get().onSubscribe(new BooleanSubscription()); + + TestHelper.race(() -> ref.get().onError(ex), () -> ts.cancel()); + + if (ts.errors().isEmpty()) { + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } + } + }); + } + + @Test + public void doneButNotEmpty() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + TestSubscriber ts = Flowable.combineLatest(pp1, pp2, (a, b) -> a + b) + .doOnNext(v -> { + if (v == 2) { + pp2.onNext(3); + pp2.onComplete(); + pp1.onComplete(); + } + }) + .test(); + + pp1.onNext(1); + pp2.onNext(1); + + ts.assertResult(2, 4); + } + + @Test + public void iterableNullPublisher() { + Flowable.combineLatest(Arrays.asList(Flowable.never(), null), (a) -> a) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.combineLatest(Flowable.never(), Flowable.never(), (a, b) -> a)); + } + + @Test + public void syncFusionRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.SYNC); + + Flowable.combineLatest(Flowable.never(), Flowable.never(), (a, b) -> a) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + } + + @Test + public void bounderyFusionRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY | QueueFuseable.BOUNDARY); + + Flowable.combineLatest(Flowable.never(), Flowable.never(), (a, b) -> a) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + } + + @Test + public void fusedNormal() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Flowable.combineLatest(Flowable.just(1), Flowable.just(2), (a, b) -> a + b) + .subscribeWith(ts) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(3); + } + + @Test + public void fusedToParallel() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Flowable.combineLatest(Flowable.just(1), Flowable.just(2), (a, b) -> a + b) + .parallel() + .sequential() + .subscribeWith(ts) + .assertResult(3); + } + + @Test + public void fusedToParallel2() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Flowable.combineLatest(Flowable.just(1), Flowable.just(2), (a, b) -> a + b) + .compose(TestHelper.flowableStripBoundary()) + .parallel() + .sequential() + .subscribeWith(ts) + .assertResult(3); + } + + @Test + public void fusedError() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Flowable.combineLatest(Flowable.just(1), Flowable.error(new TestException()), (a, b) -> a + b) + .subscribeWith(ts) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertFailure(TestException.class); + } + + @Test + public void nonFusedMoreWorkBeforeTermination() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = Flowable.combineLatest(pp, Flowable.just(1), (a, b) -> a + b) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + pp.onComplete(); + } + }) + .test(); + + pp.onNext(0); + + ts.assertResult(1, 3); + } + + @Test + public void nonFusedDelayErrorMoreWorkBeforeTermination() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = Flowable.combineLatestDelayError(Arrays.asList(pp, Flowable.just(1)), a -> Arrays.asList(a)) + .doOnNext(v -> { + if (((Integer)v.get(0)) == 0) { + pp.onNext(2); + pp.onComplete(); + } + }) + .test(); + + pp.onNext(0); + + ts.assertResult(Arrays.asList(0, 1), Arrays.asList(2, 1)); + } + + @Test + public void fusedCombinerCrashError() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Flowable.combineLatest(Flowable.just(1), Flowable.just(1), (a, b) -> { throw new TestException(); }) + .subscribeWith(ts) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertFailure(TestException.class); + } + + @Test + public void fusedCombinerCrashError2() { + Flowable.combineLatest(Flowable.just(1), Flowable.just(1), (a, b) -> { throw new TestException(); }) + .compose(TestHelper.flowableStripBoundary()) + .rebatchRequests(10) + .test() + .assertFailure(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerTest.java index b8c2eaaff3..7d6392b318 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerTest.java @@ -1395,4 +1395,40 @@ public void publisherDelayErrorMaxConcurrency() { .test() .assertFailure(TestException.class, 1, 2, 3, 4, 5); } + + @Test + public void innerSyncFused() { + Flowable.just(1) + .hide() + .concatMapEagerDelayError(v -> Flowable.range(1, 10), true, 1, 1) + .test() + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().concatMapEagerDelayError(v -> Flowable.never(), false)); + } + + @Test + public void cancelAfterOnNext() { + Flowable.just(1) + .hide() + .concatMapEagerDelayError(v -> Flowable.range(1, 5).hide(), true) + .takeUntil(v -> true) + .test() + .assertResult(1); + } + + @Test + public void noInnerQueue() { + Flowable.just(1) + .hide() + .concatMapEagerDelayError(v -> Flowable.fromPublisher(s -> { }), true) + .test(0L) + .assertEmpty() + .requestMore(1L) + .assertEmpty() + ; + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java index 0cd7562199..bb01b50a20 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java @@ -18,17 +18,18 @@ import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; import org.junit.Test; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; -import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -1088,4 +1089,141 @@ public Publisher apply(Integer v) throws Throwable { } }); } + + @Test + public void fusionRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + TestHelper.rejectFlowableFusion() + .concatMap(v -> Flowable.never(), 2, ImmediateThinScheduler.INSTANCE) + .subscribe(ts); + } + + @Test + public void fusionRejectedDelayErrorr() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + TestHelper.rejectFlowableFusion() + .concatMapDelayError(v -> Flowable.never(), true, 2, ImmediateThinScheduler.INSTANCE) + .subscribe(ts); + } + + @Test + public void scalarInnerJustDispose() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .hide() + .concatMap(v -> Flowable.fromCallable(() -> { + ts.cancel(); + return 1; + }), 2, ImmediateThinScheduler.INSTANCE) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void scalarInnerJustDisposeDelayError() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .hide() + .concatMapDelayError(v -> Flowable.fromCallable(() -> { + ts.cancel(); + return 1; + }), true, 2, ImmediateThinScheduler.INSTANCE) + .subscribe(ts); + + ts.assertEmpty(); + } + + static final class EmptyDisposingFlowable extends Flowable + implements Supplier { + final TestSubscriber ts; + EmptyDisposingFlowable(TestSubscriber ts) { + this.ts = ts; + } + + @Override + protected void subscribeActual(@NonNull Subscriber subscriber) { + EmptySubscription.complete(subscriber); + } + + @Override + public @NonNull Object get() throws Throwable { + ts.cancel(); + return null; + } + } + + @Test + public void scalarInnerEmptyDisposeDelayError() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .hide() + .concatMapDelayError(v -> new EmptyDisposingFlowable(ts), + true, 2, ImmediateThinScheduler.INSTANCE + ) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void mainErrorInnerNextIgnoreCancel() { + AtomicReference> ref = new AtomicReference<>(); + + Flowable.just(1).concatWith(Flowable.error(new TestException())) + .concatMap(v -> Flowable.fromPublisher(ref::set), 2, ImmediateThinScheduler.INSTANCE) + .doOnError(e -> { + ref.get().onSubscribe(new BooleanSubscription()); + ref.get().onNext(1); + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void scalarSupplierMainError() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.concatMap(v -> Flowable.fromCallable(() -> { + pp.onError(new TestException()); + return 2; + }), 2, ImmediateThinScheduler.INSTANCE) + .test() + ; + + pp.onNext(1); + + ts.assertFailure(TestException.class); + } + + @Test + public void mainErrorInnerErrorRace() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestException ex1 = new TestException(); + TestException ex2 = new TestException(); + + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + TestSubscriber ts = Flowable.fromPublisher(ref1::set) + .concatMap(v -> Flowable.fromPublisher(ref2::set), 2, ImmediateThinScheduler.INSTANCE) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + ref2.get().onSubscribe(new BooleanSubscription()); + + TestHelper.race(() -> ref1.get().onError(ex1), () -> ref2.get().onError(ex2)); + + ts.assertError(RuntimeException.class); + errors.clear(); + } + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java index e815c3675e..4ff32a9eb4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java @@ -25,6 +25,7 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.operators.flowable.FlowableConcatMap.WeakScalarSubscription; +import io.reactivex.rxjava3.processors.UnicastProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -252,4 +253,23 @@ public Publisher apply(Integer v) throws Throwable { } }); } + + @Test + public void asyncFusedSource() { + UnicastProcessor up = UnicastProcessor.create(); + up.onNext(1); + up.onComplete(); + + up.concatMap(v -> Flowable.just(1).hide()) + .test() + .assertResult(1); + } + + @Test + public void scalarCallableSource() { + Flowable.fromCallable(() -> 1) + .concatMap(v -> Flowable.just(1)) + .test() + .assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java index 208f7b1b51..282179b9fe 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java @@ -17,6 +17,7 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.io.IOException; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.*; @@ -27,7 +28,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -1644,4 +1645,28 @@ public void run() throws Exception { assertEquals(0, counter.get()); } + + @Test + public void arrayDelayErrorMultipleErrors() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + Flowable.concatArrayDelayError(Flowable.error(new IOException()), Flowable.error(new TestException())) + .subscribe(ts); + + ts.assertFailure(CompositeException.class); + + TestHelper.assertCompositeExceptions(ts, IOException.class, TestException.class); + } + + @Test + public void arrayDelayErrorMultipleNullErrors() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + Flowable.concatArrayDelayError(null, null) + .subscribe(ts); + + ts.assertFailure(CompositeException.class); + + TestHelper.assertCompositeExceptions(ts, NullPointerException.class, NullPointerException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreateTest.java index 38b05b0920..8ca745dc0b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreateTest.java @@ -17,16 +17,18 @@ import java.io.IOException; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Cancellable; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; public class FlowableCreateTest extends RxJavaTest { @@ -1063,4 +1065,66 @@ public void subscribe(FlowableEmitter emitter) throws Exception { }, entry.getKey()).test().assertEmpty(); } } + + @Test + public void serializedMissingMoreWorkWithComplete() { + AtomicReference> ref = new AtomicReference<>(); + + Flowable.create(emitter -> { + emitter = emitter.serialize(); + ref.set(emitter); + assertEquals(Long.MAX_VALUE, emitter.requested()); + emitter.onNext(1); + }, BackpressureStrategy.MISSING) + .doOnNext(v -> { + if (v == 1) { + ref.get().onNext(2); + ref.get().onComplete(); + } + }) + .test() + .assertResult(1, 2); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.create(e -> { }, BackpressureStrategy.BUFFER)); + } + + @Test + public void tryOnErrorNull() { + Flowable.create(emitter -> emitter.tryOnError(null), BackpressureStrategy.MISSING) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void serializedCompleteOnNext() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.create(emitter -> { + emitter = emitter.serialize(); + + emitter.onComplete(); + emitter.onNext(1); + }, BackpressureStrategy.MISSING) + .subscribe(ts); + + ts.assertResult(); + } + + @Test + public void serializedCancelOnNext() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.create(emitter -> { + emitter = emitter.serialize(); + + ts.cancel(); + emitter.onNext(1); + }, BackpressureStrategy.MISSING) + .subscribe(ts); + + ts.assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOtherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOtherTest.java index bb9faa4bca..fba8aedbac 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOtherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOtherTest.java @@ -343,4 +343,19 @@ public void subscribe(FlowableEmitter emitter) throws Exception { exec.shutdown(); } } + + @Test + public void doubleOnSubscribeMain() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.delaySubscription(Flowable.empty())); + } + + @Test + public void doubleOnSubscribeOther() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> PublishProcessor.create().delaySubscription(f)); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(PublishProcessor.create().delaySubscription(Flowable.empty())); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerializeTest.java index d4578db529..2571dc481e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerializeTest.java @@ -223,7 +223,7 @@ protected void subscribeActual(Subscriber> subscrib } @Test - public void nonNotificationInstanceAfterDispose() { + public void notificationInstanceAfterDispose() { new Flowable>() { @Override protected void subscribeActual(Subscriber> subscriber) { @@ -236,4 +236,20 @@ protected void subscribeActual(Subscriber> subscrib .test() .assertResult(); } + + @Test + @SuppressWarnings("unchecked") + public void nonNotificationInstanceAfterDispose() { + new Flowable() { + @Override + protected void subscribeActual(Subscriber subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + subscriber.onNext(Notification.createOnComplete()); + subscriber.onNext(1); + } + } + .dematerialize(v -> (Notification)v) + .test() + .assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinallyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinallyTest.java index 850c531203..3610593751 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinallyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinallyTest.java @@ -208,7 +208,7 @@ public void syncFusedConditional() { Flowable.range(1, 5) .doFinally(this) - .filter(Functions.alwaysTrue()) + .compose(TestHelper.conditional()) .subscribe(ts); ts.assertFusionMode(QueueFuseable.SYNC) @@ -237,7 +237,7 @@ public void nonFusedConditional() { Flowable.range(1, 5).hide() .doFinally(this) - .filter(Functions.alwaysTrue()) + .compose(TestHelper.conditional()) .subscribe(ts); ts.assertFusionMode(QueueFuseable.NONE) @@ -252,7 +252,7 @@ public void syncFusedBoundaryConditional() { Flowable.range(1, 5) .doFinally(this) - .filter(Functions.alwaysTrue()) + .compose(TestHelper.conditional()) .subscribe(ts); ts.assertFusionMode(QueueFuseable.NONE) @@ -270,7 +270,7 @@ public void asyncFusedConditional() { up .doFinally(this) - .filter(Functions.alwaysTrue()) + .compose(TestHelper.conditional()) .subscribe(ts); ts.assertFusionMode(QueueFuseable.ASYNC) @@ -288,7 +288,7 @@ public void asyncFusedBoundaryConditional() { up .doFinally(this) - .filter(Functions.alwaysTrue()) + .compose(TestHelper.conditional()) .subscribe(ts); ts.assertFusionMode(QueueFuseable.NONE) @@ -512,4 +512,31 @@ public void run() throws Exception { assertEquals(Arrays.asList("onNext", "onComplete", "finally"), list); } + + @Test + public void fusionRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + TestHelper.rejectFlowableFusion() + .doFinally(() -> { }) + .subscribeWith(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + } + + @Test + public void fusionRejectedConditional() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + TestHelper.rejectFlowableFusion() + .doFinally(() -> { }) + .compose(TestHelper.conditional()) + .subscribeWith(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableTest.java index 1c87d6f3b3..3d14dd0eb2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableTest.java @@ -16,15 +16,15 @@ import static org.junit.Assert.*; import java.util.List; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import org.junit.Test; import org.reactivestreams.Subscription; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.observers.TestObserver; @@ -510,6 +510,23 @@ protected void subscribeActual(CompletableObserver observer) { @Test public void delayErrorMaxConcurrency() { + Flowable.range(1, 3) + .flatMapCompletable(new Function() { + @Override + public CompletableSource apply(Integer v) throws Exception { + if (v == 2) { + return Completable.error(new TestException()); + } + return Completable.complete(); + } + }, true, 1) + .toFlowable() + .test() + .assertFailure(TestException.class); + } + + @Test + public void delayErrorMaxConcurrencyCompletable() { Flowable.range(1, 3) .flatMapCompletable(new Function() { @Override @@ -570,4 +587,59 @@ public Completable apply(Integer v) throws Throwable { } }); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.flatMapCompletable(v -> Completable.never()).toFlowable()); + } + + @Test + public void doubleOnSubscribeCompletable() { + TestHelper.checkDoubleOnSubscribeFlowableToCompletable(f -> f.flatMapCompletable(v -> Completable.never())); + } + + @Test + public void cancelWhileMapping() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor pp1 = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber<>(); + CountDownLatch cdl = new CountDownLatch(1); + + pp1.flatMapCompletable(v -> { + TestHelper.raceOther(() -> { + ts.cancel(); + }, cdl); + return Completable.complete(); + }) + .toFlowable() + .subscribe(ts); + + pp1.onNext(1); + + cdl.await(); + } + } + + @Test + public void cancelWhileMappingCompletable() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor pp1 = PublishProcessor.create(); + + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + + pp1.flatMapCompletable(v -> { + TestHelper.raceOther(() -> { + to.dispose(); + }, cdl); + return Completable.complete(); + }) + .subscribe(to); + + pp1.onNext(1); + + cdl.await(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybeTest.java index 8faad86fef..14a0418d15 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybeTest.java @@ -22,7 +22,7 @@ import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; @@ -30,6 +30,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -641,4 +642,57 @@ public Maybe apply(Integer v) throws Throwable { } }); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().flatMapMaybe(v -> Maybe.never())); + } + + @Test + public void successRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + MaybeSubject ss1 = MaybeSubject.create(); + MaybeSubject ss2 = MaybeSubject.create(); + + TestSubscriber ts = Flowable.just(ss1, ss2).flatMapMaybe(v -> v) + .test(); + + TestHelper.race( + () -> ss1.onSuccess(1), + () -> ss2.onSuccess(1) + ); + + ts.assertResult(1, 1); + } + } + + @Test + public void successCompleteRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + MaybeSubject ss1 = MaybeSubject.create(); + MaybeSubject ss2 = MaybeSubject.create(); + + TestSubscriber ts = Flowable.just(ss1, ss2).flatMapMaybe(v -> v) + .test(); + + TestHelper.race( + () -> ss1.onSuccess(1), + () -> ss2.onComplete() + ); + + ts.assertResult(1); + } + } + + @Test + public void successShortcut() { + MaybeSubject ss1 = MaybeSubject.create(); + + TestSubscriber ts = Flowable.just(ss1).hide().flatMapMaybe(v -> v) + .test(); + + ss1.onSuccess(1); + + ts.assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingleTest.java index 1a0cd3854f..cd34417ea9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingleTest.java @@ -22,7 +22,7 @@ import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; @@ -30,6 +30,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.SingleSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -541,4 +542,39 @@ public Single apply(Integer v) throws Throwable { } }); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().flatMapSingle(v -> Single.never())); + } + + @Test + public void successRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + SingleSubject ss1 = SingleSubject.create(); + SingleSubject ss2 = SingleSubject.create(); + + TestSubscriber ts = Flowable.just(ss1, ss2).flatMapSingle(v -> v) + .test(); + + TestHelper.race( + () -> ss1.onSuccess(1), + () -> ss2.onSuccess(1) + ); + + ts.assertResult(1, 1); + } + } + + @Test + public void successShortcut() { + SingleSubject ss1 = SingleSubject.create(); + + TestSubscriber ts = Flowable.just(ss1).hide().flatMapSingle(v -> v) + .test(); + + ss1.onSuccess(1); + + ts.assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java index 6df4dc7211..1687c2dd79 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java @@ -17,19 +17,22 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; +import java.io.IOException; import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; import org.junit.*; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -1150,4 +1153,329 @@ public void innerErrorsMainCancelled() { assertFalse("Has subscribers?", pp1.hasSubscribers()); } + + @Test + public void innerIsDisposed() { + FlowableFlatMap.InnerSubscriber inner = new FlowableFlatMap.InnerSubscriber<>(null, 10, 0L); + + assertFalse(inner.isDisposed()); + + inner.dispose(); + + assertTrue(inner.isDisposed()); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().flatMap(v -> Flowable.never())); + } + + @Test + public void signalsAfterMapperCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onNext(2); + s.onComplete(); + s.onError(new IOException()); + } + } + .flatMap(v -> { + throw new TestException(); + }) + .test() + .assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } + + @Test + public void scalarQueueTerminate() { + PublishProcessor pp = PublishProcessor.create(); + TestSubscriber ts = new TestSubscriber<>(); + + pp + .flatMap(v -> Flowable.just(v)) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + pp.onNext(3); + } + }) + .take(2) + .subscribe(ts); + + pp.onNext(1); + + ts.assertResult(1, 2); + } + + @Test + public void scalarQueueCompleteMain() throws Exception { + PublishProcessor pp = PublishProcessor.create(); + TestSubscriber ts = new TestSubscriber<>(); + CountDownLatch cdl = new CountDownLatch(1); + pp + .flatMap(v -> Flowable.just(v)) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + TestHelper.raceOther(() -> pp.onComplete(), cdl); + } + }) + .subscribe(ts); + + pp.onNext(1); + + cdl.await(); + ts.assertResult(1, 2); + } + + @Test + public void fusedInnerCrash() { + UnicastProcessor up = UnicastProcessor.create(); + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = Flowable.just( + pp, + up.map(v -> { + if (v == 10) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.flowableStripBoundary()) + ) + .flatMap(v -> v, true) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + up.onNext(10); + } + }) + .test(); + + pp.onNext(1); + pp.onComplete(); + + ts.assertFailure(TestException.class, 1, 2); + } + + @Test + public void fusedInnerCrash2() { + UnicastProcessor up = UnicastProcessor.create(); + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = Flowable.just( + up.map(v -> { + if (v == 10) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.flowableStripBoundary()) + , pp + ) + .flatMap(v -> v, true) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + up.onNext(10); + } + }) + .test(); + + pp.onNext(1); + pp.onComplete(); + + ts.assertFailure(TestException.class, 1, 2); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.flatMap(v -> Flowable.never())); + } + + @Test + public void allConcurrency() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.just(2).hide(), Integer.MAX_VALUE) + .test() + .assertResult(2); + } + + @Test + public void allConcurrencyScalarInner() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.just(2), Integer.MAX_VALUE) + .test() + .assertResult(2); + } + + @Test + public void allConcurrencyScalarInnerEmpty() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.empty(), Integer.MAX_VALUE) + .test() + .assertResult(); + } + + static final class ScalarEmptyCancel extends Flowable implements Supplier { + final TestSubscriber ts; + + ScalarEmptyCancel(TestSubscriber ts) { + this.ts = ts; + } + + @Override + public @NonNull Integer get() throws Throwable { + ts.cancel(); + return null; + } + + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super @NonNull Integer> subscriber) { + EmptySubscription.complete(subscriber); + } + } + + @Test + public void someConcurrencyScalarInnerCancel() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .hide() + .flatMap(v -> new ScalarEmptyCancel(ts)) + .subscribeWith(ts) + .assertEmpty(); + } + + @Test + public void allConcurrencyBackpressured() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.just(2), Integer.MAX_VALUE) + .test(0L) + .assertEmpty() + .requestMore(1) + .assertResult(2); + } + + @Test + public void someConcurrencyInnerScalarCancel() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.just(2), 2) + .takeUntil(v -> true) + .test() + .assertResult(2); + } + + @Test + public void scalarInnerOuterOverflow() { + new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super @NonNull Integer> subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + subscriber.onNext(1); + subscriber.onNext(2); + subscriber.onNext(3); + } + } + .flatMap(v -> Flowable.just(v), 1) + .test(0L) + .assertFailure(MissingBackpressureException.class); + } + + @Test + public void scalarInnerOuterOverflowSlowPath() { + AtomicReference> ref = new AtomicReference<>(); + new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super @NonNull Integer> subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + ref.set(subscriber); + subscriber.onNext(1); + } + } + .flatMap(v -> Flowable.just(v), 1) + .doOnNext(v -> { + if (v == 1) { + ref.get().onNext(2); + ref.get().onNext(3); + } + }) + .test() + .assertFailure(MissingBackpressureException.class, 1); + } + + @Test + public void innerFastPathEmitOverflow() { + Flowable.just(1) + .hide() + .flatMap(v -> new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super @NonNull Integer> subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + subscriber.onNext(1); + subscriber.onNext(2); + subscriber.onNext(3); + } + }, false, 1, 1) + .test(0L) + .assertFailure(MissingBackpressureException.class); + } + + @Test + public void takeFromScalarQueue() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.just(2), 2) + .takeUntil(v -> true) + .test(0L) + .requestMore(2) + .assertResult(2); + } + + @Test + public void scalarInnerQueueEmpty() { + Flowable.just(1) + .concatWith(Flowable.never()) + .hide() + .flatMap(v -> Flowable.just(2), 2) + .test(0L) + .requestMore(2) + .assertValuesOnly(2); + } + + @Test + public void innerCompletesAfterOnNextInDrainThenCancels() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber<>(0L); + + Flowable.just(1) + .hide() + .flatMap(v -> pp) + .doOnNext(v -> { + if (v == 1) { + pp.onComplete(); + ts.cancel(); + } + }) + .subscribe(ts); + + pp.onNext(1); + + ts + .requestMore(1) + .assertValuesOnly(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableForEachTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableForEachTest.java index d9ff63257c..0064f61c3f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableForEachTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableForEachTest.java @@ -22,6 +22,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.testsupport.TestHelper; public class FlowableForEachTest extends RxJavaTest { @@ -72,4 +73,11 @@ public void accept(Throwable e) throws Exception { assertEquals(Arrays.asList(1, 2, 3, 4, 5, 100), list); } + @Test + public void dispose() { + TestHelper.checkDisposed( + Flowable.never() + .forEachWhile(v -> true) + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCompletableTest.java index 47c0b815fb..5e05936816 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCompletableTest.java @@ -24,7 +24,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subscribers.TestSubscriber; @@ -193,4 +193,11 @@ public void syncFusedRejected() throws Throwable { verify(action).run(); } + + @Test + public void upstream() { + Flowable f = Flowable.fromCompletable(Completable.never()); + assertTrue(f instanceof HasUpstreamCompletableSource); + assertSame(Completable.never(), ((HasUpstreamCompletableSource)f).source()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterableTest.java index d49111c25b..1880d2be43 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterableTest.java @@ -19,12 +19,13 @@ import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.*; import org.junit.Test; import org.mockito.Mockito; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; @@ -962,4 +963,259 @@ public void remove() { .assertNoErrors() .assertNotComplete(); } + + @Test + public void hasNextCancelsAndCompletesFastPath() { + final TestSubscriber ts = new TestSubscriber<>(); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + int count; + + @Override + public boolean hasNext() { + if (++count == 2) { + ts.cancel(); + return false; + } + return true; + } + + @Override + public Integer next() { + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .subscribe(ts); + + ts.assertValue(1) + .assertNoErrors() + .assertNotComplete(); + } + + @Test + public void hasNextCancelsAndCompletesSlowPath() { + final TestSubscriber ts = new TestSubscriber<>(10L); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + int count; + + @Override + public boolean hasNext() { + if (++count == 2) { + ts.cancel(); + return false; + } + return true; + } + + @Override + public Integer next() { + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .subscribe(ts); + + ts.assertValue(1) + .assertNoErrors() + .assertNotComplete(); + } + + @Test + public void hasNextCancelsAndCompletesFastPathConditional() { + final TestSubscriber ts = new TestSubscriber<>(); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + int count; + + @Override + public boolean hasNext() { + if (++count == 2) { + ts.cancel(); + return false; + } + return true; + } + + @Override + public Integer next() { + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .filter(v -> true) + .subscribe(ts); + + ts.assertValue(1) + .assertNoErrors() + .assertNotComplete(); + } + + @Test + public void hasNextCancelsAndCompletesSlowPathConditional() { + final TestSubscriber ts = new TestSubscriber<>(10); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + int count; + + @Override + public boolean hasNext() { + if (++count == 2) { + ts.cancel(); + return false; + } + return true; + } + + @Override + public Integer next() { + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .filter(v -> true) + .subscribe(ts); + + ts.assertValue(1) + .assertNoErrors() + .assertNotComplete(); + } + + @Test + public void fusedPoll() throws Throwable { + AtomicReference> queue = new AtomicReference<>(); + + Flowable.fromIterable(Arrays.asList(1)) + .subscribe(new FlowableSubscriber() { + @Override + public void onSubscribe(@NonNull Subscription s) { + queue.set((SimpleQueue)s); + ((QueueSubscription)s).requestFusion(QueueFuseable.ANY); + } + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + }); + + SimpleQueue q = queue.get(); + + assertFalse(q.isEmpty()); + + assertEquals(1, q.poll()); + + assertTrue(q.isEmpty()); + + q.clear(); + + assertTrue(q.isEmpty()); + } + + @Test + public void disposeWhileIteratorNext() { + final TestSubscriber ts = new TestSubscriber<>(10); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + ts.cancel(); + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void disposeWhileIteratorNextConditional() { + final TestSubscriber ts = new TestSubscriber<>(10); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + ts.cancel(); + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .filter(v -> true) + .subscribe(ts); + + ts.assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerateTest.java index ecd965a4f8..23989c717b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerateTest.java @@ -282,4 +282,17 @@ public void accept(Emitter e) throws Exception { .test(1) .assertResult(); } + + @Test + public void onNextAfterOnComplete() { + Flowable.generate(new Consumer>() { + @Override + public void accept(Emitter e) throws Exception { + e.onComplete(); + e.onNext(1); + } + }) + .test() + .assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java index ddc34f1d98..21b7f8fb24 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java @@ -45,6 +45,15 @@ public class FlowableGroupByTest extends RxJavaTest { + static Function, Flowable> FLATTEN_INTEGER = new Function, Flowable>() { + + @Override + public Flowable apply(GroupedFlowable t) { + return t; + } + + }; + final Function length = new Function() { @Override public Integer apply(String s) { @@ -1350,15 +1359,6 @@ public String apply(Integer l) { ts.assertNoErrors(); } - static Function, Flowable> FLATTEN_INTEGER = new Function, Flowable>() { - - @Override - public Flowable apply(GroupedFlowable t) { - return t; - } - - }; - @Test public void groupByWithNullKey() { final String[] key = new String[]{"uninitialized"}; @@ -1805,10 +1805,21 @@ public Object apply(Flowable f) throws Exception { @Test public void badRequest() { - TestHelper.assertBadRequestReported(Flowable.just(1) + TestHelper.assertBadRequestReported(Flowable.just(1).hide() .groupBy(Functions.justFunction(1))); } + @Test + public void badRequestInner() { + Flowable.just(1).hide() + .groupBy(Functions.justFunction(1)) + .doOnNext(g -> { + TestHelper.assertBadRequestReported(g); + }) + .test() + .assertNoErrors(); + } + @Test public void doubleOnSubscribe() { TestHelper.checkDoubleOnSubscribeFlowable(new Function, Publisher>>() { @@ -1883,6 +1894,39 @@ public Map apply(final Consumer notify) throws Exceptio .assertNoValues() .assertError(ex); } + // ----------------------------------------------------------------------------------------------------------------------- + + private static final Function mod5 = new Function() { + + @Override + public Integer apply(Integer n) throws Exception { + return n % 5; + } + }; + + private static Function, Publisher> addCompletedKey( + final List completed) { + return new Function, Publisher>() { + @Override + public Publisher apply(final GroupedFlowable g) throws Exception { + return g.doOnComplete(new Action() { + @Override + public void run() throws Exception { + completed.add(g.getKey()); + } + }); + } + }; + } + + private static final class TestTicker extends Ticker { + long tick; + + @Override + public long read() { + return tick; + } + } @Test public void mapFactoryExpiryCompletesGroupedFlowable() { @@ -1905,32 +1949,6 @@ public void mapFactoryExpiryCompletesGroupedFlowable() { ts.assertValueCount(3); } - private static final Function mod5 = new Function() { - - @Override - public Integer apply(Integer n) throws Exception { - return n % 5; - } - }; - - @Test - public void mapFactoryWithExpiringGuavaCacheDemonstrationCodeForUseInJavadoc() { - //javadoc will be a version of this using lambdas and without assertions - final List completed = new CopyOnWriteArrayList<>(); - //size should be less than 5 to notice the effect - Function, Map> evictingMapFactory = createEvictingMapFactoryGuava(3); - int numValues = 1000; - TestSubscriber ts = - Flowable.range(1, numValues) - .groupBy(mod5, Functions.identity(), true, 16, evictingMapFactory) - .flatMap(addCompletedKey(completed)) - .test() - .assertComplete(); - ts.assertValueCount(numValues); - //the exact eviction behaviour of the guava cache is not specified so we make some approximate tests - assertTrue(completed.size() > numValues * 0.9); - } - @Test public void mapFactoryEvictionQueueClearedOnErrorCoverageOnly() { Function, Map> evictingMapFactory = createEvictingMapFactorySynchronousOnly(1); @@ -1952,28 +1970,28 @@ public Publisher apply(GroupedFlowable g) throws Exce .assertError(ex); } - private static Function, Publisher> addCompletedKey( - final List completed) { - return new Function, Publisher>() { - @Override - public Publisher apply(final GroupedFlowable g) throws Exception { - return g.doOnComplete(new Action() { - @Override - public void run() throws Exception { - completed.add(g.getKey()); - } - }); - } - }; - } + @Test + public void mapFactoryWithExpiringGuavaCacheDemonstrationCodeForUseInJavadoc() { + //javadoc will be a version of this using lambdas and without assertions + final List completed = new CopyOnWriteArrayList<>(); - private static final class TestTicker extends Ticker { - long tick; + AtomicReference> cacheOut = new AtomicReference<>(); - @Override - public long read() { - return tick; - } + //size should be less than 5 to notice the effect + Function, Map> evictingMapFactory = createEvictingMapFactoryGuava(3, cacheOut); + int numValues = 1000; + TestSubscriber ts = + Flowable.range(1, numValues) + .groupBy(mod5, Functions.identity(), true, 16, evictingMapFactory) + .flatMap(addCompletedKey(completed)) + .test() + .assertComplete() + ; + ts.assertValueCount(numValues); + //the exact eviction behaviour of the guava cache is not specified so we make some approximate tests + assertTrue(completed.size() > numValues * 0.9); + + cacheOut.get().invalidateAll(); } @Test @@ -2066,28 +2084,6 @@ public void run() throws Exception { ), list); } - @Test - public void cancellationOfUpstreamWhenGroupedFlowableCompletes() { - final AtomicBoolean cancelled = new AtomicBoolean(); - Flowable.just(1).repeat().doOnCancel(new Action() { - @Override - public void run() throws Exception { - cancelled.set(true); - } - }) - .groupBy(Functions.identity(), Functions.identity()) // - .flatMap(new Function, Publisher>() { - @Override - public Publisher apply(GroupedFlowable g) throws Exception { - return g.first(0).toFlowable(); - } - }) - .take(4) // - .test() // - .assertComplete(); - assertTrue(cancelled.get()); - } - //not thread safe private static final class SingleThreadEvictingHashMap implements Map { @@ -2185,13 +2181,14 @@ public Set> entrySet() { } } - private static Function, Map> createEvictingMapFactoryGuava(final int maxSize) { + private static Function, Map> createEvictingMapFactoryGuava(final int maxSize, + final AtomicReference> cacheOut) { Function, Map> evictingMapFactory = // new Function, Map>() { @Override public Map apply(final Consumer notify) throws Exception { - return CacheBuilder.newBuilder() // + Cache cache = CacheBuilder.newBuilder() // .maximumSize(maxSize) // .removalListener(new RemovalListener() { @Override @@ -2202,8 +2199,9 @@ public void onRemoval(RemovalNotification notification) { throw new RuntimeException(e); } }}) - . build() - .asMap(); + . build(); + cacheOut.set(cache); + return cache.asMap(); }}; return evictingMapFactory; } @@ -2228,6 +2226,30 @@ public void accept(Object object) { return evictingMapFactory; } + // ----------------------------------------------------------------------------------------------------------------------- + + @Test + public void cancellationOfUpstreamWhenGroupedFlowableCompletes() { + final AtomicBoolean cancelled = new AtomicBoolean(); + Flowable.just(1).repeat().doOnCancel(new Action() { + @Override + public void run() throws Exception { + cancelled.set(true); + } + }) + .groupBy(Functions.identity(), Functions.identity()) // + .flatMap(new Function, Publisher>() { + @Override + public Publisher apply(GroupedFlowable g) throws Exception { + return g.first(0).toFlowable(); + } + }) + .take(4) // + .test() // + .assertComplete(); + assertTrue(cancelled.get()); + } + @Test public void cancelOverFlatmapRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { @@ -2478,4 +2500,62 @@ public void run() throws Exception { assertEquals(1000, counter.get()); } + + @Test + public void delayErrorCompleteMoreWorkInGroup() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.groupBy(v -> 1, true) + .flatMap(g -> g.doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + pp.onComplete(); + } + }) + ) + .test() + ; + + pp.onNext(1); + + ts.assertResult(1, 2); + } + + @Test + public void groupSyncFusionRejected() { + Flowable.just(1) + .groupBy(v -> 1) + .doOnNext(g -> { + g.subscribeWith(new TestSubscriberEx().setInitialFusionMode(QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + }) + .test() + .assertComplete(); + } + + @Test + public void subscribeAbandonRace() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = TestSubscriber.create(); + + CountDownLatch cdl = new CountDownLatch(1); + + pp.groupBy(v -> 1) + .doOnNext(g -> { + TestHelper.raceOther(() -> { + g.subscribe(ts); + }, cdl); + }) + .test(); + + pp.onNext(1); + + cdl.await(); + + ts.assertValueCount(1); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java index bed0686923..2e7eba8d1a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java @@ -31,7 +31,7 @@ import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.operators.flowable.FlowableGroupJoin.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -732,4 +732,55 @@ public void leftRightEndState() { verify(js).innerClose(false, o); } + + @Test + public void disposeAfterOnNext() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber<>(); + + pp1.groupJoin(pp2, v -> Flowable.never(), v -> Flowable.never(), (a, b) -> a) + .doOnNext(v -> { + ts.cancel(); + }) + .subscribe(ts); + + pp2.onNext(1); + pp1.onNext(1); + } + + @Test + public void completeWithMoreWork() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber<>(); + + pp1.groupJoin(pp2, v -> Flowable.never(), v -> Flowable.never(), (a, b) -> a) + .doOnNext(v -> { + if (v == 1) { + pp2.onNext(2); + pp1.onComplete(); + pp2.onComplete(); + } + }) + .subscribe(ts); + + pp2.onNext(1); + pp1.onNext(1); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().groupJoin(Flowable.never(), v -> Flowable.never(), v -> Flowable.never(), (a, b) -> a)); + } + + @Test + public void missingBackpressure() { + Flowable.just(1) + .groupJoin(Flowable.never(), v -> BehaviorProcessor.createDefault(1), v -> Flowable.never(), (a, b) -> a) + .test(0) + .assertFailure(MissingBackpressureException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRangeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRangeTest.java index 46cf13229b..70156bcd81 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRangeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRangeTest.java @@ -118,4 +118,12 @@ public void cancel() { .test() .assertResult(0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L); } + + @Test + public void takeSameAsRange() { + Flowable.intervalRange(0, 2, 1, 1, TimeUnit.MILLISECONDS, Schedulers.trampoline()) + .take(2) + .test() + .assertResult(0L, 1L); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java index 7c33df579c..080be1bb59 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java @@ -487,4 +487,35 @@ public Object apply(Integer a, Integer b) throws Exception { ts.assertFailure(MissingBackpressureException.class); } + + @Test + public void badRequest() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + TestHelper.assertBadRequestReported(pp1.join(pp2, Functions.justFunction(Flowable.never()), Functions.justFunction(Flowable.never()), (a, b) -> a + b)); + } + + @Test + public void bothTerminateWithWorkRemaining() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + TestSubscriber ts = pp1.join( + pp2, + v -> Flowable.never(), + v -> Flowable.never(), + (a, b) -> a + b) + .doOnNext(v -> { + pp1.onComplete(); + pp2.onNext(2); + pp2.onComplete(); + }) + .test(); + + pp1.onNext(0); + pp2.onNext(1); + + ts.assertComplete(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybeTest.java index e5ef48a426..655d75fe08 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybeTest.java @@ -28,7 +28,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; -import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -448,4 +448,22 @@ public Flowable apply(Flowable upstream) { } }); } + + @Test + public void drainMoreWorkBeforeCancel() { + MaybeSubject ms = MaybeSubject.create(); + + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.range(1, 5).mergeWith(ms) + .doOnNext(v -> { + if (v == 1) { + ms.onSuccess(6); + ts.cancel(); + } + }) + .subscribe(ts); + + ts.assertValuesOnly(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingleTest.java index a48ee16fef..c187880426 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingleTest.java @@ -444,4 +444,22 @@ public Flowable apply(Flowable upstream) { } }); } + + @Test + public void drainMoreWorkBeforeCancel() { + SingleSubject ss = SingleSubject.create(); + + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.range(1, 5).mergeWith(ss) + .doOnNext(v -> { + if (v == 1) { + ss.onSuccess(6); + ts.cancel(); + } + }) + .subscribe(ts); + + ts.assertValuesOnly(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOnTest.java index 7fbf19fa60..a2bf5505ce 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOnTest.java @@ -1154,6 +1154,16 @@ public Flowable apply(Flowable f) throws Exception { }); } + @Test + public void doubleOnSubscribeConditional() { + TestHelper.checkDoubleOnSubscribeFlowable(new Function, Flowable>() { + @Override + public Flowable apply(Flowable f) throws Exception { + return f.observeOn(new TestScheduler()).compose(TestHelper.conditional()); + } + }); + } + @Test public void badSource() { List errors = TestHelper.trackPluginErrors(); @@ -1988,4 +1998,120 @@ public void fusedParallelProcessing() { .assertComplete() .assertNoErrors(); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().observeOn(ImmediateThinScheduler.INSTANCE)); + } + + @Test + public void syncFusedCancelAfterPoll() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .map(v -> { + ts.cancel(); + return v + 1; + }) + .compose(TestHelper.flowableStripBoundary()) + .observeOn(ImmediateThinScheduler.INSTANCE) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void syncFusedCancelAfterPollConditional() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .map(v -> { + ts.cancel(); + return v + 1; + }) + .compose(TestHelper.flowableStripBoundary()) + .observeOn(ImmediateThinScheduler.INSTANCE) + .compose(TestHelper.conditional()) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void backFusedMoreWork() { + final TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); + + PublishProcessor pp = PublishProcessor.create(); + + pp.observeOn(ImmediateThinScheduler.INSTANCE) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + } + }) + .subscribe(ts); + + pp.onNext(1); + + ts.assertValuesOnly(1, 2); + } + + @Test + public void moreWorkInRunAsync() { + final TestSubscriberEx ts = new TestSubscriberEx<>(); + + PublishProcessor pp = PublishProcessor.create(); + + pp.observeOn(ImmediateThinScheduler.INSTANCE) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + } + }) + .subscribe(ts); + + pp.onNext(1); + + ts.assertValuesOnly(1, 2); + } + + @Test + public void backFusedConditionalMoreWork() { + final TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); + + PublishProcessor pp = PublishProcessor.create(); + + pp.observeOn(ImmediateThinScheduler.INSTANCE) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + } + }) + .compose(TestHelper.conditional()) + .subscribe(ts); + + pp.onNext(1); + + ts.assertValuesOnly(1, 2); + } + + @Test + public void conditionalMoreWorkInRunAsync() { + final TestSubscriberEx ts = new TestSubscriberEx<>(); + + PublishProcessor pp = PublishProcessor.create(); + + pp.observeOn(ImmediateThinScheduler.INSTANCE) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + } + }) + .compose(TestHelper.conditional()) + .subscribe(ts); + + pp.onNext(1); + + ts.assertValuesOnly(1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategyTest.java index 327078ad60..000d504211 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategyTest.java @@ -206,4 +206,23 @@ public void justTake() { .test() .assertResult(1); } + + @Test + public void overflowNullAction() { + Flowable.range(1, 5) + .onBackpressureBuffer(1, null, BackpressureOverflowStrategy.DROP_OLDEST) + .test(0L) + .assertEmpty(); + } + + @Test + public void cancelOnDrain() { + Flowable.range(1, 5) + .onBackpressureBuffer(10, null, BackpressureOverflowStrategy.DROP_OLDEST) + .takeUntil(v -> true) + .test(0L) + .assertEmpty() + .requestMore(10) + .assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferTest.java index cc5992862b..588127bae4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferTest.java @@ -340,4 +340,14 @@ public void fusedNoConcurrentCleanDueToCancel() { } } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.onBackpressureBuffer()); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().onBackpressureBuffer()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java index 0dffd13798..6e701e5fc3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java @@ -1697,4 +1697,83 @@ public void disposeResets() { ts.assertValuesOnly(1); } + + @Test(expected = TestException.class) + public void connectDisposeCrash() { + ConnectableFlowable cf = Flowable.never().publish(); + + cf.connect(); + + cf.connect(d -> { throw new TestException(); }); + } + + @Test + public void resetWhileNotConnectedIsNoOp() { + ConnectableFlowable cf = Flowable.never().publish(); + + cf.reset(); + } + + @Test + public void resetWhileActiveIsNoOp() { + ConnectableFlowable cf = Flowable.never().publish(); + + cf.connect(); + + cf.reset(); + } + + @Test + public void crossCancelOnComplete() { + TestSubscriber ts1 = new TestSubscriber<>(); + TestSubscriber ts2 = new TestSubscriber() { + @Override + public void onComplete() { + super.onComplete(); + ts1.cancel(); + } + }; + + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.publish(); + + cf.subscribe(ts2); + cf.subscribe(ts1); + + cf.connect(); + + pp.onComplete(); + + ts2.assertResult(); + + ts1.assertEmpty(); + } + + @Test + public void crossCancelOnError() { + TestSubscriber ts1 = new TestSubscriber<>(); + TestSubscriber ts2 = new TestSubscriber() { + @Override + public void onError(Throwable t) { + super.onError(t); + ts1.cancel(); + } + }; + + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.publish(); + + cf.subscribe(ts2); + cf.subscribe(ts1); + + cf.connect(); + + pp.onError(new TestException()); + + ts2.assertFailure(TestException.class); + + ts1.assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLongTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLongTest.java index 96401918ef..d22cecfe5b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLongTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLongTest.java @@ -539,4 +539,44 @@ public boolean test(Long v) throws Exception { ts.assertResult(2L, 4L); } + + @Test + public void slowPathCancelBeforeComplete() { + Flowable.rangeLong(1, 2) + .take(2) + .test() + .assertResult(1L, 2L); + } + + @Test + public void conditionalFastPathCancelBeforeComplete() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.rangeLong(1, 2) + .compose(TestHelper.conditional()) + .doOnNext(v -> { + if (v == 2L) { + ts.cancel(); + } + }) + .subscribe(ts); + + ts.assertValuesOnly(1L, 2L); + } + + @Test + public void conditionalSlowPathTake() { + TestSubscriber ts = new TestSubscriber<>(4); + + Flowable.rangeLong(1, 3) + .compose(TestHelper.conditional()) + .doOnNext(v -> { + if (v == 2L) { + ts.cancel(); + } + }) + .subscribe(ts); + + ts.assertValuesOnly(1L, 2L); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeTest.java index 39ab84ad63..628edd30c0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeTest.java @@ -590,4 +590,28 @@ public void onNext(Integer t) { ts.assertResult(1, 2); } + + @Test + public void slowPathCancelBeforeComplete() { + Flowable.range(1, 2) + .take(2) + .test() + .assertResult(1, 2); + } + + @Test + public void conditionalFastPatchCancelBeforeComplete() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.range(1, 2) + .compose(TestHelper.conditional()) + .doOnNext(v -> { + if (v == 2) { + ts.cancel(); + } + }) + .subscribe(ts); + + ts.assertValuesOnly(1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceTest.java index bc7d1e8ece..170b826bd7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceTest.java @@ -530,4 +530,9 @@ public Integer apply(Integer a, Integer b) throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribeFlowable() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.reduce((a, b) -> a).toFlowable()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCountTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCountTest.java index 169409ed1b..02a730b610 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCountTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCountTest.java @@ -45,7 +45,25 @@ public class FlowableRefCountTest extends RxJavaTest { @Test - public void refCountAsync() { + public void refCountAsync() throws InterruptedException { + // Flaky + for (int i = 0; i < 10; i++) { + try { + refCountAsyncActual(); + return; + } catch (AssertionError ex) { + if (i == 9) { + throw ex; + } + Thread.sleep((int)(200 * (Math.random() * 10 + 1))); + } + } + } + + /** + * Tries to coordinate async counting but it is flaky due to the low 10s of milliseconds. + */ + void refCountAsyncActual() { final AtomicInteger subscribeCount = new AtomicInteger(); final AtomicInteger nextCount = new AtomicInteger(); Flowable r = Flowable.interval(0, 20, TimeUnit.MILLISECONDS) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java index f263b98f66..ce2cf5a564 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java @@ -732,7 +732,14 @@ public boolean isDisposed() { @Test public void boundedReplayBuffer() { - BoundedReplayBuffer buf = new BoundedReplayBuffer<>(true); + BoundedReplayBuffer buf = new BoundedReplayBuffer(true) { + private static final long serialVersionUID = -9081211580719235896L; + + @Override + void truncate() { + } + }; + buf.addLast(new Node(1, 0)); buf.addLast(new Node(2, 1)); buf.addLast(new Node(3, 2)); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java index 67763fce0b..e151dd07f1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java @@ -17,6 +17,7 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.io.IOException; import java.lang.management.*; import java.util.*; import java.util.concurrent.*; @@ -37,6 +38,7 @@ import io.reactivex.rxjava3.internal.fuseable.HasUpstreamPublisher; import io.reactivex.rxjava3.internal.operators.flowable.FlowableReplay.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.internal.util.BackpressureHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.*; @@ -44,6 +46,7 @@ import io.reactivex.rxjava3.testsupport.*; public class FlowableReplayTest extends RxJavaTest { + @Test public void bufferedReplay() { PublishProcessor source = PublishProcessor.create(); @@ -732,7 +735,14 @@ public boolean isDisposed() { @Test public void boundedReplayBuffer() { - BoundedReplayBuffer buf = new BoundedReplayBuffer<>(false); + BoundedReplayBuffer buf = new BoundedReplayBuffer(false) { + private static final long serialVersionUID = -9081211580719235896L; + + @Override + void truncate() { + } + }; + buf.addLast(new Node(1, 0)); buf.addLast(new Node(2, 1)); buf.addLast(new Node(3, 2)); @@ -760,6 +770,19 @@ public void boundedReplayBuffer() { } + @Test(expected = IllegalStateException.class) + public void boundedRemoveFirstOneItemOnly() { + BoundedReplayBuffer buf = new BoundedReplayBuffer(false) { + private static final long serialVersionUID = -9081211580719235896L; + + @Override + void truncate() { + } + }; + + buf.removeFirst(); + } + @Test public void timedAndSizedTruncation() { TestScheduler test = new TestScheduler(); @@ -965,7 +988,9 @@ public void take() { TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable cached = Flowable.range(1, 100).replay().autoConnect(); - cached.take(10).subscribe(ts); + cached + .take(10) + .subscribe(ts); ts.assertNoErrors(); ts.assertTerminated(); @@ -1079,7 +1104,7 @@ public void valuesAndThenError() { } @Test - public void unsafeChildThrows() { + public void unsafeChildOnNextThrows() { final AtomicInteger count = new AtomicInteger(); Flowable source = Flowable.range(1, 100) @@ -1107,6 +1132,52 @@ public void onNext(Integer t) { ts.assertError(TestException.class); } + @Test + public void unsafeChildOnErrorThrows() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable source = Flowable.error(new IOException()) + .replay() + .autoConnect(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onError(Throwable t) { + super.onError(t); + throw new TestException(); + } + }; + + source.subscribe(ts); + + ts.assertFailure(IOException.class); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void unsafeChildOnCompleteThrows() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable source = Flowable.empty() + .replay() + .autoConnect(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onComplete() { + super.onComplete(); + throw new TestException(); + } + }; + + source.subscribe(ts); + + ts.assertResult(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + @Test public void unboundedLeavesEarly() { PublishProcessor source = PublishProcessor.create(); @@ -1992,4 +2063,132 @@ public void accept(byte[] v) throws Exception { + " -> " + after.get() / 1024.0 / 1024.0); } } + + @Test + public void unsafeChildOnNextThrowsSizeBound() { + final AtomicInteger count = new AtomicInteger(); + + Flowable source = Flowable.range(1, 100) + .doOnNext(new Consumer() { + @Override + public void accept(Integer t) { + count.getAndIncrement(); + } + }) + .replay(1000).autoConnect(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(Integer t) { + throw new TestException(); + } + }; + + source.subscribe(ts); + + Assert.assertEquals(100, count.get()); + + ts.assertNoValues(); + ts.assertNotComplete(); + ts.assertError(TestException.class); + } + + @Test + public void unsafeChildOnErrorThrowsSizeBound() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable source = Flowable.error(new IOException()) + .replay(1000) + .autoConnect(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onError(Throwable t) { + super.onError(t); + throw new TestException(); + } + }; + + source.subscribe(ts); + + ts.assertFailure(IOException.class); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void unsafeChildOnCompleteThrowsSizeBound() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable source = Flowable.empty() + .replay(1000) + .autoConnect(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onComplete() { + super.onComplete(); + throw new TestException(); + } + }; + + source.subscribe(ts); + + ts.assertResult(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test(expected = TestException.class) + public void connectDisposeCrash() { + ConnectableFlowable cf = Flowable.never().replay(); + + cf.connect(); + + cf.connect(d -> { throw new TestException(); }); + } + + @Test + public void resetWhileNotConnectedIsNoOp() { + ConnectableFlowable cf = Flowable.never().replay(); + + cf.reset(); + } + + @Test + public void resetWhileActiveIsNoOp() { + ConnectableFlowable cf = Flowable.never().replay(); + + cf.connect(); + + cf.reset(); + } + + @Test + public void delayedUpstreamSubscription() { + AtomicReference> ref = new AtomicReference<>(); + Flowable f = Flowable.unsafeCreate(ref::set); + + TestSubscriber ts = f.replay() + .autoConnect() + .test(); + + AtomicLong requested = new AtomicLong(); + + ref.get().onSubscribe(new Subscription() { + @Override + public void request(long n) { + BackpressureHelper.add(requested, n); + } + + @Override + public void cancel() { + } + }); + + assertEquals(Long.MAX_VALUE, requested.get()); + ref.get().onComplete(); + + ts.assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTest.java index f9ed6509db..3c47d264ca 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTest.java @@ -481,4 +481,10 @@ public void badRequest() { TestHelper.assertBadRequestReported(PublishProcessor.create() .sample(PublishProcessor.create())); } + + @Test + public void badRequestTimed() { + TestHelper.assertBadRequestReported(PublishProcessor.create() + .sample(1, TimeUnit.MINUTES)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMapTest.java index a4abfce00c..5a47a46d9b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMapTest.java @@ -243,4 +243,12 @@ public void cancelled() { assertTrue(scalar.isCancelled()); } + + @Test + public void mapToNonScalar() { + Flowable.fromCallable(() -> 1) + .concatMap(v -> Flowable.range(1, 5)) + .test() + .assertResult(1, 2, 3, 4, 5); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanTest.java index 2988da7570..6ebac502b0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanTest.java @@ -676,4 +676,27 @@ public Integer apply(Integer a, Integer b) throws Exception { } } } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().scanWith(() -> 1, (a, b) -> a + b)); + } + + @Test + public void drainMoreWork() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.scanWith(() -> 0, (a, b) -> a + b) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + pp.onComplete(); + } + }) + .test(); + + pp.onNext(1); + + ts.assertResult(0, 1, 3); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualTest.java index e5e74e8227..a3e997d882 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualTest.java @@ -591,4 +591,26 @@ public Flowable apply(Flowable upstream) { } }); } + + @Test + public void fusionRejected() { + Flowable.sequenceEqual(TestHelper.rejectFlowableFusion(), Flowable.never()) + .test() + .assertEmpty(); + } + + @Test + public void fusionRejectedFlowable() { + Flowable.sequenceEqual(TestHelper.rejectFlowableFusion(), Flowable.never()) + .toFlowable() + .test() + .assertEmpty(); + } + + @Test + public void asyncSourceCompare() { + Flowable.sequenceEqual(Flowable.fromCallable(() -> 1), Flowable.just(1)) + .test() + .assertResult(true); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleTest.java index 099f63f049..0eb0853c0a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleTest.java @@ -805,4 +805,9 @@ public void singleOrError() { .test() .assertFailure(NoSuchElementException.class); } + + @Test + public void dispose() { + TestHelper.checkDisposed(PublishProcessor.create().single(1)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimedTest.java index 87f4ddf881..66d9aa86b6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimedTest.java @@ -250,4 +250,27 @@ public void observeOn() { .assertComplete() .assertNoErrors(); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().skipLast(1, TimeUnit.MINUTES)); + } + + @Test + public void delayErrorMoreWork() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.skipLast(0, TimeUnit.MILLISECONDS, true) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(1); + pp.onComplete(); + } + }) + .test(); + + pp.onNext(1); + + ts.assertComplete(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java index a9f66d733e..1554345f61 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java @@ -27,6 +27,7 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.operators.flowable.FlowableSubscribeOn.SubscribeOnSubscriber; +import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subscribers.*; @@ -406,4 +407,9 @@ public void subscribe(FlowableEmitter s) throws Exception { .assertNoErrors() .assertComplete(); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().subscribeOn(ImmediateThinScheduler.INSTANCE)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchTest.java index fb571c1109..4726d9bfcc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchTest.java @@ -32,7 +32,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.internal.util.ExceptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subscribers.*; import io.reactivex.rxjava3.testsupport.*; @@ -1229,4 +1229,112 @@ public Publisher apply(Integer v) .test() .assertResult(10, 20); } + + @Test + public void asyncFusedInner() { + Flowable.just(1) + .hide() + .switchMap(v -> Flowable.fromCallable(() -> 1)) + .test() + .assertResult(1); + } + + @Test + public void innerIgnoresCancelAndErrors() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp + .switchMap(v -> { + if (v == 1) { + return Flowable.unsafeCreate(s -> { + s.onSubscribe(new BooleanSubscription()); + pp.onNext(2); + s.onError(new TestException()); + }); + } + return Flowable.never(); + }) + .test(); + + pp.onNext(1); + + ts.assertEmpty(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.switchMap(v -> Flowable.never())); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().switchMap(v -> Flowable.never())); + } + + @Test + public void innerFailed() { + BehaviorProcessor.createDefault(Flowable.error(new TestException())) + .switchMap(v -> v) + .test() + .assertFailure(TestException.class) + ; + } + + @Test + public void innerCompleted() { + BehaviorProcessor.createDefault(Flowable.empty().hide()) + .switchMap(v -> v) + .test() + .assertEmpty() + ; + } + + @Test + public void innerCompletedBackpressureBoundary() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = BehaviorProcessor.createDefault(pp) + .onBackpressureBuffer() + .switchMap(v -> v) + .test(1L) + ; + + ts.assertEmpty(); + + pp.onNext(1); + pp.onComplete(); + + ts.assertValuesOnly(1); + } + + @Test + public void innerCompletedDelayError() { + BehaviorProcessor.createDefault(Flowable.empty().hide()) + .switchMapDelayError(v -> v) + .test() + .assertEmpty() + ; + } + + @Test + public void innerCompletedBackpressureBoundaryDelayError() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = BehaviorProcessor.createDefault(pp) + .onBackpressureBuffer() + .switchMapDelayError(v -> v) + .test(1L) + ; + + ts.assertEmpty(); + + pp.onNext(1); + pp.onComplete(); + + ts.assertValuesOnly(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTest.java index de6a431844..9c413985aa 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTest.java @@ -23,8 +23,9 @@ import org.junit.Test; import org.mockito.InOrder; -import org.reactivestreams.Subscriber; +import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; @@ -340,4 +341,54 @@ public void takeLastTake() { .test() .assertResult(6, 7); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().takeLast(2)); + } + + @Test + public void cancelThenRequest() { + Flowable.never().takeLast(2) + .subscribe(new FlowableSubscriber() { + + @Override + public void onNext(@NonNull Object t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + public void onSubscribe(@NonNull Subscription s) { + s.cancel(); + s.request(1); + } + }); + } + + @Test + public void noRequestEmpty() { + Flowable.empty() + .takeLast(2) + .test(0L) + .assertResult(); + } + + @Test + public void moreValuesRemainingThanRequested() { + Flowable.range(1, 4) + .takeLast(3) + .test(0L) + .assertEmpty() + .requestMore(2) + .assertValuesOnly(2, 3) + .requestMore(2) + .assertResult(2, 3, 4); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTest.java index d26d49019a..4c26b2a9f8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTest.java @@ -200,4 +200,14 @@ public void backpressureNoRequest() { .test(0L) .assertFailure(MissingBackpressureException.class); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().throttleFirst(1, TimeUnit.MINUTES)); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.throttleFirst(1, TimeUnit.MINUTES)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTests.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTests.java index 88a3bc2253..de0dcec14c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTests.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTests.java @@ -30,7 +30,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; -import io.reactivex.rxjava3.schedulers.TestScheduler; +import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -528,4 +528,9 @@ public void run() { } } } + + @Test + public void doubleOnSubscribeFallback() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.timeout(1, TimeUnit.MINUTES, Flowable.never())); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutWithSelectorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutWithSelectorTest.java index 7e55fa0d77..db09c48872 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutWithSelectorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutWithSelectorTest.java @@ -32,6 +32,7 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.operators.flowable.FlowableTimeout.TimeoutConsumer; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.*; @@ -894,4 +895,13 @@ protected void subscribeActual(Subscriber s) { RxJavaPlugins.reset(); } } + + @Test + public void timeoutConsumerIsDisposed() { + TimeoutConsumer consumer = new TimeoutConsumer(0, null); + + assertFalse(consumer.isDisposed()); + consumer.dispose(); + assertTrue(consumer.isDisposed()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimerTest.java index e23228f943..4e299dcbd1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimerTest.java @@ -380,4 +380,9 @@ public Long apply(Long v) throws Exception { exec.shutdown(); } } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.timer(1, TimeUnit.MINUTES)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOnTest.java index 1f37dbddce..281b74e463 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOnTest.java @@ -26,6 +26,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Action; +import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -274,4 +275,9 @@ protected void subscribeActual(Subscriber subscriber) { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.unsubscribeOn(ImmediateThinScheduler.INSTANCE)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java index fbefcc953e..b8ae0b35c9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java @@ -605,4 +605,120 @@ public void accept(Flowable v) throws Throwable { inner.get().test().assertResult(1); } + + @Test + public void badRequestExact() { + TestHelper.assertBadRequestReported(Flowable.never().window(1)); + } + + @Test + public void badRequestSkip() { + TestHelper.assertBadRequestReported(Flowable.never().window(1, 2)); + } + + @Test + public void badRequestOverlap() { + TestHelper.assertBadRequestReported(Flowable.never().window(2, 1)); + } + + @Test + public void skipEmpty() { + Flowable.empty() + .window(1, 2) + .test() + .assertResult(); + } + + @Test + public void exactEmpty() { + Flowable.empty() + .window(2) + .test() + .assertResult(); + } + + @Test + public void skipMultipleRequests() { + Flowable.range(1, 10) + .window(1, 2) + .doOnNext(w -> w.test()) + .rebatchRequests(1) + .test() + .assertComplete(); + } + + @Test + public void skipOne() { + Flowable.just(1) + .window(2, 3) + .flatMap(v -> v) + .test() + .assertResult(1); + } + + @Test + public void overlapMultipleRequests() { + Flowable.range(1, 10) + .window(2, 1) + .doOnNext(w -> w.test()) + .rebatchRequests(1) + .test() + .assertComplete(); + } + + @Test + public void overlapCancelAfterWindow() { + Flowable.range(1, 10) + .window(2, 1) + .takeUntil(v -> true) + .doOnNext(w -> w.test()) + .test(0L) + .requestMore(10) + .assertComplete(); + } + + @Test + public void overlapEmpty() { + Flowable.empty() + .window(2, 1) + .test() + .assertResult(); + } + + @Test + public void overlapEmptyNoRequest() { + Flowable.empty() + .window(2, 1) + .test(0L) + .assertResult(); + } + + @Test + public void overlapMoreWorkAfterOnNext() { + PublishProcessor pp = PublishProcessor.create(); + AtomicBoolean once = new AtomicBoolean(); + + TestSubscriber> ts = pp.window(2, 1) + .doOnNext(v -> { + v.test(); + if (once.compareAndSet(false, true)) { + pp.onNext(2); + pp.onComplete(); + } + }) + .test(); + + pp.onNext(1); + + ts.assertComplete(); + } + + @Test + public void moreQueuedClean() { + Flowable.range(1, 10) + .window(5, 1) + .doOnNext(w -> w.test()) + .test(3) + .cancel(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java index 023f6848d5..a9a5a7e821 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java @@ -15,6 +15,7 @@ import static org.junit.Assert.*; +import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.*; @@ -233,20 +234,20 @@ public void dispose() { @Test public void reentrant() { - final FlowableProcessor ps = PublishProcessor.create(); + final FlowableProcessor pp = PublishProcessor.create(); TestSubscriber ts = new TestSubscriber() { @Override public void onNext(Integer t) { super.onNext(t); if (t == 1) { - ps.onNext(2); - ps.onComplete(); + pp.onNext(2); + pp.onComplete(); } } }; - ps.window(BehaviorProcessor.createDefault(1), Functions.justFunction(Flowable.never())) + pp.window(BehaviorProcessor.createDefault(1), Functions.justFunction(Flowable.never())) .flatMap(new Function, Flowable>() { @Override public Flowable apply(Flowable v) throws Exception { @@ -255,7 +256,7 @@ public Flowable apply(Flowable v) throws Exception { }) .subscribe(ts); - ps.onNext(1); + pp.onNext(1); ts .awaitDone(1, TimeUnit.SECONDS) @@ -539,4 +540,179 @@ public Publisher apply(Integer end) throws Throwable { assertFalse(boundary.hasSubscribers()); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(o -> o.window(Flowable.never(), v -> Flowable.never())); + } + + @Test + public void openError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestException ex1 = new TestException(); + TestException ex2 = new TestException(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Flowable f1 = Flowable.fromPublisher(ref1::set); + Flowable f2 = Flowable.fromPublisher(ref2::set); + + TestSubscriber> ts = BehaviorProcessor.createDefault(1) + .window(f1, v -> f2) + .doOnNext(w -> w.test()) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + ref2.get().onSubscribe(new BooleanSubscription()); + + TestHelper.race( + () -> ref1.get().onError(ex1), + () -> ref2.get().onError(ex2) + ); + + ts.assertError(RuntimeException.class); + + if (!errors.isEmpty()) { + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } + + errors.clear(); + } + }); + } + + @Test + public void closeError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Flowable f1 = Flowable.unsafeCreate(ref1::set); + Flowable f2 = Flowable.unsafeCreate(ref2::set); + + TestSubscriber ts = BehaviorProcessor.createDefault(1) + .window(f1, v -> f2) + .flatMap(v -> v) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + ref2.get().onSubscribe(new BooleanSubscription()); + + ref2.get().onError(new TestException()); + ref2.get().onError(new TestException()); + + ts.assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void upstreamFailsBeforeFirstWindow() { + Flowable.error(new TestException()) + .window(Flowable.never(), v -> Flowable.never()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void windowOpenMainCompletes() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishProcessor pp = PublishProcessor.create(); + Flowable f1 = Flowable.unsafeCreate(ref1::set); + + AtomicInteger counter = new AtomicInteger(); + + TestSubscriber> ts = pp + .window(f1, v -> Flowable.never()) + .doOnNext(w -> { + if (counter.getAndIncrement() == 0) { + ref1.get().onNext(2); + pp.onNext(1); + pp.onComplete(); + } + w.test(); + }) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + + ts.assertComplete(); + } + + @Test + public void windowOpenMainError() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishProcessor pp = PublishProcessor.create(); + Flowable f1 = Flowable.unsafeCreate(ref1::set); + + AtomicInteger counter = new AtomicInteger(); + + TestSubscriber> ts = pp + .window(f1, v -> Flowable.never()) + .doOnNext(w -> { + if (counter.getAndIncrement() == 0) { + ref1.get().onNext(2); + pp.onNext(1); + pp.onError(new TestException()); + } + w.test(); + }) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + + ts.assertError(TestException.class); + } + + @Test + public void windowOpenIgnoresDispose() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishProcessor pp = PublishProcessor.create(); + Flowable f1 = Flowable.unsafeCreate(ref1::set); + + TestSubscriber> ts = pp + .window(f1, v -> Flowable.never()) + .take(1) + .doOnNext(w -> { + w.test(); + }) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + ref1.get().onNext(2); + + ts.assertValueCount(1); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().window(Flowable.never(), v -> Flowable.never())); + } + + @Test + public void mainIgnoresCancelBeforeOnError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable.fromPublisher(s -> { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onError(new IOException()); + }) + .window(BehaviorProcessor.createDefault(1), v -> Flowable.error(new TestException())) + .doOnNext(w -> w.test()) + .test() + .assertError(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java index e3d9466b3e..bf6a271b84 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java @@ -1321,5 +1321,27 @@ public void accept(Flowable v) throws Throwable { inner.get().test().assertResult(); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().window(1, TimeUnit.SECONDS)); + } + + @Test + public void timedBoundarySignalAndDisposeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestScheduler scheduler = new TestScheduler(); + + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(1, TimeUnit.MINUTES, scheduler, 1) + .test(); + + TestHelper.race( + () -> pp.onNext(1), + () -> ts.cancel() + ); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipTest.java index 614dc28aaa..bc012f9713 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipTest.java @@ -1914,4 +1914,30 @@ public Integer apply(Object[] t) throws Throwable { .test() .assertResult(2); } + + @Test + public void fusedInnerPollCrashDelayError() { + Flowable.zip( + Flowable.range(1, 5), + Flowable.just(1) + .map(v -> { throw new TestException(); }) + .compose(TestHelper.flowableStripBoundary()), + (a, b) -> a + b, true + ) + .test() + .assertFailure(TestException.class); + } + + @Test + public void fusedInnerPollCrashRequestBoundaryDelayError() { + Flowable.zip( + Flowable.range(1, 5), + Flowable.just(1) + .map(v -> { throw new TestException(); }) + .compose(TestHelper.flowableStripBoundary()), + (a, b) -> a + b, true + ) + .test(0L) + .assertFailure(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayTest.java index 301a8a35f5..681748c39d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayTest.java @@ -18,12 +18,13 @@ import java.io.IOException; import java.util.List; -import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -188,4 +189,79 @@ public void subscribe(MaybeEmitter s) throws Exception { assertEquals(1, calls[0]); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Maybe.concatArray(MaybeSubject.create(), MaybeSubject.create())); + } + + @Test + public void badRequestDelayError() { + TestHelper.assertBadRequestReported(Maybe.concatArrayDelayError(MaybeSubject.create(), MaybeSubject.create())); + } + + @Test + public void mixed() { + Maybe.concatArray( + Maybe.just(1), + Maybe.empty(), + Maybe.just(2), + Maybe.empty(), + Maybe.empty() + ) + .test() + .assertResult(1, 2); + } + + @Test + public void requestBeforeSuccess() { + MaybeSubject ms = MaybeSubject.create(); + TestSubscriber ts = Maybe.concatArray(ms, ms) + .test(); + + ts.assertEmpty(); + + ms.onSuccess(1); + + ts.assertResult(1, 1); + } + + @Test + public void requestBeforeComplete() { + MaybeSubject ms = MaybeSubject.create(); + TestSubscriber ts = Maybe.concatArray(ms, ms) + .test(); + + ts.assertEmpty(); + + ms.onComplete(); + + ts.assertResult(); + } + + @Test + public void requestBeforeSuccessDelayError() { + MaybeSubject ms = MaybeSubject.create(); + TestSubscriber ts = Maybe.concatArrayDelayError(ms, ms) + .test(); + + ts.assertEmpty(); + + ms.onSuccess(1); + + ts.assertResult(1, 1); + } + + @Test + public void requestBeforeCompleteDelayError() { + MaybeSubject ms = MaybeSubject.create(); + TestSubscriber ts = Maybe.concatArrayDelayError(ms, ms) + .test(); + + ts.assertEmpty(); + + ms.onComplete(); + + ts.assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterableTest.java index 7450dc993b..7b9c33db99 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterableTest.java @@ -24,6 +24,7 @@ import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.util.CrashingMappedIterable; import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -158,4 +159,12 @@ public void subscribe(MaybeEmitter s) throws Exception { assertEquals(1, calls[0]); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Maybe.concat(Arrays.asList( + MaybeSubject.create(), + MaybeSubject.create() + ))); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionTest.java index bca4023f83..cc7a981050 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionTest.java @@ -141,4 +141,9 @@ protected void subscribeActual(Subscriber subscriber) { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribePublisher() { + TestHelper.checkDoubleOnSubscribeFlowableToMaybe(f -> Maybe.just(1).delaySubscription(f)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowableTest.java index 1c840a2295..aed3f49199 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowableTest.java @@ -27,7 +27,7 @@ import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.util.CrashingIterable; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -120,6 +120,20 @@ public Iterable apply(Integer v) throws Exception { .assertResult(1); } + @Test + public void take2() { + Maybe.just(1).flattenAsFlowable(new Function>() { + @Override + public Iterable apply(Integer v) throws Exception { + return Arrays.asList(v, v + 1); + } + }) + .doOnSubscribe(s -> s.request(Long.MAX_VALUE)) + .take(1) + .test() + .assertResult(1); + } + @Test public void fused() { TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); @@ -562,4 +576,33 @@ public void remove() { ts.request(Long.MAX_VALUE); ts.assertValues(1, 1).assertNoErrors().assertNotComplete(); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(MaybeSubject.create().flattenAsFlowable(v -> Arrays.asList(v))); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeMaybeToFlowable(m -> m.flattenAsFlowable(v -> Arrays.asList(v))); + } + + @Test + public void onSuccessRequestRace() { + List list = Arrays.asList(1); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + MaybeSubject ms = MaybeSubject.create(); + + TestSubscriber ts = ms.flattenAsFlowable(v -> list) + .test(0L); + + TestHelper.race( + () -> ms.onSuccess(1), + () -> ts.request(1) + ); + + ts.assertResult(1); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallableTest.java index 70c432f5ae..28284b0a78 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallableTest.java @@ -213,4 +213,18 @@ public String answer(InvocationOnMock invocation) throws Throwable { verify(observer).onSubscribe(any(Disposable.class)); verifyNoMoreInteractions(observer); } + + @Test + public void disposeUpfront() { + Maybe.fromCallable(() -> 1) + .test(true) + .assertEmpty(); + } + + @Test + public void success() { + Maybe.fromCallable(() -> 1) + .test() + .assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplierTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplierTest.java index 247021b12b..a9a74f46a5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplierTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplierTest.java @@ -214,4 +214,23 @@ public String answer(InvocationOnMock invocation) throws Throwable { verify(observer).onSubscribe(any(Disposable.class)); verifyNoMoreInteractions(observer); } + + @Test + public void success() { + Maybe.fromSupplier(() -> 1) + .test() + .assertResult(1); + } + + @Test + public void disposeUpfront() throws Throwable { + @SuppressWarnings("unchecked") + Supplier supplier = mock(Supplier.class); + + Maybe.fromSupplier(supplier) + .test(true) + .assertEmpty(); + + verify(supplier, never()).get(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArrayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArrayTest.java index 6b886fd894..0ddc2eefe1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArrayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArrayTest.java @@ -17,16 +17,16 @@ import java.util.*; -import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import org.reactivestreams.Subscription; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.operators.maybe.MaybeMergeArray.MergeMaybeObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -250,4 +250,25 @@ public void onComplete() { } }); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported( + Maybe.mergeArray(MaybeSubject.create(), MaybeSubject.create()) + ); + } + + @Test + public void cancel2() { + TestHelper.checkDisposed(Maybe.mergeArray(MaybeSubject.create(), MaybeSubject.create())); + } + + @Test + public void take() { + Maybe.mergeArray(Maybe.just(1), Maybe.empty(), Maybe.just(2)) + .doOnSubscribe(s -> s.request(Long.MAX_VALUE)) + .take(1) + .test() + .assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisherTest.java index 188db93597..22e99bf9aa 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisherTest.java @@ -19,7 +19,9 @@ import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.observers.TestObserver; @@ -231,4 +233,29 @@ public Object apply(Flowable f) throws Exception { } }, false, null, 1, 1); } + + @Test + public void mainSuccessAfterOtherSignal() { + PublishProcessor pp = PublishProcessor.create(); + + new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + observer.onSubscribe(Disposable.empty()); + pp.onNext(2); + observer.onSuccess(1); + } + } + .timeout(pp) + .test() + .assertFailure(TimeoutException.class); + } + + @Test + public void mainSuccess() { + Maybe.just(1) + .timeout(Flowable.never()) + .test() + .assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutTest.java index 7e30ce6a49..ba806c5a32 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutTest.java @@ -19,12 +19,15 @@ import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.testsupport.*; public class MaybeTimeoutTest extends RxJavaTest { @@ -341,4 +344,21 @@ public void run() { } } } + + @Test + public void mainSuccessAfterOtherSignal() { + MaybeSubject ms = MaybeSubject.create(); + + new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + observer.onSubscribe(Disposable.empty()); + ms.onSuccess(2); + observer.onSuccess(1); + } + } + .timeout(ms) + .test() + .assertFailure(TimeoutException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArrayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArrayTest.java index c685e79cae..1407886997 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArrayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArrayTest.java @@ -15,17 +15,21 @@ import static org.junit.Assert.*; -import java.util.List; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeZipArrayTest extends RxJavaTest { @@ -168,4 +172,53 @@ public void singleSourceZipperReturnsNull() { .to(TestHelper.testConsumer()) .assertFailureAndMessage(NullPointerException.class, "The zipper returned a null value"); } + + @Test + public void dispose2() { + TestHelper.checkDisposed(Maybe.zipArray(v -> v, MaybeSubject.create(), MaybeSubject.create())); + } + + @Test + public void bothComplete() { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Maybe m1 = new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + ref1.set(observer); + } + }; + Maybe m2 = new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + ref2.set(observer); + } + }; + + TestObserver to = Maybe.zipArray(v -> v, m1, m2) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref2.get().onSubscribe(Disposable.empty()); + + ref1.get().onComplete(); + ref2.get().onComplete(); + + to.assertResult(); + } + + @Test + public void bothSucceed() { + Maybe.zipArray(v -> Arrays.asList(v), Maybe.just(1), Maybe.just(2)) + .test() + .assertResult(Arrays.asList(1, 2)); + } + + @Test + public void oneSourceOnly() { + Maybe.zipArray(v -> Arrays.asList(v), Maybe.just(1)) + .test() + .assertResult(Arrays.asList(1)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToIteratorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToIteratorTest.java index 4b6e2bc5cd..2eb25e0dab 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToIteratorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToIteratorTest.java @@ -23,7 +23,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.operators.observable.BlockingObservableIterable.BlockingObservableIterator; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -131,4 +131,13 @@ public void run() { assertFalse(it.hasNext()); } + + @Test(expected = TestException.class) + public void errorAfterDispose() { + Iterator it = Observable.error(new TestException()).blockingIterable().iterator(); + + ((Disposable)it).dispose(); + + it.hasNext(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllTest.java index 293607d66b..9883764f28 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllTest.java @@ -364,4 +364,14 @@ public boolean test(Integer v) throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservableToSingle(o -> o.all(v -> true)); + } + + @Test + public void doubleOnSubscribeObservable() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.all(v -> true).toObservable()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTest.java index af4e7a9415..30c9efd221 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTest.java @@ -37,7 +37,7 @@ import io.reactivex.rxjava3.observers.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.*; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.TestHelper; public class ObservableBufferTest extends RxJavaTest { @@ -1788,4 +1788,31 @@ public List get() throws Exception { .assertFailure(TestException.class) ; } + + @Test + public void timedUnboundedCancelUpfront() { + Observable.never() + .buffer(1, TimeUnit.SECONDS) + .test(true) + .assertEmpty(); + } + + @Test + public void boundaryCloseCompleteRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + BehaviorSubject bs = BehaviorSubject.createDefault(1); + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = bs + .buffer(BehaviorSubject.createDefault(0), v -> ps) + .test(); + + TestHelper.race( + () -> bs.onComplete(), + () -> ps.onComplete() + ); + + to.assertResult(Arrays.asList(1)); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCacheTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCacheTest.java index 8f4440e41b..3ad0f0ef67 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCacheTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCacheTest.java @@ -20,12 +20,12 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; -import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.observers.TestObserver; @@ -341,4 +341,18 @@ public Object call() throws Exception { assertEquals(1, call.get()); } + + @Test + public void addRemoveRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + Observable o = Observable.never().cache(); + + TestObserver to = o.test(); + + TestHelper.race( + () -> to.dispose(), + () -> o.test() + ); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatestTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatestTest.java index e304a27a33..d8f35d38d1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatestTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatestTest.java @@ -19,7 +19,7 @@ import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; import org.junit.Test; import org.mockito.*; @@ -1226,4 +1226,76 @@ public Integer apply(Object[] t) throws Throwable { .test() .assertResult(2); } + + @Test + public void onCompleteDisposeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + TestObserver to = new TestObserver<>(); + PublishSubject ps = PublishSubject.create(); + + Observable.combineLatest(ps, Observable.never(), (a, b) -> a) + .subscribe(to); + + TestHelper.race(() -> ps.onComplete(), () -> to.dispose()); + } + } + + @Test + public void onErrorDisposeDelayErrorRace() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestException ex = new TestException(); + + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + TestObserverEx to = new TestObserverEx<>(); + AtomicReference> ref = new AtomicReference<>(); + Observable o = new Observable() { + @Override + public void subscribeActual(Observer observer) { + ref.set(observer); + } + }; + + Observable.combineLatestDelayError(Arrays.asList(o, Observable.never()), (a) -> a) + .subscribe(to); + + ref.get().onSubscribe(Disposable.empty()); + + TestHelper.race(() -> ref.get().onError(ex), () -> to.dispose()); + + if (to.errors().isEmpty()) { + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } + } + }); + } + + @Test + public void doneButNotEmpty() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = Observable.combineLatest(ps1, ps2, (a, b) -> a + b) + .doOnNext(v -> { + if (v == 2) { + ps2.onNext(3); + ps2.onComplete(); + ps1.onComplete(); + } + }) + .test(); + + ps1.onNext(1); + ps2.onNext(1); + + to.assertResult(2, 4); + } + + @Test + public void iterableNullPublisher() { + Observable.combineLatest(Arrays.asList(Observable.never(), null), (a) -> a) + .test() + .assertFailure(NullPointerException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEagerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEagerTest.java index 054ab91d8f..079a8c8a47 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEagerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEagerTest.java @@ -1078,4 +1078,13 @@ public void observerDelayErrorMaxConcurrency() { .test() .assertFailure(TestException.class, 1, 2, 3, 4, 5); } + + @Test + public void innerFusionRejected() { + Observable.just(1) + .hide() + .concatMapEager(v -> TestHelper.rejectObservableFusion()) + .test() + .assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java index a82553b9a4..48ab5a2fc1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java @@ -20,14 +20,16 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; -import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.observers.*; @@ -1049,4 +1051,70 @@ public Observable apply(Integer v) throws Throwable { } }); } + + @Test + public void fusionRejected() { + TestObserverEx to = new TestObserverEx<>(); + + TestHelper.rejectObservableFusion() + .concatMap(v -> Observable.never(), 2, ImmediateThinScheduler.INSTANCE) + .subscribe(to); + } + + @Test + public void fusionRejectedDelayErrorr() { + TestObserverEx to = new TestObserverEx<>(); + + TestHelper.rejectObservableFusion() + .concatMapDelayError(v -> Observable.never(), true, 2, ImmediateThinScheduler.INSTANCE) + .subscribe(to); + } + + @Test + public void scalarInnerJustDisposeDelayError() { + TestObserver to = new TestObserver<>(); + + Observable.just(1) + .hide() + .concatMapDelayError(v -> Observable.fromCallable(() -> { + to.dispose(); + return 1; + }), true, 2, ImmediateThinScheduler.INSTANCE) + .subscribe(to); + + to.assertEmpty(); + } + + static final class EmptyDisposingObservable extends Observable + implements Supplier { + final TestObserver to; + EmptyDisposingObservable(TestObserver to) { + this.to = to; + } + + @Override + protected void subscribeActual(@NonNull Observer observer) { + EmptyDisposable.complete(observer); + } + + @Override + public @NonNull Object get() throws Throwable { + to.dispose(); + return null; + } + } + + @Test + public void scalarInnerEmptyDisposeDelayError() { + TestObserver to = new TestObserver<>(); + + Observable.just(1) + .hide() + .concatMapDelayError(v -> new EmptyDisposingObservable(to), + true, 2, ImmediateThinScheduler.INSTANCE + ) + .subscribe(to); + + to.assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapTest.java index a5d2a6aa4c..f8aa2cf42b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapTest.java @@ -22,10 +22,11 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.operators.observable.ObservableConcatMapSchedulerTest.EmptyDisposingObservable; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.subjects.*; @@ -565,4 +566,108 @@ public Observable apply(Integer v) throws Throwable { } }); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.concatMap(v -> Observable.never())); + } + + @Test + public void doubleOnSubscribeDelayError() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.concatMapDelayError(v -> Observable.never())); + } + + @Test + public void scalarXMap() { + Observable.fromCallable(() -> 1) + .concatMap(v -> Observable.just(2).hide()) + .test() + .assertResult(2); + } + + @Test + public void rejectedFusion() { + TestHelper.rejectObservableFusion() + .concatMap(v -> Observable.never()) + .test(); + } + + @Test + public void rejectedFusionDelayError() { + TestHelper.rejectObservableFusion() + .concatMapDelayError(v -> Observable.never()) + .test(); + } + + @Test + public void asyncFusedDelayError() { + UnicastSubject uc = UnicastSubject.create(); + + TestObserver to = uc.concatMapDelayError(v -> Observable.just(v).hide()) + .test(); + + uc.onNext(1); + uc.onComplete(); + + to.assertResult(1); + } + + @Test + public void scalarInnerJustDelayError() { + Observable.just(1) + .hide() + .concatMapDelayError(v -> Observable.just(v)) + .test() + .assertResult(1); + } + + @Test + public void scalarInnerEmptyDelayError() { + Observable.just(1) + .hide() + .concatMapDelayError(v -> Observable.empty()) + .test() + .assertResult(); + } + + @Test + public void scalarInnerJustDisposeDelayError() { + TestObserver to = new TestObserver<>(); + + Observable.just(1) + .hide() + .concatMapDelayError(v -> Observable.fromCallable(() -> { + to.dispose(); + return 1; + })) + .subscribe(to); + + to.assertEmpty(); + } + + @Test + public void scalarInnerEmptyDisposeDelayError() { + TestObserver to = new TestObserver<>(); + + Observable.just(1) + .hide() + .concatMapDelayError(v -> new EmptyDisposingObservable(to)) + .subscribe(to); + + to.assertEmpty(); + } + + @Test + public void delayErrorInnerActive() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = Observable.range(1, 5) + .hide() + .concatMapDelayError(v -> ps) + .test(); + + ps.onComplete(); + + to.assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java index a0faf3179b..9f25879a60 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java @@ -17,13 +17,16 @@ import java.io.IOException; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Cancellable; +import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.testsupport.*; @@ -654,4 +657,96 @@ public void subscribe(ObservableEmitter emitter) throws Exception { } }).test().assertEmpty(); } + + @Test + public void emptySerialized() { + Observable.create(emitter -> emitter.serialize().onComplete()) + .test() + .assertResult(); + } + + @Test + public void serializedDisposedBeforeOnNext() { + TestObserver to = new TestObserver<>(); + + Observable.create(emitter -> { + to.dispose(); + emitter.serialize().onNext(1); + }) + .subscribe(to); + + to.assertEmpty(); + } + + @Test + public void serializedOnNextAfterComplete() { + TestObserver to = new TestObserver<>(); + + Observable.create(emitter -> { + emitter = emitter.serialize(); + + emitter.onComplete(); + emitter.onNext(1); + }) + .subscribe(to); + + to.assertResult(); + } + + @Test + public void serializedEnqueueAndDrainRace() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestObserver to = new TestObserver<>(); + AtomicReference> ref = new AtomicReference<>(); + + CountDownLatch cdl = new CountDownLatch(1); + + Observable.create(emitter -> { + emitter = emitter.serialize(); + ref.set(emitter); + emitter.onNext(1); + }) + .doOnNext(v -> { + if (v == 1) { + TestHelper.raceOther(() -> { + ref.get().onNext(2); + }, cdl); + ref.get().onNext(3); + } + }) + .subscribe(to); + + cdl.await(); + + to.assertValueCount(3); + } + } + + @Test + public void serializedDrainDoneButNotEmpty() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestObserver to = new TestObserver<>(); + AtomicReference> ref = new AtomicReference<>(); + + CountDownLatch cdl = new CountDownLatch(1); + + Observable.create(emitter -> { + emitter = emitter.serialize(); + ref.set(emitter); + emitter.onNext(1); + }) + .doOnNext(v -> { + if (v == 1) { + TestHelper.raceOther(() -> { + ref.get().onNext(2); + ref.get().onComplete(); + }, cdl); + ref.get().onNext(3); + } + }) + .subscribe(to); + + cdl.await(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTest.java index d59431a677..4b3a64def7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTest.java @@ -517,4 +517,9 @@ public ObservableSource apply(Object o) { } }).subscribe(); } + + @Test + public void doubleOnSubscribeTime() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.debounce(1, TimeUnit.SECONDS)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerializeTest.java index 8c7b6017ab..bc041cb1be 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerializeTest.java @@ -221,16 +221,17 @@ protected void subscribeActual(Observer> observer) } @Test + @SuppressWarnings("unchecked") public void nonNotificationInstanceAfterDispose() { - new Observable>() { + new Observable() { @Override - protected void subscribeActual(Observer> observer) { + protected void subscribeActual(Observer observer) { observer.onSubscribe(Disposable.empty()); observer.onNext(Notification.createOnComplete()); - observer.onNext(Notification.createOnNext(1)); + observer.onNext(1); } } - .dematerialize(Functions.>identity()) + .dematerialize(v -> (Notification)v) .test() .assertResult(); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinallyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinallyTest.java index 6328a1ba4f..d3a11c186d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinallyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinallyTest.java @@ -519,4 +519,16 @@ public void run() throws Exception { assertEquals(Arrays.asList("onNext", "onComplete", "finally"), list); } + @Test + public void fusionRejected() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ANY); + + TestHelper.rejectObservableFusion() + .doFinally(() -> { }) + .subscribeWith(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableTest.java index 95baba1448..169186da49 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableTest.java @@ -16,7 +16,7 @@ import static org.junit.Assert.*; import java.util.List; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import org.junit.Test; @@ -506,4 +506,59 @@ public Completable apply(Integer v) throws Throwable { } }); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.flatMapCompletable(v -> Completable.never()).toObservable()); + } + + @Test + public void doubleOnSubscribeCompletable() { + TestHelper.checkDoubleOnSubscribeObservableToCompletable(o -> o.flatMapCompletable(v -> Completable.never())); + } + + @Test + public void cancelWhileMapping() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishSubject ps1 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + + ps1.flatMapCompletable(v -> { + TestHelper.raceOther(() -> { + to.dispose(); + }, cdl); + return Completable.complete(); + }) + .toObservable() + .subscribe(to); + + ps1.onNext(1); + + cdl.await(); + } + } + + @Test + public void cancelWhileMappingCompletable() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishSubject ps1 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + + ps1.flatMapCompletable(v -> { + TestHelper.raceOther(() -> { + to.dispose(); + }, cdl); + return Completable.complete(); + }) + .subscribe(to); + + ps1.onNext(1); + + cdl.await(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybeTest.java index a1540dcfca..6bb2b40dc3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybeTest.java @@ -21,14 +21,14 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.*; public class ObservableFlatMapMaybeTest extends RxJavaTest { @@ -483,4 +483,64 @@ public Maybe apply(Integer v) throws Throwable { } }); } + + @Test + public void cancelWhileMapping() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishSubject ps1 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + + ps1.flatMapMaybe(v -> { + TestHelper.raceOther(() -> { + to.dispose(); + }, cdl); + return Maybe.just(1); + }) + .subscribe(to); + + ps1.onNext(1); + + cdl.await(); + } + } + + @Test + public void successCompleteRace() { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + MaybeSubject ms1 = MaybeSubject.create(); + MaybeSubject ms2 = MaybeSubject.create(); + + TestObserver to = Observable.just(1, 2) + .flatMapMaybe(v -> v == 1 ? ms1 : ms2) + .test(); + + TestHelper.race( + () -> ms1.onComplete(), + () -> ms2.onSuccess(1) + ); + + to.assertResult(1); + } + } + + @Test + public void successCompleteRace2() { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + MaybeSubject ms1 = MaybeSubject.create(); + MaybeSubject ms2 = MaybeSubject.create(); + + TestObserver to = Observable.just(1, 2) + .flatMapMaybe(v -> v == 1 ? ms1 : ms2) + .test(); + + TestHelper.race( + () -> ms2.onSuccess(1), + () -> ms1.onComplete() + ); + + to.assertResult(1); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingleTest.java index b990bd407c..5eefbacb4a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingleTest.java @@ -21,14 +21,14 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.*; public class ObservableFlatMapSingleTest extends RxJavaTest { @@ -398,4 +398,69 @@ public Single apply(Integer v) throws Throwable { } }); } + + @Test + public void innerErrorOuterCompleteRace() { + TestException ex = new TestException(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishSubject ps1 = PublishSubject.create(); + SingleSubject ps2 = SingleSubject.create(); + + TestObserver to = ps1.flatMapSingle(v -> ps2) + .test(); + + ps1.onNext(1); + + TestHelper.race( + () -> ps1.onComplete(), + () -> ps2.onError(ex) + ); + + to.assertFailure(TestException.class); + } + } + + @Test + public void cancelWhileMapping() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishSubject ps1 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + + ps1.flatMapSingle(v -> { + TestHelper.raceOther(() -> { + to.dispose(); + }, cdl); + return Single.just(1); + }) + .subscribe(to); + + ps1.onNext(1); + + cdl.await(); + } + } + + @Test + public void onNextDrainCancel() { + SingleSubject ss1 = SingleSubject.create(); + SingleSubject ss2 = SingleSubject.create(); + + TestObserver to = new TestObserver<>(); + + Observable.just(1, 2) + .flatMapSingle(v -> v == 1 ? ss1 : ss2) + .doOnNext(v -> { + if (v == 1) { + ss2.onSuccess(2); + to.dispose(); + } + }) + .subscribe(to); + + ss1.onSuccess(1); + + to.assertValuesOnly(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java index 8dca948c81..165aa9dabf 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java @@ -17,12 +17,14 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; +import java.io.IOException; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import org.junit.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; @@ -1113,4 +1115,129 @@ public void innerErrorsMainCancelled() { assertFalse("Has subscribers?", ps1.hasObservers()); } + + @Test + public void signalsAfterMapperCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onNext(2); + observer.onComplete(); + observer.onError(new IOException()); + } + } + .flatMap(v -> { + throw new TestException(); + }) + .test() + .assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } + + @Test + public void scalarQueueTerminate() { + PublishSubject ps = PublishSubject.create(); + TestObserver to = new TestObserver<>(); + + ps + .flatMap(v -> Observable.just(v)) + .doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + ps.onNext(3); + } + }) + .take(2) + .subscribe(to); + + ps.onNext(1); + + to.assertResult(1, 2); + } + + @Test + public void scalarQueueCompleteMain() throws Exception { + PublishSubject ps = PublishSubject.create(); + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + ps + .flatMap(v -> Observable.just(v)) + .doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + TestHelper.raceOther(() -> ps.onComplete(), cdl); + } + }) + .subscribe(to); + + ps.onNext(1); + + cdl.await(); + to.assertResult(1, 2); + } + + @Test + public void fusedInnerCrash() { + UnicastSubject us = UnicastSubject.create(); + PublishSubject ps = PublishSubject.create(); + + TestObserver to = Observable.just( + ps, + us.map(v -> { + if (v == 10) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.observableStripBoundary()) + ) + .flatMap(v -> v, true) + .doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + us.onNext(10); + } + }) + .test(); + + ps.onNext(1); + ps.onComplete(); + + to.assertFailure(TestException.class, 1, 2); + } + + @Test + public void fusedInnerCrash2() { + UnicastSubject us = UnicastSubject.create(); + PublishSubject ps = PublishSubject.create(); + + TestObserver to = Observable.just( + us.map(v -> { + if (v == 10) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.observableStripBoundary()) + , ps + ) + .flatMap(v -> v, true) + .doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + us.onNext(10); + } + }) + .test(); + + ps.onNext(1); + ps.onComplete(); + + to.assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterableTest.java index 2fdf6b24f3..8a2de4cf48 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterableTest.java @@ -96,4 +96,9 @@ public void remove() { assertEquals(1, counter.get()); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.flatMapIterable(v -> Collections.singletonList(v))); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCompletableTest.java index 6cdc0108d6..68c9f10292 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCompletableTest.java @@ -24,7 +24,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -192,4 +192,16 @@ public void syncFusedRejected() throws Throwable { verify(action).run(); } + + @Test + public void disposed() { + TestHelper.checkDisposed(Observable.fromCompletable(Completable.never())); + } + + @Test + public void upstream() { + Observable o = Observable.fromCompletable(Completable.never()); + assertTrue(o instanceof HasUpstreamCompletableSource); + assertSame(Completable.never(), ((HasUpstreamCompletableSource)o).source()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterableTest.java index a3c546933a..ec6b70e31b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterableTest.java @@ -345,4 +345,28 @@ public void onComplete() { } }); } + + @Test + public void disposeAfterHasNext() { + TestObserver to = new TestObserver<>(); + + Observable.fromIterable(() -> new Iterator() { + int count; + @Override + public boolean hasNext() { + if (count++ == 2) { + to.dispose(); + return false; + } + return true; + } + + @Override + public Integer next() { + return 1; + } + }) + .subscribeWith(to) + .assertValuesOnly(1, 1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerateTest.java index 19735a3ad4..f6beca1800 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerateTest.java @@ -194,4 +194,17 @@ public void accept(Emitter e) throws Exception { .test() .assertResult(); } + + @Test + public void onNextAfterOnComplete() { + Observable.generate(new Consumer>() { + @Override + public void accept(Emitter e) throws Exception { + e.onComplete(); + e.onNext(1); + } + }) + .test() + .assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java index 28d3f2d643..22a56a3356 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java @@ -27,14 +27,14 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observables.GroupedObservable; import io.reactivex.rxjava3.observers.*; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.*; public class ObservableGroupByTest extends RxJavaTest { @@ -1672,4 +1672,68 @@ public void accept(GroupedObservable g) throws Throwable { to2.assertFailure(TestException.class, 1); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.groupBy(v -> v)); + } + + @Test + public void nullKeyDisposeGroup() { + Observable.just(1) + .groupBy(v -> null) + .flatMap(v -> v.take(1)) + .test() + .assertResult(1); + } + + @Test + public void groupSubscribeOnNextRace() throws Throwable { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + BehaviorSubject bs = BehaviorSubject.createDefault(1); + CountDownLatch cdl = new CountDownLatch(1); + + bs.groupBy(v -> 1) + .doOnNext(g -> { + TestHelper.raceOther(() -> { + g.test(); + }, cdl); + }) + .test(); + + cdl.await(); + } + } + + @Test + public void abandonedGroupDispose() { + AtomicReference> ref = new AtomicReference<>(); + + Observable.just(1) + .groupBy(v -> 1) + .doOnNext(ref::set) + .test(); + + ref.get().take(1).test().assertResult(1); + } + + @Test + public void delayErrorCompleteMoreWorkInGroup() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.groupBy(v -> 1, true) + .flatMap(g -> g.doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + ps.onComplete(); + } + }) + ) + .test() + ; + + ps.onNext(1); + + to.assertResult(1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java index 5998e37a64..3f01c05cb4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java @@ -726,4 +726,42 @@ public void leftRightEndState() { verify(js).innerClose(false, o); } + + @Test + public void disposeAfterOnNext() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + + ps1.groupJoin(ps2, v -> Observable.never(), v -> Observable.never(), (a, b) -> a) + .doOnNext(v -> { + to.dispose(); + }) + .subscribe(to); + + ps2.onNext(1); + ps1.onNext(1); + } + + @Test + public void completeWithMoreWork() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + + ps1.groupJoin(ps2, v -> Observable.never(), v -> Observable.never(), (a, b) -> a) + .doOnNext(v -> { + if (v == 1) { + ps2.onNext(2); + ps1.onComplete(); + ps2.onComplete(); + } + }) + .subscribe(to); + + ps2.onNext(1); + ps1.onNext(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRangeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRangeTest.java index 95f26637bf..db42957fa5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRangeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRangeTest.java @@ -85,4 +85,12 @@ public void cancel() { .test() .assertResult(0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L); } + + @Test + public void takeSameAsRange() { + Observable.intervalRange(0, 2, 1, 1, TimeUnit.MILLISECONDS, Schedulers.trampoline()) + .take(2) + .test() + .assertResult(0L, 1L); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java index aa17361026..54c2822395 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java @@ -446,4 +446,27 @@ public Integer apply(Integer a, Integer b) throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void bothTerminateWithWorkRemaining() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = ps1.join( + ps2, + v -> Observable.never(), + v -> Observable.never(), + (a, b) -> a + b) + .doOnNext(v -> { + ps1.onComplete(); + ps2.onNext(2); + ps2.onComplete(); + }) + .test(); + + ps1.onNext(0); + ps2.onNext(1); + + to.assertComplete(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java index d9b98de8f9..09b0bf0cfe 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java @@ -45,7 +45,25 @@ public class ObservableRefCountTest extends RxJavaTest { @Test - public void refCountAsync() { + public void refCountAsync() throws InterruptedException { + // Flaky + for (int i = 0; i < 10; i++) { + try { + refCountAsyncActual(); + return; + } catch (AssertionError ex) { + if (i == 9) { + throw ex; + } + Thread.sleep((int)(200 * (Math.random() * 10 + 1))); + } + } + } + + /** + * Tries to coordinate async counting but it is flaky due to the low 10s of milliseconds. + */ + void refCountAsyncActual() { final AtomicInteger subscribeCount = new AtomicInteger(); final AtomicInteger nextCount = new AtomicInteger(); Observable r = Observable.interval(0, 25, TimeUnit.MILLISECONDS) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java index 9b31f51e8d..cbaf961149 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java @@ -1698,4 +1698,30 @@ public void accept(byte[] v) throws Exception { Assert.fail("Bounded Replay Leak check: Memory leak detected: " + (initial / 1024.0 / 1024.0) + " -> " + after.get() / 1024.0 / 1024.0); } - }} + } + + @Test(expected = TestException.class) + public void connectDisposeCrash() { + ConnectableObservable co = Observable.never().replay(); + + co.connect(); + + co.connect(d -> { throw new TestException(); }); + } + + @Test + public void resetWhileNotConnectedIsNoOp() { + ConnectableObservable co = Observable.never().replay(); + + co.reset(); + } + + @Test + public void resetWhileActiveIsNoOp() { + ConnectableObservable co = Observable.never().replay(); + + co.connect(); + + co.reset(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTest.java index 0861fc2596..3e842e67ed 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTest.java @@ -439,4 +439,8 @@ public Observable apply(Observable o) }); } + @Test + public void doubleOnSubscribeObservable() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.sample(Observable.never())); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMapTest.java index bffd568b70..4bfee7eb01 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMapTest.java @@ -21,6 +21,7 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; +import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.operators.observable.ObservableScalarXMap.ScalarDisposable; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -234,4 +235,13 @@ public void run() { TestHelper.race(r1, r2); } } + + @Test + public void scalarDisposbleWrongFusion() { + TestObserver to = new TestObserver<>(); + final ScalarDisposable sd = new ScalarDisposable<>(to, 1); + to.onSubscribe(sd); + + assertEquals(QueueFuseable.NONE, sd.requestFusion(QueueFuseable.ASYNC)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualTest.java index 3d805ec79a..da75e3ee94 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualTest.java @@ -336,4 +336,77 @@ public void run() { to.assertEmpty(); } } + + @Test + public void firstCompletesBeforeSecond() { + Observable.sequenceEqual(Observable.just(1), Observable.empty()) + .test() + .assertResult(false); + } + + @Test + public void secondCompletesBeforeFirst() { + Observable.sequenceEqual(Observable.empty(), Observable.just(1)) + .test() + .assertResult(false); + } + + @Test + public void bothEmpty() { + Observable.sequenceEqual(Observable.empty(), Observable.empty()) + .test() + .assertResult(true); + } + + @Test + public void bothJust() { + Observable.sequenceEqual(Observable.just(1), Observable.just(1)) + .test() + .assertResult(true); + } + + @Test + public void bothCompleteWhileComparing() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = Observable.sequenceEqual(ps1, ps2, (a, b) -> { + ps1.onNext(1); + ps1.onComplete(); + + ps2.onNext(1); + ps2.onComplete(); + return a.equals(b); + }) + .test() + ; + + ps1.onNext(0); + ps2.onNext(0); + + to.assertResult(true); + } + + @Test + public void bothCompleteWhileComparingAsObservable() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = Observable.sequenceEqual(ps1, ps2, (a, b) -> { + ps1.onNext(1); + ps1.onComplete(); + + ps2.onNext(1); + ps2.onComplete(); + return a.equals(b); + }) + .toObservable() + .test() + ; + + ps1.onNext(0); + ps2.onNext(0); + + to.assertResult(true); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimedTest.java index d0a1b24fa2..650575e7f3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimedTest.java @@ -194,7 +194,7 @@ public ObservableSource apply(Observable o) throws Exception { } @Test - public void onNextDisposeRace() { + public void onCompleteDisposeRace() { TestScheduler scheduler = new TestScheduler(); for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final PublishSubject ps = PublishSubject.create(); @@ -219,6 +219,32 @@ public void run() { } } + @Test + public void onCompleteDisposeDelayErrorRace() { + TestScheduler scheduler = new TestScheduler(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + final PublishSubject ps = PublishSubject.create(); + + final TestObserver to = ps.skipLast(1, TimeUnit.DAYS, scheduler, true).test(); + + Runnable r1 = new Runnable() { + @Override + public void run() { + ps.onComplete(); + } + }; + + Runnable r2 = new Runnable() { + @Override + public void run() { + to.dispose(); + } + }; + + TestHelper.race(r1, r2); + } + } + @Test public void errorDelayed() { Observable.error(new TestException()) @@ -236,4 +262,177 @@ public void take() { .awaitDone(5, TimeUnit.SECONDS) .assertResult(1); } + + @Test + public void onNextDisposeRace() { + TestScheduler scheduler = new TestScheduler(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + final PublishSubject ps = PublishSubject.create(); + + final TestObserver to = ps.skipLast(1, TimeUnit.DAYS, scheduler).test(); + + Runnable r1 = new Runnable() { + @Override + public void run() { + ps.onNext(1); + } + }; + + Runnable r2 = new Runnable() { + @Override + public void run() { + to.dispose(); + } + }; + + TestHelper.race(r1, r2); + } + } + + @Test + public void onNextOnCompleteDisposeDelayErrorRace() { + TestScheduler scheduler = new TestScheduler(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + final PublishSubject ps = PublishSubject.create(); + + final TestObserver to = ps.skipLast(1, TimeUnit.DAYS, scheduler, true).test(); + + Runnable r1 = new Runnable() { + @Override + public void run() { + ps.onNext(1); + ps.onComplete(); + } + }; + + Runnable r2 = new Runnable() { + @Override + public void run() { + to.dispose(); + } + }; + + TestHelper.race(r1, r2); + } + } + + @Test + public void skipLastTimedDelayError() { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject source = PublishSubject.create(); + + // FIXME the timeunit now matters due to rounding + Observable result = source.skipLast(1000, TimeUnit.MILLISECONDS, scheduler, true); + + Observer o = TestHelper.mockObserver(); + + result.subscribe(o); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + scheduler.advanceTimeBy(500, TimeUnit.MILLISECONDS); + + source.onNext(4); + source.onNext(5); + source.onNext(6); + + scheduler.advanceTimeBy(950, TimeUnit.MILLISECONDS); + source.onComplete(); + + InOrder inOrder = inOrder(o); + inOrder.verify(o).onNext(1); + inOrder.verify(o).onNext(2); + inOrder.verify(o).onNext(3); + inOrder.verify(o, never()).onNext(4); + inOrder.verify(o, never()).onNext(5); + inOrder.verify(o, never()).onNext(6); + inOrder.verify(o).onComplete(); + inOrder.verifyNoMoreInteractions(); + + verify(o, never()).onError(any(Throwable.class)); + } + + @Test + public void skipLastTimedErrorBeforeTimeDelayError() { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject source = PublishSubject.create(); + + Observable result = source.skipLast(1, TimeUnit.SECONDS, scheduler, true); + + Observer o = TestHelper.mockObserver(); + + result.subscribe(o); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + source.onError(new TestException()); + + scheduler.advanceTimeBy(1050, TimeUnit.MILLISECONDS); + + verify(o).onError(any(TestException.class)); + + verify(o, never()).onComplete(); + verify(o, never()).onNext(any()); + } + + @Test + public void skipLastTimedCompleteBeforeTimeDelayError() { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject source = PublishSubject.create(); + + Observable result = source.skipLast(1, TimeUnit.SECONDS, scheduler, true); + + Observer o = TestHelper.mockObserver(); + + result.subscribe(o); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + scheduler.advanceTimeBy(500, TimeUnit.MILLISECONDS); + + source.onComplete(); + + InOrder inOrder = inOrder(o); + inOrder.verify(o).onComplete(); + inOrder.verifyNoMoreInteractions(); + + verify(o, never()).onNext(any()); + verify(o, never()).onError(any(Throwable.class)); + } + + @Test + public void skipLastTimedWhenAllElementsAreValidDelayError() { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject source = PublishSubject.create(); + + Observable result = source.skipLast(1, TimeUnit.MILLISECONDS, scheduler, true); + + Observer o = TestHelper.mockObserver(); + + result.subscribe(o); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + scheduler.advanceTimeBy(500, TimeUnit.MILLISECONDS); + + source.onComplete(); + + InOrder inOrder = inOrder(o); + inOrder.verify(o).onNext(1); + inOrder.verify(o).onNext(2); + inOrder.verify(o).onNext(3); + inOrder.verify(o).onComplete(); + inOrder.verifyNoMoreInteractions(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchTest.java index 2a1b1dad6e..a68dd46f08 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchTest.java @@ -24,10 +24,11 @@ import org.junit.*; import org.mockito.InOrder; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -1250,4 +1251,151 @@ public Observable apply(Integer v) .test() .assertResult(10, 20); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(f -> f.switchMap(v -> Observable.never())); + } + + @Test + public void mainCompleteCancelRace() { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + AtomicReference> ref = new AtomicReference<>(); + Observable o = new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + ref.set(observer); + } + }; + + TestObserver to = o.switchMap(v -> Observable.never()) + .test(); + + ref.get().onSubscribe(Disposable.empty()); + + TestHelper.race( + () -> ref.get().onComplete(), + () -> to.dispose() + ); + } + } + + @Test + public void mainCompleteInnerErrorRace() { + TestException ex = new TestException(); + + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + AtomicReference> ref1 = new AtomicReference<>(); + Observable o1 = new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + ref1.set(observer); + } + }; + AtomicReference> ref2 = new AtomicReference<>(); + Observable o2 = new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + ref2.set(observer); + } + }; + + o1.switchMap(v -> o2) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + ref2.get().onSubscribe(Disposable.empty()); + + TestHelper.race( + () -> ref1.get().onComplete(), + () -> ref2.get().onError(ex) + ); + } + } + + @Test + public void innerNoSubscriptionYet() { + AtomicReference> ref1 = new AtomicReference<>(); + Observable o1 = new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + ref1.set(observer); + } + }; + AtomicReference> ref2 = new AtomicReference<>(); + Observable o2 = new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + ref2.set(observer); + } + }; + + o1.switchMap(v -> o2) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + ref1.get().onComplete(); + } + + @Test + public void switchDuringOnNext() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.switchMap(v -> Observable.range(v, 5)) + .doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + } + }) + .test(); + + ps.onNext(1); + + to + .assertValuesOnly(1, 2, 3, 4, 5, 6); + } + + @Test + public void mainCompleteWhileInnerActive() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = ps1.switchMapDelayError(v -> ps2) + .test(); + + ps1.onNext(1); + ps1.onComplete(); + + ps2.onComplete(); + + to.assertResult(); + } + + @Test + public void innerIgnoresCancelAndErrors() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps + .switchMap(v -> { + if (v == 1) { + return Observable.unsafeCreate(s -> { + s.onSubscribe(Disposable.empty()); + ps.onNext(2); + s.onError(new TestException()); + }); + } + return Observable.never(); + }) + .test(); + + ps.onNext(1); + + to.assertEmpty(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimedTest.java index f8d17474c2..3c671695ff 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimedTest.java @@ -300,4 +300,9 @@ public void lastWindowIsFixedInTime() { to.assertResult(1, 2, 3, 4); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.takeLast(1, TimeUnit.SECONDS)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTest.java index c8eedc72bd..4aa693978e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTest.java @@ -16,16 +16,14 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -import java.util.List; import java.util.concurrent.TimeUnit; -import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import org.mockito.InOrder; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.TestScheduler; import io.reactivex.rxjava3.subjects.PublishSubject; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -167,28 +165,7 @@ public void dispose() { } @Test - public void badSource() { - List errors = TestHelper.trackPluginErrors(); - try { - new Observable() { - @Override - protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposable.empty()); - observer.onNext(1); - observer.onNext(2); - observer.onComplete(); - observer.onNext(3); - observer.onError(new TestException()); - observer.onComplete(); - } - } - .throttleFirst(1, TimeUnit.DAYS) - .test() - .assertResult(1); - - TestHelper.assertUndeliverable(errors, 0, TestException.class); - } finally { - RxJavaPlugins.reset(); - } + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.throttleFirst(1, TimeUnit.SECONDS)); } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOnTest.java index da58ffdb2a..59b1a16da2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOnTest.java @@ -26,6 +26,7 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Action; +import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.*; @@ -265,4 +266,9 @@ protected void subscribeActual(Observer observer) { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.unsubscribeOn(ImmediateThinScheduler.INSTANCE)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java index af67c36bf2..ee0c112a25 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java @@ -15,16 +15,17 @@ import static org.junit.Assert.*; +import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.*; -import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -530,4 +531,174 @@ public void mainError() { .test() .assertFailure(TestException.class); } -} + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.window(Observable.never(), v -> Observable.never())); + } + + @Test + public void openError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestException ex1 = new TestException(); + TestException ex2 = new TestException(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Observable o1 = Observable.unsafeCreate(ref1::set); + Observable o2 = Observable.unsafeCreate(ref2::set); + + TestObserver> to = BehaviorSubject.createDefault(1) + .window(o1, v -> o2) + .doOnNext(w -> w.test()) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + ref2.get().onSubscribe(Disposable.empty()); + + TestHelper.race( + () -> ref1.get().onError(ex1), + () -> ref2.get().onError(ex2) + ); + + to.assertError(RuntimeException.class); + + if (!errors.isEmpty()) { + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } + + errors.clear(); + } + }); + } + + @Test + public void closeError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Observable o1 = Observable.unsafeCreate(ref1::set); + Observable o2 = Observable.unsafeCreate(ref2::set); + + TestObserver to = BehaviorSubject.createDefault(1) + .window(o1, v -> o2) + .flatMap(v -> v) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + ref2.get().onSubscribe(Disposable.empty()); + + ref2.get().onError(new TestException()); + ref2.get().onError(new TestException()); + + to.assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void upstreamFailsBeforeFirstWindow() { + Observable.error(new TestException()) + .window(Observable.never(), v -> Observable.never()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void windowOpenMainCompletes() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishSubject ps = PublishSubject.create(); + Observable o1 = Observable.unsafeCreate(ref1::set); + + AtomicInteger counter = new AtomicInteger(); + + TestObserver> to = ps + .window(o1, v -> Observable.never()) + .doOnNext(w -> { + if (counter.getAndIncrement() == 0) { + ref1.get().onNext(2); + ps.onNext(1); + ps.onComplete(); + } + w.test(); + }) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + + to.assertComplete(); + } + + @Test + public void windowOpenMainError() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishSubject ps = PublishSubject.create(); + Observable o1 = Observable.unsafeCreate(ref1::set); + + AtomicInteger counter = new AtomicInteger(); + + TestObserver> to = ps + .window(o1, v -> Observable.never()) + .doOnNext(w -> { + if (counter.getAndIncrement() == 0) { + ref1.get().onNext(2); + ps.onNext(1); + ps.onError(new TestException()); + } + w.test(); + }) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + + to.assertError(TestException.class); + } + + @Test + public void windowOpenIgnoresDispose() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishSubject ps = PublishSubject.create(); + Observable o1 = Observable.unsafeCreate(ref1::set); + + TestObserver> to = ps + .window(o1, v -> Observable.never()) + .take(1) + .doOnNext(w -> { + w.test(); + }) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + ref1.get().onNext(2); + + to.assertValueCount(1); + } + + @Test + public void mainIgnoresCancelBeforeOnError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Observable.unsafeCreate(s -> { + s.onSubscribe(Disposable.empty()); + s.onNext(1); + s.onError(new IOException()); + }) + .window(BehaviorSubject.createDefault(1), v -> Observable.error(new TestException())) + .doOnNext(w -> w.test()) + .test() + .assertError(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } +} \ No newline at end of file diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java index 13f662c88e..56ee06ff24 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java @@ -1099,4 +1099,26 @@ public void accept(Observable v) throws Throwable { inner.get().test().assertResult(); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.window(1, TimeUnit.SECONDS)); + } + + @Test + public void timedBoundarySignalAndDisposeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(1, TimeUnit.MINUTES, scheduler, 1) + .test(); + + TestHelper.race( + () -> ps.onNext(1), + () -> to.dispose() + ); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayTest.java index 555b53e30a..619a6c1241 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayTest.java @@ -19,18 +19,18 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; -import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.*; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.TestHelper; public class SingleDelayTest extends RxJavaTest { @@ -270,4 +270,16 @@ public Single apply(Single s) throws Exception { }); } + + @Test + public void withPublisherDoubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowableToSingle( + f -> SingleSubject.create().delaySubscription(f)); + } + + @Test + public void withObservableDoubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservableToSingle( + o -> SingleSubject.create().delaySubscription(o)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleEqualsTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleEqualsTest.java index 6c1bf14f30..14bd931d76 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleEqualsTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleEqualsTest.java @@ -24,6 +24,34 @@ public class SingleEqualsTest extends RxJavaTest { + @Test + public void bothSucceedEqual() { + Single.sequenceEqual(Single.just(1), Single.just(1)) + .test() + .assertResult(true); + } + + @Test + public void bothSucceedNotEqual() { + Single.sequenceEqual(Single.just(1), Single.just(2)) + .test() + .assertResult(false); + } + + @Test + public void firstSucceedOtherError() { + Single.sequenceEqual(Single.just(1), Single.error(new TestException())) + .test() + .assertFailure(TestException.class); + } + + @Test + public void firstErrorOtherSucceed() { + Single.sequenceEqual(Single.error(new TestException()), Single.just(1)) + .test() + .assertFailure(TestException.class); + } + @Test public void bothError() { List errors = TestHelper.trackPluginErrors(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowableTest.java index 4643d61303..d308998e6e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowableTest.java @@ -21,13 +21,14 @@ import org.junit.Test; import org.reactivestreams.Subscription; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.util.CrashingIterable; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -585,4 +586,51 @@ public void run() { TestHelper.race(r1, r2); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingleToFlowable(s -> s.flattenAsFlowable(v -> Collections.emptyList())); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(SingleSubject.create().flattenAsFlowable(v -> Collections.emptyList())); + } + + @Test + public void slowPatchCancelAfterOnNext() { + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + cancel(); + onComplete(); + } + }; + + Single.just(1) + .flattenAsFlowable(v -> Arrays.asList(1, 2)) + .subscribe(ts); + + ts.assertResult(1); + } + + @Test + public void onSuccessRequestRace() { + List list = Arrays.asList(1); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + SingleSubject ss = SingleSubject.create(); + + TestSubscriber ts = ss.flattenAsFlowable(v -> list) + .test(0L); + + TestHelper.race( + () -> ss.onSuccess(1), + () -> ts.request(1) + ); + + ts.assertResult(1); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelperTest.java index 5d5e152c1b..7756ef5783 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelperTest.java @@ -31,8 +31,8 @@ public void utilityClass() { @Test public void noSuchElementCallableEnum() { - assertEquals(1, SingleInternalHelper.NoSuchElementCallable.values().length); - assertNotNull(SingleInternalHelper.NoSuchElementCallable.valueOf("INSTANCE")); + assertEquals(1, SingleInternalHelper.NoSuchElementSupplier.values().length); + assertNotNull(SingleInternalHelper.NoSuchElementSupplier.valueOf("INSTANCE")); } @Test diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeoutTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeoutTest.java index 7c51e8be91..c544fd3ae9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeoutTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeoutTest.java @@ -21,7 +21,9 @@ import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Action; import io.reactivex.rxjava3.observers.TestObserver; @@ -216,9 +218,41 @@ public void run() { public void mainTimedOut() { Single .never() - .timeout(1, TimeUnit.NANOSECONDS) + .timeout(1, TimeUnit.MILLISECONDS) .to(TestHelper.testConsumer()) .awaitDone(5, TimeUnit.SECONDS) - .assertFailureAndMessage(TimeoutException.class, timeoutMessage(1, TimeUnit.NANOSECONDS)); + .assertFailureAndMessage(TimeoutException.class, timeoutMessage(1, TimeUnit.MILLISECONDS)); + } + + @Test + public void mainTimeoutFallbackSuccess() { + Single.never() + .timeout(1, TimeUnit.MILLISECONDS, Single.just(1)) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertResult(1); + } + + @Test + public void timeoutBeforeOnSubscribeFromMain() { + Disposable d = Disposable.empty(); + + new Single() { + @Override + protected void subscribeActual(@NonNull SingleObserver observer) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + observer.onSubscribe(d); + } + } + .timeout(1, TimeUnit.MILLISECONDS, Single.just(1)) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertResult(1); + + assertTrue(d.isDisposed()); } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArrayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArrayTest.java index 50989db3ec..2720cd4559 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArrayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArrayTest.java @@ -26,6 +26,7 @@ import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subjects.SingleSubject; import io.reactivex.rxjava3.testsupport.TestHelper; public class SingleZipArrayTest extends RxJavaTest { @@ -193,4 +194,23 @@ public void singleSourceZipperReturnsNull() { .to(TestHelper.testConsumer()) .assertFailureAndMessage(NullPointerException.class, "The zipper returned a null value"); } + + @Test + public void singleSourceZipperReturnsNull2() { + Single.zipArray(Functions.justFunction(null), Single.just(1), Single.just(2)) + .to(TestHelper.testConsumer()) + .assertFailureAndMessage(NullPointerException.class, "The zipper returned a null value"); + } + + @Test + public void dispose2() { + TestHelper.checkDisposed(Single.zipArray(Functions.justFunction(1), SingleSubject.create(), SingleSubject.create())); + } + + @Test + public void bothSucceed() { + Single.zipArray(a -> Arrays.asList(a), Single.just(1), Single.just(2)) + .test() + .assertResult(Arrays.asList(1, 2)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoSchedulerInternalTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoSchedulerInternalTest.java new file mode 100644 index 0000000000..77f638a2be --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoSchedulerInternalTest.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.schedulers; + +import static org.junit.Assert.*; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.internal.schedulers.IoScheduler.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class IoSchedulerInternalTest extends RxJavaTest { + + @Test + public void expiredQueueEmpty() { + ConcurrentLinkedQueue expire = new ConcurrentLinkedQueue<>(); + CompositeDisposable cd = new CompositeDisposable(); + + CachedWorkerPool.evictExpiredWorkers(expire, cd); + } + + @Test + public void expiredWorkerRemoved() { + ConcurrentLinkedQueue expire = new ConcurrentLinkedQueue<>(); + CompositeDisposable cd = new CompositeDisposable(); + + ThreadWorker tw = new ThreadWorker(new RxThreadFactory("IoExpiryTest")); + + try { + expire.add(tw); + cd.add(tw); + + CachedWorkerPool.evictExpiredWorkers(expire, cd); + + assertTrue(tw.isDisposed()); + assertTrue(expire.isEmpty()); + } finally { + tw.dispose(); + } + } + + @Test + public void noExpiredWorker() { + ConcurrentLinkedQueue expire = new ConcurrentLinkedQueue<>(); + CompositeDisposable cd = new CompositeDisposable(); + + ThreadWorker tw = new ThreadWorker(new RxThreadFactory("IoExpiryTest")); + tw.setExpirationTime(System.nanoTime() + 10_000_000_000L); + + try { + expire.add(tw); + cd.add(tw); + + CachedWorkerPool.evictExpiredWorkers(expire, cd); + + assertFalse(tw.isDisposed()); + assertFalse(expire.isEmpty()); + } finally { + tw.dispose(); + } + } + + @Test + public void expireReuseRace() { + ConcurrentLinkedQueue expire = new ConcurrentLinkedQueue<>(); + CompositeDisposable cd = new CompositeDisposable(); + + ThreadWorker tw = new ThreadWorker(new RxThreadFactory("IoExpiryTest")); + tw.dispose(); + + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + expire.add(tw); + cd.add(tw); + + TestHelper.race( + () -> CachedWorkerPool.evictExpiredWorkers(expire, cd), + () -> expire.remove(tw) + ); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java index f6dfe5e455..ad568225e6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java @@ -14,16 +14,18 @@ package io.reactivex.rxjava3.internal.schedulers; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import org.junit.Test; import io.reactivex.rxjava3.core.Scheduler; import io.reactivex.rxjava3.core.Scheduler.Worker; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.SingleScheduler.ScheduledWorker; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -123,4 +125,20 @@ public void runnableDisposedAsyncTimed() throws Exception { return Schedulers.single(); } + @Test + public void zeroPeriodRejectedExecution() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Scheduler s = RxJavaPlugins.createSingleScheduler(new RxThreadFactory("Test")); + s.shutdown(); + Runnable run = mock(Runnable.class); + + s.schedulePeriodicallyDirect(run, 1, 0, TimeUnit.MILLISECONDS); + + Thread.sleep(100); + + verify(run, never()).run(); + + TestHelper.assertUndeliverable(errors, 0, RejectedExecutionException.class); + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java index e30400214c..f114623bc4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java @@ -20,15 +20,18 @@ import static org.mockito.Mockito.*; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; -import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Scheduler.Worker; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.TrampolineScheduler.*; import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.TestHelper; public class TrampolineSchedulerInternalTest extends RxJavaTest { @@ -209,4 +212,29 @@ public void run() { verify(r, never()).run(); } + + @Test + public void submitAndDisposeNextTask() { + Scheduler.Worker w = Schedulers.trampoline().createWorker(); + + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + Runnable run = mock(Runnable.class); + + AtomicInteger sync = new AtomicInteger(2); + + w.schedule(() -> { + Disposable d = w.schedule(run); + + Schedulers.single().scheduleDirect(() -> { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + d.dispose(); + }); + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + }); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriberTest.java index c84e380701..f8d40aaa14 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriberTest.java @@ -14,15 +14,17 @@ package io.reactivex.rxjava3.internal.subscribers; import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; import org.junit.Test; import org.reactivestreams.Subscription; -import io.reactivex.rxjava3.annotations.Nullable; -import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; -import io.reactivex.rxjava3.internal.subscriptions.ScalarSubscription; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.annotations.*; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.internal.subscriptions.*; +import io.reactivex.rxjava3.testsupport.*; public class BasicFuseableConditionalSubscriberTest extends RxJavaTest { @@ -83,4 +85,172 @@ public Integer poll() throws Exception { fcs.clear(); assertTrue(fcs.isEmpty()); } + + @Test + public void implementationStopsOnSubscribe() { + @SuppressWarnings("unchecked") + ConditionalSubscriber ts = mock(ConditionalSubscriber.class); + + BasicFuseableConditionalSubscriber bfs = new BasicFuseableConditionalSubscriber(ts) { + + @Override + protected boolean beforeDownstream() { + return false; + } + + @Override + public void onNext(@NonNull Integer t) { + ts.onNext(t); + } + + @Override + public int requestFusion(int mode) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean tryOnNext(@NonNull Integer t) { + // TODO Auto-generated method stub + return false; + } + + @Override + public @Nullable Integer poll() throws Throwable { + return null; + } + }; + + bfs.onSubscribe(new BooleanSubscription()); + + verify(ts, never()).onSubscribe(any()); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f + .map(v -> v) + .filter(v -> true) + ); + } + + @Test + public void transitiveBoundaryFusionNone() { + @SuppressWarnings("unchecked") + ConditionalSubscriber ts = mock(ConditionalSubscriber.class); + + BasicFuseableConditionalSubscriber bfs = new BasicFuseableConditionalSubscriber(ts) { + + @Override + protected boolean beforeDownstream() { + return false; + } + + @Override + public void onNext(@NonNull Integer t) { + ts.onNext(t); + } + + @Override + public int requestFusion(int mode) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean tryOnNext(@NonNull Integer t) { + // TODO Auto-generated method stub + return false; + } + + @Override + public @Nullable Integer poll() throws Throwable { + return null; + } + }; + + bfs.onSubscribe(new BooleanSubscription()); + + assertEquals(QueueFuseable.NONE, bfs.transitiveBoundaryFusion(QueueFuseable.ANY)); + } + + @Test + public void transitiveBoundaryFusionAsync() { + @SuppressWarnings("unchecked") + ConditionalSubscriber ts = mock(ConditionalSubscriber.class); + + BasicFuseableConditionalSubscriber bfs = new BasicFuseableConditionalSubscriber(ts) { + + @Override + protected boolean beforeDownstream() { + return false; + } + + @Override + public void onNext(@NonNull Integer t) { + ts.onNext(t); + } + + @Override + public int requestFusion(int mode) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean tryOnNext(@NonNull Integer t) { + // TODO Auto-generated method stub + return false; + } + + @Override + public @Nullable Integer poll() throws Throwable { + return null; + } + }; + + bfs.onSubscribe(EmptySubscription.INSTANCE); + + assertEquals(QueueFuseable.ASYNC, bfs.transitiveBoundaryFusion(QueueFuseable.ANY)); + } + + @Test + public void transitiveBoundaryFusionAsyncBoundary() { + @SuppressWarnings("unchecked") + ConditionalSubscriber ts = mock(ConditionalSubscriber.class); + + BasicFuseableConditionalSubscriber bfs = new BasicFuseableConditionalSubscriber(ts) { + + @Override + protected boolean beforeDownstream() { + return false; + } + + @Override + public void onNext(@NonNull Integer t) { + ts.onNext(t); + } + + @Override + public int requestFusion(int mode) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean tryOnNext(@NonNull Integer t) { + // TODO Auto-generated method stub + return false; + } + + @Override + public @Nullable Integer poll() throws Throwable { + return null; + } + }; + + bfs.onSubscribe(EmptySubscription.INSTANCE); + + assertEquals(QueueFuseable.NONE, bfs.transitiveBoundaryFusion(QueueFuseable.ANY | QueueFuseable.BOUNDARY)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriberTest.java index 87ccd91b92..afc79d2b2d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriberTest.java @@ -17,9 +17,9 @@ import org.junit.Test; -import io.reactivex.rxjava3.annotations.Nullable; +import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.internal.subscriptions.ScalarSubscription; +import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -53,4 +53,36 @@ public Integer poll() throws Exception { fcs.clear(); assertTrue(fcs.isEmpty()); } + + @Test + public void implementationStopsOnSubscribe() { + TestSubscriber ts = new TestSubscriber<>(); + BasicFuseableSubscriber bfs = new BasicFuseableSubscriber(ts) { + + @Override + protected boolean beforeDownstream() { + return false; + } + + @Override + public void onNext(@NonNull Integer t) { + ts.onNext(t); + } + + @Override + public int requestFusion(int mode) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public @Nullable Integer poll() throws Throwable { + return null; + } + }; + + bfs.onSubscribe(new BooleanSubscription()); + + assertFalse(ts.hasSubscription()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java index d6b80cc873..19bb7104ab 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java @@ -378,4 +378,37 @@ public void customOnErrorShouldReportCustomOnError() { assertTrue(subscriber.hasCustomOnError()); } + + @Test + public void cancel() { + BoundedSubscriber subscriber = new BoundedSubscriber<>(Functions.emptyConsumer(), + Functions.emptyConsumer(), + Functions.EMPTY_ACTION, + Functions.boundedConsumer(128), 128); + + BooleanSubscription bs = new BooleanSubscription(); + subscriber.onSubscribe(bs); + + subscriber.cancel(); + + assertTrue(bs.isCancelled()); + } + + @Test + public void dispose() { + BoundedSubscriber subscriber = new BoundedSubscriber<>(Functions.emptyConsumer(), + Functions.emptyConsumer(), + Functions.EMPTY_ACTION, + Functions.boundedConsumer(128), 128); + + BooleanSubscription bs = new BooleanSubscription(); + subscriber.onSubscribe(bs); + + assertFalse(subscriber.isDisposed()); + + subscriber.dispose(); + + assertTrue(bs.isCancelled()); + assertTrue(subscriber.isDisposed()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java index 69450f2ad0..5c923949e8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java @@ -292,4 +292,20 @@ public void getTimedOut() throws Exception { assertEquals(timeoutMessage(1, TimeUnit.NANOSECONDS), expected.getMessage()); } } + + @Test + public void onNextCompleteOnError() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + fs.onNext(1); + fs.onComplete(); + fs.onError(new TestException("One")); + + assertEquals((Integer)1, fs.get(5, TimeUnit.MILLISECONDS)); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } finally { + RxJavaPlugins.reset(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/MiscUtilTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/MiscUtilTest.java index b27279eb43..0ea0868e24 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/MiscUtilTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/MiscUtilTest.java @@ -60,9 +60,13 @@ public void errorModeEnum() { @Test public void linkedArrayList() { LinkedArrayList list = new LinkedArrayList(2); + assertEquals(0, list.size()); list.add(1); + assertEquals(1, list.size()); list.add(2); + assertEquals(2, list.size()); list.add(3); + assertEquals(3, list.size()); assertEquals("[1, 2, 3]", list.toString()); } diff --git a/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java index e581012d9b..48bd28aaf8 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java @@ -24,7 +24,7 @@ import org.junit.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.util.ExceptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -1142,4 +1142,28 @@ public void nullOnNext() { to.assertFailureAndMessage(NullPointerException.class, ExceptionHelper.nullWarning("onNext called with a null value.")); } + + @Test + public void onErrorQueuedUp() { + AtomicReference> soRef = new AtomicReference<>(); + TestObserverEx to = new TestObserverEx() { + @Override + public void onNext(Integer t) { + super.onNext(t); + soRef.get().onNext(2); + soRef.get().onError(new TestException()); + } + }; + + final SerializedObserver so = new SerializedObserver<>(to, true); + soRef.set(so); + + Disposable d = Disposable.empty(); + + so.onSubscribe(d); + + so.onNext(1); + + to.assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java index 01d6d76789..31e66d808b 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java @@ -1076,4 +1076,34 @@ public void assertValuesOnlyThrowsWhenErrored() { // expected } } + + @Test + public void onErrorIsNull() { + TestObserver to = TestObserver.create(); + to.onSubscribe(Disposable.empty()); + + to.onError(null); + + to.assertFailure(NullPointerException.class); + } + + @Test + public void awaitCountTimeout() { + TestObserver to = TestObserver.create(); + to.onSubscribe(Disposable.empty()); + to.awaitCount(1); + assertTrue(to.timeout); + } + + @Test(expected = RuntimeException.class) + public void awaitCountInterrupted() { + try { + TestObserver to = TestObserver.create(); + to.onSubscribe(Disposable.empty()); + Thread.currentThread().interrupt(); + to.awaitCount(1); + } finally { + Thread.interrupted(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelCollectTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelCollectTest.java index 1a94b3c247..a9509a46a5 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelCollectTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelCollectTest.java @@ -163,4 +163,11 @@ public void accept(List a, Object b) throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallel( + pf -> pf.collect(ArrayList::new, ArrayList::add) + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelDoOnNextTryTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelDoOnNextTryTest.java index 0ba01a05fd..14f585d9d0 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelDoOnNextTryTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelDoOnNextTryTest.java @@ -384,4 +384,23 @@ public void filterInvalidSourceConditional() { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .doOnNext(v -> { }, ParallelFailureHandling.SKIP) + .sequential() + ); + } + + @Test + public void doubleOnSubscribeConditional() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .doOnNext(v -> { }, ParallelFailureHandling.SKIP) + .filter(v -> true, ParallelFailureHandling.SKIP) + .sequential() + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTest.java index 1b77450986..93e3fb33c7 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTest.java @@ -119,4 +119,45 @@ public boolean test(Integer v) throws Exception { .test() .assertFailure(TestException.class); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .filter(v -> true) + .sequential() + ); + } + + @Test + public void doubleOnSubscribeConditional() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .filter(v -> true) + .filter(v -> true) + .sequential() + ); + } + + @Test + public void conditionalFalseTrue() { + Flowable.just(1) + .parallel() + .filter(v -> false) + .filter(v -> true) + .sequential() + .test() + .assertResult(); + } + + @Test + public void conditionalTrueFalse() { + Flowable.just(1) + .parallel() + .filter(v -> true) + .filter(v -> false) + .sequential() + .test() + .assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTryTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTryTest.java index 084566a0a5..37377bab52 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTryTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTryTest.java @@ -373,4 +373,45 @@ public void filterInvalidSourceConditional() { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .filter(v -> true, ParallelFailureHandling.SKIP) + .sequential() + ); + } + + @Test + public void doubleOnSubscribeConditional() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .filter(v -> true, ParallelFailureHandling.SKIP) + .filter(v -> true, ParallelFailureHandling.SKIP) + .sequential() + ); + } + + @Test + public void conditionalFalseTrue() { + Flowable.just(1) + .parallel() + .filter(v -> false, ParallelFailureHandling.SKIP) + .filter(v -> true, ParallelFailureHandling.SKIP) + .sequential() + .test() + .assertResult(); + } + + @Test + public void conditionalTrueFalse() { + Flowable.just(1) + .parallel() + .filter(v -> true, ParallelFailureHandling.SKIP) + .filter(v -> false, ParallelFailureHandling.SKIP) + .sequential() + .test() + .assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFlatMapIterableTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFlatMapIterableTest.java index 916d3651b9..4b5aeb0c78 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFlatMapIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFlatMapIterableTest.java @@ -14,7 +14,6 @@ package io.reactivex.rxjava3.parallel; import java.util.Arrays; -import java.util.stream.Stream; import org.junit.Test; @@ -25,7 +24,7 @@ public class ParallelFlatMapIterableTest extends RxJavaTest { @Test public void subscriberCount() { ParallelFlowableTest.checkSubscriberCount(Flowable.range(1, 5).parallel() - .flatMapStream(v -> Stream.of(1, 2, 3))); + .flatMapIterable(v -> Arrays.asList(1, 2, 3))); } @Test diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFromPublisherTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFromPublisherTest.java index c790201d98..981781cd9f 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFromPublisherTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFromPublisherTest.java @@ -21,6 +21,7 @@ import org.junit.Test; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; @@ -28,7 +29,7 @@ import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscribers.BasicFuseableSubscriber; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; -import io.reactivex.rxjava3.processors.UnicastProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.*; @@ -187,4 +188,107 @@ public Object apply(Integer v) throws Exception { assertTrue(map.toString(), e.contains("RxComputationThreadPool")); } } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(PublishProcessor.create().parallel()); + } + + @Test + public void syncFusedEmptyPoll() { + Flowable.just(1, 2) + .filter(v -> v == 1) + .compose(TestHelper.flowableStripBoundary()) + .parallel(1) + .sequential() + .test() + .assertResult(1); + } + + @Test + public void asyncFusedEmptyPoll() { + UnicastProcessor up = UnicastProcessor.create(); + up.onNext(1); + up.onNext(2); + up.onComplete(); + + up + .filter(v -> v == 1) + .compose(TestHelper.flowableStripBoundary()) + .parallel(1) + .sequential() + .test() + .assertResult(1); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.parallel().sequential()); + } + + @SuppressWarnings("unchecked") + @Test + public void requestUnboundedRace() { + FlowableSubscriber fs = new FlowableSubscriber() { + + @Override + public void onNext(@NonNull Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + public void onSubscribe(@NonNull Subscription s) { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestHelper.race( + () -> s.request(Long.MAX_VALUE), + () -> s.request(Long.MAX_VALUE) + ); + } + } + }; + + PublishProcessor.create() + .parallel(1) + .subscribe(new FlowableSubscriber[] { fs }); + } + + @SuppressWarnings("unchecked") + @Test + public void requestRace() { + FlowableSubscriber fs = new FlowableSubscriber() { + + @Override + public void onNext(@NonNull Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + public void onSubscribe(@NonNull Subscription s) { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestHelper.race( + () -> s.request(1), + () -> s.request(1) + ); + } + } + }; + + PublishProcessor.create() + .parallel(1) + .subscribe(new FlowableSubscriber[] { fs }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelJoinTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelJoinTest.java index fb50a775f8..8741eb1168 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelJoinTest.java @@ -14,14 +14,17 @@ package io.reactivex.rxjava3.parallel; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import org.reactivestreams.Subscriber; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -326,4 +329,198 @@ public Integer apply(Integer v) throws Exception { .test() .assertFailure(TestException.class, 2, 3, 4); } + + @Test + public void takeUntil() { + Flowable.range(1, 10) + .parallel(1) + .sequential() + .takeUntil(v -> true) + .test(0L) + .requestMore(100) + .assertResult(1); + } + + @Test + public void takeUntilDelayError() { + Flowable.range(1, 10) + .parallel(1) + .sequentialDelayError() + .takeUntil(v -> true) + .test(0L) + .requestMore(100) + .assertResult(1); + } + + @Test + public void oneItemNext() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.parallel(1) + .sequential() + .test(0L); + + pp.onNext(1); + + ts.requestMore(10) + .assertValuesOnly(1); + } + + @Test + public void delayErrorOneItemNext() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.parallel(1) + .sequentialDelayError() + .test(0L); + + pp.onNext(1); + + ts.requestMore(10) + .assertValuesOnly(1); + } + + @Test + public void onNextWhileProcessingSlowPath() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + if (t == 1) { + pp.onNext(2); + } + } + }; + + ParallelFlowable.fromArray(pp) + .sequential() + .subscribeWith(ts); + + pp.onNext(1); + + ts + .assertValuesOnly(1, 2); + } + + @Test + public void delayErrorOnNextWhileProcessingSlowPath() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + if (t == 1) { + pp.onNext(2); + } + } + }; + + ParallelFlowable.fromArray(pp) + .sequentialDelayError() + .subscribeWith(ts); + + pp.onNext(1); + + ts + .assertValuesOnly(1, 2); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported( + ParallelFlowable.fromArray(PublishProcessor.create()) + .sequential() + ); + } + + @Test + public void onNextMissingBackpressureRace() throws Throwable { + TestHelper.withErrorTracking(errors -> { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Flowable f1 = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + ref1.set(s); + } + }; + Flowable f2 = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + ref2.set(s); + } + }; + + ParallelFlowable.fromArray(f1, f2) + .sequential(1) + .test(0) + ; + + TestHelper.race( + () -> { + ref1.get().onNext(1); + ref1.get().onNext(2); + }, + () -> { + ref2.get().onNext(3); + ref2.get().onNext(4); + } + ); + + errors.clear(); + } + }); + } + + @Test + public void onNextMissingBackpressureDelayErrorRace() throws Throwable { + TestHelper.withErrorTracking(errors -> { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Flowable f1 = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + ref1.set(s); + } + }; + Flowable f2 = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + ref2.set(s); + } + }; + + ParallelFlowable.fromArray(f1, f2) + .sequentialDelayError(1) + .test(0) + ; + + TestHelper.race( + () -> { + ref1.get().onNext(1); + ref1.get().onNext(2); + }, + () -> { + ref2.get().onNext(3); + ref2.get().onNext(4); + } + ); + + errors.clear(); + } + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTest.java index e45034d416..5432c10a76 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTest.java @@ -15,15 +15,19 @@ import static org.junit.Assert.*; -import java.util.*; +import java.util.List; import java.util.concurrent.TimeUnit; import org.junit.Test; +import org.reactivestreams.Subscriber; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -199,4 +203,25 @@ public void doubleOnSubscribe() { .filter(v -> true) ); } + + @Test + public void conditionalCancelIgnored() { + Flowable f = new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super @NonNull Integer> s) { + @SuppressWarnings("unchecked") + ConditionalSubscriber subscriber = (ConditionalSubscriber)s; + subscriber.onSubscribe(new BooleanSubscription()); + subscriber.tryOnNext(1); + subscriber.tryOnNext(2); + } + }; + + ParallelFlowable.fromArray(f) + .map(v -> { throw new TestException(); }) + .filter(v -> true) + .sequential() + .test() + .assertFailure(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java index 85b311847a..ebdd2fd8cc 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java @@ -194,4 +194,13 @@ public void run() throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .doOnComplete(() -> { }) + .sequential() + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceFullTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceFullTest.java index 25d72af909..625fb8a42e 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceFullTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceFullTest.java @@ -16,7 +16,7 @@ import static org.junit.Assert.*; import java.io.IOException; -import java.util.List; +import java.util.*; import org.junit.Test; @@ -164,4 +164,11 @@ public Integer apply(Integer a, Integer b) throws Exception { .test() .assertFailure(TestException.class); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallelToFlowable( + pf -> pf.reduce((a, b) -> a) + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceTest.java index 5646b176b1..3e906ef01b 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceTest.java @@ -169,4 +169,11 @@ public List apply(List a, Object b) throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallel( + pf -> pf.reduce(ArrayList::new, (a, b) -> a) + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelRunOnTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelRunOnTest.java index 907b0bb889..44ce7960d1 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelRunOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelRunOnTest.java @@ -322,4 +322,60 @@ public void onNext(Integer t) { ts.assertResult(1); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallel(pf -> pf.runOn(ImmediateThinScheduler.INSTANCE)); + } + + @Test + public void doubleOnSubscribeConditional() { + TestHelper.checkDoubleOnSubscribeParallel(pf -> + pf.runOn(ImmediateThinScheduler.INSTANCE) + .filter(v -> true) + ); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported( + ParallelFlowable.fromArray(PublishProcessor.create()) + .runOn(ImmediateThinScheduler.INSTANCE) + ); + } + + @SuppressWarnings("unchecked") + @Test + public void asManyItemsAsRequested() { + TestSubscriber ts = new TestSubscriber<>(0); + + Flowable.range(1, 5) + .parallel(1) + .runOn(ImmediateThinScheduler.INSTANCE) + .subscribe(new Subscriber[] { + ts + }); + + ts + .requestMore(5) + .assertResult(1, 2, 3, 4, 5); + } + + @SuppressWarnings("unchecked") + @Test + public void asManyItemsAsRequestedConditional() { + TestSubscriber ts = new TestSubscriber<>(0); + + Flowable.range(1, 5) + .parallel(1) + .runOn(ImmediateThinScheduler.INSTANCE) + .filter(v -> true) + .subscribe(new Subscriber[] { + ts + }); + + ts + .requestMore(5) + .assertResult(1, 2, 3, 4, 5); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelSortedJoinTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelSortedJoinTest.java index e7b5cda9b0..1f3950c112 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelSortedJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelSortedJoinTest.java @@ -24,6 +24,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.operators.parallel.ParallelSortedJoin; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -207,4 +208,32 @@ public void run() { TestHelper.race(r1, r2); } } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(PublishProcessor.create().parallel().sorted(Functions.naturalComparator())); + } + + @Test + public void comparatorCrashWhileMainOnError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishProcessor> pp1 = PublishProcessor.create(); + PublishProcessor> pp2 = PublishProcessor.create(); + + new ParallelSortedJoin<>(ParallelFlowable.fromArray(pp1, pp2) + , (a, b) -> { + pp1.onError(new IOException()); + throw new TestException(); + }) + .test(); + + pp1.onNext(Arrays.asList(1)); + pp2.onNext(Arrays.asList(2)); + + pp1.onComplete(); + pp2.onComplete(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java index 6daaf15245..3fa8554a7d 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java @@ -523,4 +523,9 @@ public void onComplete() { ts1.assertResult(); ts2.assertEmpty(); } + + @Test + public void cancel() { + TestHelper.checkDisposed(AsyncProcessor.create()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/processors/BehaviorProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/BehaviorProcessorTest.java index d49ba5f565..0d0acb2796 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/BehaviorProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/BehaviorProcessorTest.java @@ -612,6 +612,36 @@ public void run() { } } + @Test + public void multipleSubscribersRemoveSomeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + final BehaviorProcessor p = BehaviorProcessor.create(); + + final TestSubscriber ts1 = p.test(); + final TestSubscriber ts2 = p.test(); + final TestSubscriber ts3 = p.test(); + + Runnable r1 = new Runnable() { + @Override + public void run() { + ts1.cancel(); + } + }; + + Runnable r2 = new Runnable() { + @Override + public void run() { + ts2.cancel(); + } + }; + + TestHelper.race(r1, r2); + + p.onNext(1); + ts3.assertValuesOnly(1); + } + } + @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void subscribeOnNextRace() { diff --git a/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java index bb4b97e68b..74b8fac206 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java @@ -26,6 +26,7 @@ import org.mockito.*; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; @@ -1199,6 +1200,30 @@ public void takeSizeAndTime() { .assertResult(2); } + @Test + public void takeSizeAndTime2() { + TestScheduler scheduler = new TestScheduler(); + + ReplayProcessor rp = ReplayProcessor.createWithTimeAndSize(1, TimeUnit.SECONDS, scheduler, 2); + + rp.onNext(1); + rp.onNext(2); + rp.onNext(3); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + cancel(); + onComplete(); + } + }; + + rp + .subscribeWith(ts) + .assertResult(2); + } + @Test public void takeSize() { ReplayProcessor rp = ReplayProcessor.createWithSize(2); @@ -1213,6 +1238,28 @@ public void takeSize() { .assertResult(2); } + @Test + public void takeSize2() { + ReplayProcessor rp = ReplayProcessor.createWithSize(2); + + rp.onNext(1); + rp.onNext(2); + rp.onNext(3); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + cancel(); + onComplete(); + } + }; + + rp + .subscribeWith(ts) + .assertResult(2); + } + @Test public void reentrantDrain() { TestScheduler scheduler = new TestScheduler(); diff --git a/src/test/java/io/reactivex/rxjava3/processors/SerializedProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/SerializedProcessorTest.java index fc50ade3ef..b2bd9bac19 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/SerializedProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/SerializedProcessorTest.java @@ -20,6 +20,7 @@ import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; @@ -664,4 +665,27 @@ public void run() { ts.assertEmpty(); } } + + @Test + public void onErrorQueued() { + FlowableProcessor sp = PublishProcessor.create().toSerialized(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + if (t == 1) { + sp.onNext(2); + sp.onSubscribe(new BooleanSubscription()); + sp.onError(new TestException()); + } + } + }; + + sp.subscribe(ts); + + sp.onNext(1); + + ts.assertFailure(TestException.class, 1); // errors skip ahead + } } diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java index b2e90cdb00..9910c03148 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java @@ -18,14 +18,14 @@ import java.lang.management.*; import java.util.List; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; import org.junit.Test; import io.reactivex.rxjava3.core.Scheduler; import io.reactivex.rxjava3.core.Scheduler.Worker; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; +import io.reactivex.rxjava3.internal.disposables.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -509,4 +509,42 @@ public void run() { assertSame(Functions.EMPTY_RUNNABLE, wrapper.getWrappedRunnable()); } + + @Test + public void interruptibleRunnableRunDisposeRace() { + ExecutorService exec = Executors.newSingleThreadExecutor(); + try { + Scheduler s = Schedulers.from(r -> exec.execute(r), true); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + SequentialDisposable sd = new SequentialDisposable(); + + TestHelper.race( + () -> sd.update(s.scheduleDirect(() -> { })), + () -> sd.dispose() + ); + } + } finally { + exec.shutdown(); + } + } + + @Test + public void interruptibleRunnableRunDispose() { + try { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + AtomicReference runRef = new AtomicReference<>(); + Scheduler s = Schedulers.from(r -> { + runRef.set(r); + }, true); + + Disposable d = s.scheduleDirect(() -> { }); + TestHelper.race( + () -> runRef.get().run(), + () -> d.dispose() + ); + } + } finally { + Thread.interrupted(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java index 395300e292..96d63b3ab2 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java @@ -24,7 +24,7 @@ import org.mockito.*; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; @@ -517,4 +517,9 @@ public void onComplete() { to1.assertResult(); to2.assertEmpty(); } + + @Test + public void dispose() { + TestHelper.checkDisposed(AsyncSubject.create()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/subjects/BehaviorSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/BehaviorSubjectTest.java index 8e472ec4c5..25e239e378 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/BehaviorSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/BehaviorSubjectTest.java @@ -833,4 +833,19 @@ public void emittingEmitNext() { assertNotNull(bd.queue); } + + @Test + public void hasObservers() { + BehaviorSubject bs = BehaviorSubject.create(); + + assertFalse(bs.hasObservers()); + + TestObserver to = bs.test(); + + assertTrue(bs.hasObservers()); + + to.dispose(); + + assertFalse(bs.hasObservers()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/subjects/SerializedSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/SerializedSubjectTest.java index bf74ac5998..86589cd25d 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/SerializedSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/SerializedSubjectTest.java @@ -20,9 +20,10 @@ import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -665,4 +666,51 @@ public void run() { to.assertEmpty(); } } + + @Test + public void onErrorQueued() { + Subject sp = PublishSubject.create().toSerialized(); + + TestObserver to = new TestObserver() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + if (t == 1) { + sp.onNext(2); + sp.onNext(3); + sp.onSubscribe(Disposable.empty()); + sp.onError(new TestException()); + } + } + }; + + sp.subscribe(to); + + sp.onNext(1); + + to.assertFailure(TestException.class, 1); // errors skip ahead + } + + @Test + public void onCompleteQueued() { + Subject sp = PublishSubject.create().toSerialized(); + + TestObserver to = new TestObserver() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + if (t == 1) { + sp.onNext(2); + sp.onNext(3); + sp.onComplete(); + } + } + }; + + sp.subscribe(to); + + sp.onNext(1); + + to.assertResult(1, 2, 3); + } } diff --git a/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java index ac000e4228..0c05dbbf9f 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java @@ -495,4 +495,20 @@ public void fusedNoConcurrentCleanDueToCancel() { } } } + + @Test + public void withCapacityHint() { + UnicastSubject us = UnicastSubject.create(16); + + TestObserver to = us.test(); + + for (int i = 0; i < 256; i++) { + us.onNext(i); + } + us.onComplete(); + + to.assertValueCount(256) + .assertComplete() + .assertNoErrors(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java index 00c7ae2103..ee842fcf02 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java @@ -1134,4 +1134,28 @@ public void nullOnNext() { ts.assertFailureAndMessage(NullPointerException.class, ExceptionHelper.nullWarning("onNext called with a null value.")); } + + @Test + public void onErrorQueuedUp() { + AtomicReference> ssRef = new AtomicReference<>(); + TestSubscriberEx ts = new TestSubscriberEx() { + @Override + public void onNext(Integer t) { + super.onNext(t); + ssRef.get().onNext(2); + ssRef.get().onError(new TestException()); + } + }; + + final SerializedSubscriber so = new SerializedSubscriber<>(ts, true); + ssRef.set(so); + + BooleanSubscription bs = new BooleanSubscription(); + + so.onSubscribe(bs); + + so.onNext(1); + + ts.assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java index 6ce4f4ed24..48bb722585 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java @@ -1661,4 +1661,40 @@ public void assertValuesOnlyThrowsWhenErrored() { // expected } } + + @Test + public void onErrorIsNull() { + TestSubscriber ts = TestSubscriber.create(); + ts.onSubscribe(new BooleanSubscription()); + + ts.onError(null); + + ts.assertFailure(NullPointerException.class); + } + + static final class TestSubscriberImpl extends TestSubscriber { + public boolean isTimeout() { + return timeout; + } + } + + @Test + public void awaitCountTimeout() { + TestSubscriberImpl ts = new TestSubscriberImpl<>(); + ts.onSubscribe(new BooleanSubscription()); + ts.awaitCount(1); + assertTrue(ts.isTimeout()); + } + + @Test(expected = RuntimeException.class) + public void awaitCountInterrupted() { + try { + TestSubscriber ts = TestSubscriber.create(); + ts.onSubscribe(new BooleanSubscription()); + Thread.currentThread().interrupt(); + ts.awaitCount(1); + } finally { + Thread.interrupted(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java b/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java index bb6608e96d..dfaccd6ac5 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java @@ -30,6 +30,7 @@ import org.mockito.stubbing.Answer; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; @@ -40,7 +41,7 @@ import io.reactivex.rxjava3.internal.operators.completable.CompletableToFlowable; import io.reactivex.rxjava3.internal.operators.maybe.MaybeToFlowable; import io.reactivex.rxjava3.internal.operators.single.SingleToFlowable; -import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.ExceptionHelper; import io.reactivex.rxjava3.observers.BaseTestConsumer; import io.reactivex.rxjava3.parallel.ParallelFlowable; @@ -356,6 +357,68 @@ public void onComplete() { RxJavaPlugins.setErrorHandler(null); } } + + /** + * Assert that by consuming the Publisher with a bad request amount, it is + * reported to the plugin error handler promptly. + * @param source the source to consume + */ + public static void assertBadRequestReported(ParallelFlowable source) { + List list = trackPluginErrors(); + try { + final CountDownLatch cdl = new CountDownLatch(1); + + FlowableSubscriber bad = new FlowableSubscriber() { + + @Override + public void onSubscribe(Subscription s) { + try { + s.request(-99); + s.cancel(); + s.cancel(); + } finally { + cdl.countDown(); + } + } + + @Override + public void onNext(Object t) { + + } + + @Override + public void onError(Throwable t) { + + } + + @Override + public void onComplete() { + + } + + }; + + @SuppressWarnings("unchecked") + FlowableSubscriber[] subs = new FlowableSubscriber[source.parallelism()]; + subs[0] = bad; + for (int i = 1; i < subs.length; i++) { + subs[i] = NoOpConsumer.INSTANCE; + } + source.subscribe(subs); + + try { + assertTrue(cdl.await(5, TimeUnit.SECONDS)); + } catch (InterruptedException ex) { + throw new AssertionError(ex.getMessage()); + } + + assertTrue(list.toString(), list.get(0) instanceof IllegalArgumentException); + assertEquals("n > 0 required but it was -99", list.get(0).getMessage()); + } finally { + RxJavaPlugins.setErrorHandler(null); + } + } + /** * Synchronizes the execution of two runnables (as much as possible) * to test race conditions. @@ -616,6 +679,18 @@ public static void doubleOnSubscribe(MaybeObserver observer) { } } + public static void checkDisposed(Disposable d) { + assertFalse("Disposed upfront?!", d.isDisposed()); + + d.dispose(); + + assertTrue("Not disposed?!", d.isDisposed()); + + d.dispose(); + + assertTrue("Not disposed again?!", d.isDisposed()); + } + /** * Checks if the upstream's Subscription sent through the onSubscribe reports * isCancelled properly before and after calling dispose. @@ -1485,6 +1560,69 @@ public int parallelism() { RxJavaPlugins.reset(); } } + /** + * Check if the given transformed reactive type reports multiple onSubscribe calls to + * RxJavaPlugins. + * @param the input value type + * @param transform the transform to drive an operator + */ + public static void checkDoubleOnSubscribeParallelToFlowable(Function, ? extends Flowable> transform) { + List errors = trackPluginErrors(); + try { + final Boolean[] b = { null, null, null, null }; + final CountDownLatch cdl = new CountDownLatch(2); + + ParallelFlowable source = new ParallelFlowable() { + @Override + public void subscribe(Subscriber[] subscribers) { + for (int i = 0; i < subscribers.length; i++) { + try { + BooleanSubscription bs1 = new BooleanSubscription(); + + subscribers[i].onSubscribe(bs1); + + BooleanSubscription bs2 = new BooleanSubscription(); + + subscribers[i].onSubscribe(bs2); + + b[i * 2 + 0] = bs1.isCancelled(); + b[i * 2 + 1] = bs2.isCancelled(); + } finally { + cdl.countDown(); + } + } + } + + @Override + public int parallelism() { + return 2; + } + }; + + Flowable out = transform.apply(source); + + out.subscribe(NoOpConsumer.INSTANCE); + + try { + assertTrue("Timed out", cdl.await(5, TimeUnit.SECONDS)); + } catch (InterruptedException ex) { + throw ExceptionHelper.wrapOrThrow(ex); + } + + assertEquals("Rail 1 First disposed?", false, b[0]); + assertEquals("Rail 1 Second not disposed?", true, b[1]); + + assertEquals("Rail 2 First disposed?", false, b[2]); + assertEquals("Rail 2 Second not disposed?", true, b[3]); + + assertError(errors, 0, IllegalStateException.class, "Subscription already set!"); + assertError(errors, 1, IllegalStateException.class, "Subscription already set!"); + } catch (Throwable ex) { + throw ExceptionHelper.wrapOrThrow(ex); + } finally { + RxJavaPlugins.reset(); + } + } /** * Check if the given transformed reactive type reports multiple onSubscribe calls to @@ -3581,4 +3719,124 @@ public static void assertError(CompletableFuture cf, Class { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + run.run(); + + resume.countDown(); + }); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + } + + /** + * Inserts a ConditionalSubscriber into the chain to trigger the conditional paths + * without interfering with the requestFusion parts. + * @param the element type + * @return the new FlowableTransformer instance + */ + public static FlowableTransformer conditional() { + return f -> new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super T> subscriber) { + f.subscribe(new ForwardingConditionalSubscriber<>(subscriber)); + } + }; + } + + /** + * Wraps a Subscriber and exposes it as a fuseable conditional subscriber without interfering with + * requestFusion. + * @param the element type + */ + static final class ForwardingConditionalSubscriber extends BasicQueueSubscription implements ConditionalSubscriber { + + private static final long serialVersionUID = 365317603608134078L; + + final Subscriber downstream; + + Subscription upstream; + + QueueSubscription qs; + + ForwardingConditionalSubscriber(Subscriber downstream) { + this.downstream = downstream; + } + + @SuppressWarnings("unchecked") + @Override + public void onSubscribe(@NonNull Subscription s) { + this.upstream = s; + if (s instanceof QueueSubscription) { + this.qs = (QueueSubscription)s; + } + downstream.onSubscribe(this); + } + + @Override + public void onNext(@NonNull T t) { + downstream.onNext(t); + } + + @Override + public boolean tryOnNext(@NonNull T t) { + downstream.onNext(t); + return true; + } + + @Override + public void onError(Throwable t) { + downstream.onError(t); + } + + @Override + public void onComplete() { + downstream.onComplete(); + } + + @Override + public int requestFusion(int mode) { + return qs != null ? qs.requestFusion(mode) : 0; + } + + @Override + public @Nullable T poll() throws Throwable { + return qs.poll(); + } + + @Override + public boolean isEmpty() { + return qs.isEmpty(); + } + + @Override + public void clear() { + qs.clear(); + } + + @Override + public void request(long n) { + upstream.request(n); + } + + @Override + public void cancel() { + upstream.cancel(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/validators/OperatorsUseInterfaces.java b/src/test/java/io/reactivex/rxjava3/validators/OperatorsUseInterfaces.java index e521c15b6e..a603a589fc 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/OperatorsUseInterfaces.java +++ b/src/test/java/io/reactivex/rxjava3/validators/OperatorsUseInterfaces.java @@ -17,13 +17,13 @@ import java.lang.reflect.*; import java.util.*; -import java.util.Observable; import java.util.concurrent.Callable; import org.junit.Test; import org.reactivestreams.Publisher; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.parallel.ParallelFlowable; From 6b2cf341c3ec2e27449b1270396e3cb7d562273b Mon Sep 17 00:00:00 2001 From: akarnokd Date: Thu, 13 Feb 2020 17:35:23 +0100 Subject: [PATCH 003/521] Missing version tag from Single.concatMaps --- src/main/java/io/reactivex/rxjava3/core/Single.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/reactivex/rxjava3/core/Single.java b/src/main/java/io/reactivex/rxjava3/core/Single.java index 09c67ef5a1..e830589e23 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Single.java +++ b/src/main/java/io/reactivex/rxjava3/core/Single.java @@ -2699,6 +2699,7 @@ public final Single cast(@NonNull Class clazz) { * @return the new {@code Single} returned from {@code mapper} when applied to the item emitted by the current {@code Single} * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap + * @since 3.0.0 */ @CheckReturnValue @NonNull @@ -2753,6 +2754,7 @@ public final Completable concatMapCompletable(@NonNull FunctionReactiveX operators documentation: FlatMap + * @since 3.0.0 */ @CheckReturnValue @NonNull From e793bc1d1a29dca18be795cf4a7628e2d44a4234 Mon Sep 17 00:00:00 2001 From: akarnokd Date: Thu, 13 Feb 2020 18:13:56 +0100 Subject: [PATCH 004/521] More missing version tags --- src/main/java/io/reactivex/rxjava3/core/Maybe.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/reactivex/rxjava3/core/Maybe.java b/src/main/java/io/reactivex/rxjava3/core/Maybe.java index 784343922d..de0f840326 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Maybe.java +++ b/src/main/java/io/reactivex/rxjava3/core/Maybe.java @@ -549,6 +549,7 @@ public static Flowable concatDelayError(@NonNull Publisher<@NonNull ? ext * @return the new {@code Flowable} with the concatenating behavior * @throws NullPointerException if {@code sources} is {@code null} * @throws IllegalArgumentException if {@code prefetch} is non-positive + * @since 3.0.0 */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue From 1c17f3669bcfc7651925f7dc977a9561ace84d7d Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 14 Feb 2020 14:19:22 +0100 Subject: [PATCH 005/521] Update 2.x maintenance date, include 3.0 wiki --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 698aa96f40..eb6b23dea9 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,11 @@ It extends the [observer pattern](http://en.wikipedia.org/wiki/Observer_pattern) Learn more about RxJava in general on the Wiki Home. +:information_source: Please read the [What's different in 3.0](https://github.com/ReactiveX/RxJava/wiki/What's-different-in-3.0) for details on the changes and migration information when upgrading from 2.x. + #### Version 2.x -The [2.x version](https://github.com/ReactiveX/RxJava/tree/2.x) will be supported with bugfixes and important documentation updates until -**December 31, 2020**. No new features will be added to 2.x. +The [2.x version](https://github.com/ReactiveX/RxJava/tree/2.x) is in maintenance mode and will be supported with only through bugfixes until **February 28, 2021**. No new features will be added to 2.x. #### Version 1.x From 042dee38fc9b1655f68a2296ee94035ba5e6efa1 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 14 Feb 2020 14:20:41 +0100 Subject: [PATCH 006/521] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb6b23dea9..ed7ad4a5de 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Learn more about RxJava in general on the buffer) { + ReplaySubscriber(ReplayBuffer buffer, AtomicReference> current) { this.buffer = buffer; + this.current = current; this.management = new AtomicInteger(); this.subscribers = new AtomicReference<>(EMPTY); this.shouldConnect = new AtomicBoolean(); @@ -266,9 +270,7 @@ public boolean isDisposed() { @Override public void dispose() { subscribers.set(TERMINATED); - // unlike OperatorPublish, we can't null out the terminated so - // late subscribers can still get replay - // current.compareAndSet(ReplaySubscriber.this, null); + current.compareAndSet(ReplaySubscriber.this, null); // we don't care if it fails because it means the current has // been replaced in the meantime SubscriptionHelper.cancel(this); @@ -1198,7 +1200,7 @@ public void subscribe(Subscriber child) { return; } // create a new subscriber to source - ReplaySubscriber u = new ReplaySubscriber<>(buf); + ReplaySubscriber u = new ReplaySubscriber<>(buf, curr); // let's try setting it as the current subscriber-to-source if (!curr.compareAndSet(null, u)) { // didn't work, maybe someone else did it or the current subscriber diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java index f7937b0509..3b7bff1673 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java @@ -174,7 +174,7 @@ public void connect(Consumer connection) { // create a new subscriber-to-source ReplayBuffer buf = bufferFactory.call(); - ReplayObserver u = new ReplayObserver<>(buf); + ReplayObserver u = new ReplayObserver<>(buf, current); // try setting it as the current subscriber-to-source if (!current.compareAndSet(ps, u)) { // did not work, perhaps a new subscriber arrived @@ -240,8 +240,12 @@ static final class ReplayObserver */ final AtomicBoolean shouldConnect; - ReplayObserver(ReplayBuffer buffer) { + /** The current connection. */ + final AtomicReference> current; + + ReplayObserver(ReplayBuffer buffer, AtomicReference> current) { this.buffer = buffer; + this.current = current; this.observers = new AtomicReference<>(EMPTY); this.shouldConnect = new AtomicBoolean(); @@ -255,9 +259,7 @@ public boolean isDisposed() { @Override public void dispose() { observers.set(TERMINATED); - // unlike OperatorPublish, we can't null out the terminated so - // late observers can still get replay - // current.compareAndSet(ReplayObserver.this, null); + current.compareAndSet(ReplayObserver.this, null); // we don't care if it fails because it means the current has // been replaced in the meantime DisposableHelper.dispose(this); @@ -1004,7 +1006,7 @@ public void subscribe(Observer child) { // create a new subscriber to source ReplayBuffer buf = bufferFactory.call(); - ReplayObserver u = new ReplayObserver<>(buf); + ReplayObserver u = new ReplayObserver<>(buf, curr); // let's try setting it as the current subscriber-to-source if (!curr.compareAndSet(null, u)) { // didn't work, maybe someone else did it or the current subscriber diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java index 6e701e5fc3..a3fb489e9f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java @@ -1776,4 +1776,31 @@ public void onError(Throwable t) { ts1.assertEmpty(); } + + @Test + public void disposeNoNeedForReset() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.publish(); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java index ce2cf5a564..62612f886b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java @@ -1923,19 +1923,6 @@ public ReplayBuffer get() throws Exception { .assertFailure(TestException.class); } - @Test - public void currentDisposedWhenConnecting() { - FlowableReplay fr = (FlowableReplay)FlowableReplay.create(Flowable.never(), 16, true); - fr.connect(); - - fr.current.get().dispose(); - assertTrue(fr.current.get().isDisposed()); - - fr.connect(); - - assertFalse(fr.current.get().isDisposed()); - } - @Test public void noBoundedRetentionViaThreadLocal() throws Exception { Flowable source = Flowable.range(1, 200) @@ -2275,4 +2262,85 @@ public void timeAndSizeNoTerminalTruncationOnTimechange() { .assertComplete() .assertNoErrors(); } + + @Test + public void disposeNoNeedForResetSizeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10, true); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10, TimeUnit.MINUTES, Schedulers.single(), true); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeAndSIzeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10, 10, TimeUnit.MINUTES, Schedulers.single(), true); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java index e151dd07f1..2a799af71f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java @@ -1988,19 +1988,6 @@ public ReplayBuffer get() throws Exception { .assertFailure(TestException.class); } - @Test - public void currentDisposedWhenConnecting() { - FlowableReplay fr = (FlowableReplay)FlowableReplay.create(Flowable.never(), 16, false); - fr.connect(); - - fr.current.get().dispose(); - assertTrue(fr.current.get().isDisposed()); - - fr.connect(); - - assertFalse(fr.current.get().isDisposed()); - } - @Test public void noBoundedRetentionViaThreadLocal() throws Exception { Flowable source = Flowable.range(1, 200) @@ -2191,4 +2178,112 @@ public void cancel() { ts.assertResult(); } + + @Test + public void disposeNoNeedForReset() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetSizeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10, TimeUnit.MINUTES); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeAndSIzeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10, 10, TimeUnit.MINUTES); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishTest.java index 16cb03d00e..a83372c03e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishTest.java @@ -866,4 +866,31 @@ public void disposeResets() { to.assertValuesOnly(1); } + + @Test + public void disposeNoNeedForReset() { + PublishSubject ps = PublishSubject.create(); + + ConnectableObservable co = ps.publish(); + + TestObserver to = co.test(); + + Disposable d = co.connect(); + + ps.onNext(1); + + d.dispose(); + + to = co.test(); + + to.assertEmpty(); + + co.connect(); + + to.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayEagerTruncateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayEagerTruncateTest.java index fe2c59d088..cf473d6e78 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayEagerTruncateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayEagerTruncateTest.java @@ -30,7 +30,7 @@ import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.core.Scheduler.Worker; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -1976,4 +1976,85 @@ public void timeAndSizeNoTerminalTruncationOnTimechange() { .assertComplete() .assertNoErrors(); } + + @Test + public void disposeNoNeedForResetSizeBound() { + PublishSubject ps = PublishSubject.create(); + + ConnectableObservable co = ps.replay(10, true); + + TestObserver to = co.test(); + + Disposable d = co.connect(); + + ps.onNext(1); + + d.dispose(); + + to = co.test(); + + to.assertEmpty(); + + co.connect(); + + to.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeBound() { + PublishSubject ps = PublishSubject.create(); + + ConnectableObservable co = ps.replay(10, TimeUnit.MINUTES, Schedulers.single(), true); + + TestObserver to = co.test(); + + Disposable d = co.connect(); + + ps.onNext(1); + + d.dispose(); + + to = co.test(); + + to.assertEmpty(); + + co.connect(); + + to.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeAndSIzeBound() { + PublishSubject ps = PublishSubject.create(); + + ConnectableObservable co = ps.replay(10, 10, TimeUnit.MINUTES, Schedulers.single(), true); + + TestObserver to = co.test(); + + Disposable d = co.connect(); + + ps.onNext(1); + + d.dispose(); + + to = co.test(); + + to.assertEmpty(); + + co.connect(); + + to.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java index cbaf961149..94a7a6087f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java @@ -1724,4 +1724,31 @@ public void resetWhileActiveIsNoOp() { co.reset(); } + + @Test + public void disposeNoNeedForReset() { + PublishSubject ps = PublishSubject.create(); + + ConnectableObservable co = ps.replay(); + + TestObserver to = co.test(); + + Disposable d = co.connect(); + + ps.onNext(1); + + d.dispose(); + + to = co.test(); + + to.assertEmpty(); + + co.connect(); + + to.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(2); + } } From a29e7e4560782660afe93edf0cade858c9344add Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2020 09:33:01 +0100 Subject: [PATCH 009/521] Bump build-info-extractor-gradle from 4.13.0 to 4.14.1 (#6924) Bumps build-info-extractor-gradle from 4.13.0 to 4.14.1. Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b0a12f1c79..cd274b5adf 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.0" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.4" - ext.jfrogExtractorVersion = "4.13.0" + ext.jfrogExtractorVersion = "4.14.1" ext.bndVersion = "5.0.0" ext.checkstyleVersion = "8.26" From fd496db9a64320fcb9f9b4d110018bcd41e46c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juhos=20Istv=C3=A1n?= Date: Sun, 8 Mar 2020 22:24:20 +0100 Subject: [PATCH 010/521] Fix typos in Backpressure.md (#6927) Fix lower case generic type declaration in reactive pull backpressure's code sample Fix typo in the Further reading section --- docs/Backpressure.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Backpressure.md b/docs/Backpressure.md index bfe90330bb..eadcfff263 100644 --- a/docs/Backpressure.md +++ b/docs/Backpressure.md @@ -128,7 +128,7 @@ someObservable.subscribe(new Subscriber() { } @Override - public void onNext(t n) { + public void onNext(T n) { // do something with the emitted item "n" // request another item: request(1); @@ -169,7 +169,7 @@ If you do not apply any of these operators to an Observable that does not suppor # Further reading -If the standard operators are providing the expected behavior, [one can write custom operators in RxJava](https://github.com/ReactiveX/RxJava/wiki/Implementing-custom-operators-(draft)). +If the standard operators aren't providing the expected behavior, [one can write custom operators in RxJava](https://github.com/ReactiveX/RxJava/wiki/Implementing-custom-operators-(draft)). # See also * [RxJava 0.20.0-RC1 release notes](https://github.com/ReactiveX/RxJava/releases/tag/0.20.0-RC1) From 61ea483727aa5d40bb1424b25b187cc2ac5470a3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2020 09:56:12 +0100 Subject: [PATCH 011/521] Bump mockito-core from 3.2.4 to 3.3.3 (#6935) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.2.4 to 3.3.3. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.2.4...v3.3.3) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index cd274b5adf..19fbe0b71f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.0.0" - ext.mockitoVersion = "3.2.4" + ext.mockitoVersion = "3.3.3" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.0" ext.guavaVersion = "28.2-jre" From cceb5d700acd534e2b39ce76f6f0e7b4f16d2147 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2020 09:30:13 +0100 Subject: [PATCH 012/521] Bump build-info-extractor-gradle from 4.14.1 to 4.15.0 (#6937) Bumps build-info-extractor-gradle from 4.14.1 to 4.15.0. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 19fbe0b71f..5ea92ab891 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.0" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.4" - ext.jfrogExtractorVersion = "4.14.1" + ext.jfrogExtractorVersion = "4.15.0" ext.bndVersion = "5.0.0" ext.checkstyleVersion = "8.26" From f863a4ec849f8b1721ae5142b7572d8c13fc84ef Mon Sep 17 00:00:00 2001 From: Volodymyr Date: Thu, 19 Mar 2020 10:57:53 +0200 Subject: [PATCH 013/521] Add see annotation for range operators (#6934) --- src/main/java/io/reactivex/rxjava3/core/Flowable.java | 5 ++++- src/main/java/io/reactivex/rxjava3/core/Observable.java | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index ddf5b3cf7b..33c89e1343 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -2921,11 +2921,12 @@ public static Flowable interval(long period, @NonNull TimeUnit unit, @NonN * @throws IllegalArgumentException * if {@code count} is less than zero, or if {@code start} + {@code count} − 1 exceeds * {@link Long#MAX_VALUE} + * @see #range(int, int) */ @CheckReturnValue + @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - @NonNull public static Flowable intervalRange(long start, long count, long initialDelay, long period, @NonNull TimeUnit unit) { return intervalRange(start, count, initialDelay, period, unit, Schedulers.computation()); } @@ -4386,6 +4387,8 @@ public static Flowable never() { * if {@code count} is less than zero, or if {@code start} + {@code count} − 1 exceeds * {@link Integer#MAX_VALUE} * @see ReactiveX operators documentation: Range + * @see #rangeLong(long, long) + * @see #intervalRange(long, long, long, long, TimeUnit) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index c37da859fd..9972112585 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -2537,10 +2537,11 @@ public static Observable interval(long period, @NonNull TimeUnit unit, @No * @throws IllegalArgumentException * if {@code count} is negative, or if {@code start} + {@code count} − 1 exceeds * {@link Long#MAX_VALUE} + * @see #range(int, int) */ @CheckReturnValue - @SchedulerSupport(SchedulerSupport.COMPUTATION) @NonNull + @SchedulerSupport(SchedulerSupport.COMPUTATION) public static Observable intervalRange(long start, long count, long initialDelay, long period, @NonNull TimeUnit unit) { return intervalRange(start, count, initialDelay, period, unit, Schedulers.computation()); } @@ -3895,6 +3896,8 @@ public static Observable never() { * if {@code count} is negative, or if {@code start} + {@code count} − 1 exceeds * {@link Integer#MAX_VALUE} * @see ReactiveX operators documentation: Range + * @see #rangeLong(long, long) + * @see #intervalRange(long, long, long, long, TimeUnit) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @@ -3933,6 +3936,7 @@ public static Observable range(int start, int count) { * if {@code count} is negative, or if {@code start} + {@code count} − 1 exceeds * {@link Long#MAX_VALUE} * @see ReactiveX operators documentation: Range + * @see #intervalRange(long, long, long, long, TimeUnit) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) From dff00abbf878840016ff797b7c17ba6a280f3155 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2020 08:35:58 +0100 Subject: [PATCH 014/521] Bump build-info-extractor-gradle from 4.15.0 to 4.15.1 (#6940) Bumps build-info-extractor-gradle from 4.15.0 to 4.15.1. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5ea92ab891..18517522a6 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.0" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.4" - ext.jfrogExtractorVersion = "4.15.0" + ext.jfrogExtractorVersion = "4.15.1" ext.bndVersion = "5.0.0" ext.checkstyleVersion = "8.26" From 73604160a227166ba0d107438c6a321c5ecfc718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Karnok?= Date: Fri, 20 Mar 2020 09:23:47 +0100 Subject: [PATCH 015/521] Remove unused imports --- src/main/java/io/reactivex/rxjava3/core/Completable.java | 1 - src/main/java/io/reactivex/rxjava3/core/Maybe.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Completable.java b/src/main/java/io/reactivex/rxjava3/core/Completable.java index 708818db92..6662dceb0c 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Completable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Completable.java @@ -18,7 +18,6 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; diff --git a/src/main/java/io/reactivex/rxjava3/core/Maybe.java b/src/main/java/io/reactivex/rxjava3/core/Maybe.java index de0f840326..dba5a15219 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Maybe.java +++ b/src/main/java/io/reactivex/rxjava3/core/Maybe.java @@ -20,7 +20,6 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; From a2128ae70dc98410d3b009943cd46f1b0e73049b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2020 10:18:12 +0100 Subject: [PATCH 016/521] Bump biz.aQute.bnd.gradle from 5.0.0 to 5.0.1 (#6941) Bumps [biz.aQute.bnd.gradle](https://github.com/bndtools/bnd) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/bndtools/bnd/releases) - [Changelog](https://github.com/bndtools/bnd/blob/master/docs/ADDING_RELEASE_DOCS.md) - [Commits](https://github.com/bndtools/bnd/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 18517522a6..f51d004ee4 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.4" ext.jfrogExtractorVersion = "4.15.1" - ext.bndVersion = "5.0.0" + ext.bndVersion = "5.0.1" ext.checkstyleVersion = "8.26" // -------------------------------------- From 14a95255e1b98f49a5344c0f7f793b530282ff66 Mon Sep 17 00:00:00 2001 From: Tomislav Hofman Date: Thu, 2 Apr 2020 14:19:56 +0200 Subject: [PATCH 017/521] Update image urls to non-transparent version. (#6944) --- docs/Backpressure.md | 22 +- docs/Blocking-Observable-Operators.md | 2 +- docs/Connectable-Observable-Operators.md | 2 +- docs/How-To-Use-RxJava.md | 6 +- docs/Phantom-Operators.md | 28 +- .../reactivex/rxjava3/core/Completable.java | 2 +- .../io/reactivex/rxjava3/core/Flowable.java | 746 +++++++++--------- .../java/io/reactivex/rxjava3/core/Maybe.java | 14 +- .../io/reactivex/rxjava3/core/Observable.java | 702 ++++++++-------- .../io/reactivex/rxjava3/core/Single.java | 144 ++-- .../flowables/ConnectableFlowable.java | 2 +- .../flowable/BlockingFlowableMostRecent.java | 2 +- .../flowable/BlockingFlowableNext.java | 2 +- .../BlockingObservableMostRecent.java | 2 +- .../observable/BlockingObservableNext.java | 2 +- .../single/SingleFlatMapPublisher.java | 2 +- .../observables/ConnectableObservable.java | 2 +- .../rxjava3/subjects/BehaviorSubject.java | 2 +- 18 files changed, 842 insertions(+), 842 deletions(-) diff --git a/docs/Backpressure.md b/docs/Backpressure.md index eadcfff263..8529ec0995 100644 --- a/docs/Backpressure.md +++ b/docs/Backpressure.md @@ -20,7 +20,7 @@ Cold Observables are ideal for the reactive pull model of backpressure described Your first line of defense against the problems of over-producing Observables is to use some of the ordinary set of Observable operators to reduce the number of emitted items to a more manageable number. The examples in this section will show how you might use such operators to handle a bursty Observable like the one illustrated in the following marble diagram: -​ +​ By fine-tuning the parameters to these operators you can ensure that a slow-consuming observer is not overwhelmed by a fast-producing Observable. @@ -33,7 +33,7 @@ The following diagrams show how you could use each of these operators on the bur ### sample (or throttleLast) The `sample` operator periodically "dips" into the sequence and emits only the most recently emitted item during each dip: -​ +​ ```java Observable burstySampled = bursty.sample(500, TimeUnit.MILLISECONDS); ``` @@ -41,7 +41,7 @@ Observable burstySampled = bursty.sample(500, TimeUnit.MILLISECONDS); ### throttleFirst The `throttleFirst` operator is similar, but emits not the most recently emitted item, but the first item that was emitted after the previous "dip": -​ +​ ```java Observable burstyThrottled = bursty.throttleFirst(500, TimeUnit.MILLISECONDS); ``` @@ -49,7 +49,7 @@ Observable burstyThrottled = bursty.throttleFirst(500, TimeUnit.MILLISE ### debounce (or throttleWithTimeout) The `debounce` operator emits only those items from the source Observable that are not followed by another item within a specified duration: -​ +​ ```java Observable burstyDebounced = bursty.debounce(10, TimeUnit.MILLISECONDS); ``` @@ -64,14 +64,14 @@ The following diagrams show how you could use each of these operators on the bur You could, for example, close and emit a buffer of items from the bursty Observable periodically, at a regular interval of time: -​ +​ ```java Observable> burstyBuffered = bursty.buffer(500, TimeUnit.MILLISECONDS); ``` Or you could get fancy, and collect items in buffers during the bursty periods and emit them at the end of each burst, by using the `debounce` operator to emit a buffer closing indicator to the `buffer` operator: -​ +​ ```java // we have to multicast the original bursty Observable so we can use it // both as our source and as the source for our buffer closing selector: @@ -86,14 +86,14 @@ Observable> burstyBuffered = burstyMulticast.buffer(burstyDebounce `window` is similar to `buffer`. One variant of `window` allows you to periodically emit Observable windows of items at a regular interval of time: -​ +​ ```java Observable> burstyWindowed = bursty.window(500, TimeUnit.MILLISECONDS); ```` You could also choose to emit a new window each time you have collected a particular number of items from the source Observable: -​ +​ ```java Observable> burstyWindowed = bursty.window(5); ``` @@ -158,11 +158,11 @@ For this to work, though, Observables _A_ and _B_ must respond correctly to the
onBackpressureBuffer
-
maintains a buffer of all emissions from the source Observable and emits them to downstream Subscribers according to the requests they generate

an experimental version of this operator (not available in RxJava 1.0) allows you to set the capacity of the buffer; applying this operator will cause the resulting Observable to terminate with an error if this buffer is overrun​
+
maintains a buffer of all emissions from the source Observable and emits them to downstream Subscribers according to the requests they generate

an experimental version of this operator (not available in RxJava 1.0) allows you to set the capacity of the buffer; applying this operator will cause the resulting Observable to terminate with an error if this buffer is overrun​
onBackpressureDrop
-
drops emissions from the source Observable unless there is a pending request from a downstream Subscriber, in which case it will emit enough items to fulfill the request
​
+
drops emissions from the source Observable unless there is a pending request from a downstream Subscriber, in which case it will emit enough items to fulfill the request
​
onBackpressureBlock (experimental, not in RxJava 1.0)
-
blocks the thread on which the source Observable is operating until such time as a Subscriber issues a request for items, and then unblocks the thread only so long as there are pending requests
+
blocks the thread on which the source Observable is operating until such time as a Subscriber issues a request for items, and then unblocks the thread only so long as there are pending requests
If you do not apply any of these operators to an Observable that does not support backpressure, _and_ if either you as the Subscriber or some operator between you and the Observable attempts to apply reactive pull backpressure, you will encounter a `MissingBackpressureException` which you will be notified of via your `onError()` callback. diff --git a/docs/Blocking-Observable-Operators.md b/docs/Blocking-Observable-Operators.md index 64d6e1b40a..fe2a640f49 100644 --- a/docs/Blocking-Observable-Operators.md +++ b/docs/Blocking-Observable-Operators.md @@ -18,7 +18,7 @@ To transform an `Observable` into a `BlockingObservable`, use the [`Observable.t > This documentation accompanies its explanations with a modified form of "marble diagrams." Here is how these marble diagrams represent Blocking Observables: - + #### see also: * javadoc: `BlockingObservable` diff --git a/docs/Connectable-Observable-Operators.md b/docs/Connectable-Observable-Operators.md index a048547529..582f0fac12 100644 --- a/docs/Connectable-Observable-Operators.md +++ b/docs/Connectable-Observable-Operators.md @@ -7,7 +7,7 @@ This section explains the [`ConnectableObservable`](http://reactivex.io/RxJava/j A Connectable Observable resembles an ordinary Observable, except that it does not begin emitting items when it is subscribed to, but only when its `connect()` method is called. In this way you can wait for all intended Subscribers to subscribe to the Observable before the Observable begins emitting items. - + The following example code shows two Subscribers subscribing to the same Observable. In the first case, they subscribe to an ordinary Observable; in the second case, they subscribe to a Connectable Observable that only connects after both Subscribers subscribe. Note the difference in the output: diff --git a/docs/How-To-Use-RxJava.md b/docs/How-To-Use-RxJava.md index ad309496b9..2d091bdce4 100644 --- a/docs/How-To-Use-RxJava.md +++ b/docs/How-To-Use-RxJava.md @@ -285,7 +285,7 @@ onNext => value_14_xform Here is a marble diagram that illustrates this transformation: - + This next example, in Clojure, consumes three asynchronous Observables, including a dependency from one to another, and emits a single response item by combining the items emitted by each of the three Observables with the [`zip`](http://reactivex.io/documentation/operators/zip.html) operator and then transforming the result with [`map`](http://reactivex.io/documentation/operators/map.html): @@ -333,7 +333,7 @@ The response looks like this: And here is a marble diagram that illustrates how that code produces that response: - + The following example, in Groovy, comes from [Ben Christensen’s QCon presentation on the evolution of the Netflix API](https://speakerdeck.com/benjchristensen/evolution-of-the-netflix-api-qcon-sf-2013). It combines two Observables with the [`merge`](http://reactivex.io/documentation/operators/merge.html) operator, then uses the [`reduce`](http://reactivex.io/documentation/operators/reduce.html) operator to construct a single item out of the resulting sequence, then transforms that item with [`map`](http://reactivex.io/documentation/operators/map.html) before emitting it: @@ -350,7 +350,7 @@ public Observable getVideoSummary(APIVideo video) { And here is a marble diagram that illustrates how that code uses the [`reduce`](http://reactivex.io/documentation/operators/reduce.html) operator to bring the results from multiple Observables together in one structure: - + ## Error Handling diff --git a/docs/Phantom-Operators.md b/docs/Phantom-Operators.md index 60da4a1a40..b01ac28ff2 100644 --- a/docs/Phantom-Operators.md +++ b/docs/Phantom-Operators.md @@ -19,7 +19,7 @@ These operators have been proposed but are not part of the 1.0 release of RxJava ## chunkify( ) #### returns an iterable that periodically returns a list of items emitted by the source Observable since the last list - + The `chunkify( )` operator represents a blocking observable as an Iterable, that, each time you iterate over it, returns a list of items emitted by the source Observable since the previous iteration. These lists may be empty if there have been no such items emitted. @@ -27,7 +27,7 @@ The `chunkify( )` operator represents a blocking observable as an Iterable, th ## fromFuture( ) #### convert a Future into an Observable, but do not attempt to get the Future's value until a Subscriber subscribes - + The `fromFuture( )` method also converts a Future into an Observable, but it obtains this Future indirectly, by means of a function you provide. It creates the Observable immediately, but waits to call the function and to obtain the Future until a Subscriber subscribes to it. @@ -35,7 +35,7 @@ The `fromFuture( )` method also converts a Future into an Observable, but it o ## forEachFuture( ) #### create a futureTask that will invoke a specified function on each item emitted by an Observable - + The `forEachFuture( )` returns a `FutureTask` for each item emitted by the source Observable (or each item and each notification) that, when executed, will apply a function you specify to each such item (or item and notification). @@ -43,7 +43,7 @@ The `forEachFuture( )` returns a `FutureTask` for each item emitted by the sou ## forIterable( ) #### apply a function to the elements of an Iterable to create Observables which are then concatenated - + `forIterable( )` is similar to `from(Iterable )` but instead of the resulting Observable emitting the elements of the Iterable as its own emitted items, it applies a specified function to each of these elements to generate one Observable per element, and then concatenates the emissions of these Observables to be its own sequence of emitted items. @@ -58,7 +58,7 @@ If the a subscriber to the Observable that results when a Future is converted to ## generate( ) and generateAbsoluteTime( ) #### create an Observable that emits a sequence of items as generated by a function of your choosing - + The basic form of `generate( )` takes four parameters. These are `initialState` and three functions: `iterate( )`, `condition( )`, and `resultSelector( )`. `generate( )` uses these four parameters to generate an Observable sequence, which is its return value. It does so in the following way. @@ -66,7 +66,7 @@ The basic form of `generate( )` takes four parameters. These are `initialState There are also versions of `generate( )` that allow you to do the work of generating the sequence on a particular `Scheduler` and that allow you to set the time interval between emissions by applying a function to the current state. The `generateAbsoluteTime( )` allows you to control the time at which an item is emitted by applying a function to the state to get an absolute system clock time (rather than an interval from the previous emission). - + #### see also: * Introduction to Rx: Generate @@ -79,7 +79,7 @@ There are also versions of `generate( )` that allow you to do the work of gene This version of `groupBy` adds another parameter: an Observable that emits duration markers. When a duration marker is emitted by this Observable, any grouped Observables that have been opened are closed, and `groupByUntil( )` will create new grouped Observables for any subsequent emissions by the source Observable. -​ +​ Another variety of `groupByUntil( )` limits the number of groups that can be active at any particular time. If an item is emitted by the source Observable that would cause the number of groups to exceed this maximum, before the new group is emitted, one of the existing groups is closed (that is, the Observable it represents terminates by calling its Subscribers' `onCompleted` methods and then expires). @@ -101,7 +101,7 @@ To represent an Observable as a Connectable Observable, use the `multicast( )` ## onErrorFlatMap( ) #### instructs an Observable to emit a sequence of items whenever it encounters an error -​ +​ The `onErrorFlatMap( )` method is similar to `onErrorResumeNext( )` except that it does not assume the source Observable will correctly terminate when it issues an error. Because of this, after emitting its backup sequence of items, `onErrorFlatMap( )` relinquishes control of the emitted sequence back to the source Observable. If that Observable again issues an error, `onErrorFlatMap( )` will again emit its backup sequence. @@ -111,13 +111,13 @@ Because `onErrorFlatMap( )` is designed to work with pathological source Obser Note that you should apply `onErrorFlatMap( )` directly to the pathological source Observable, and not to that Observable after it has been modified by additional operators, as such operators may effectively renormalize the source Observable by unsubscribing from it immediately after it issues an error. Below, for example, is an illustration showing how `onErrorFlatMap( )` will respond to two error-generating Observables that have been merged by the `merge( )` operator. Note that it will *not* react to both errors generated by both Observables, but only to the single error passed along by `merge( )`: -​ +​ *** ## parallel( ) #### split the work done on the emissions from an Observable into multiple Observables each operating on its own parallel thread -​ +​ The `parallel( )` method splits an Observable into as many Observables as there are available processors, and does work in parallel on each of these Observables. `parallel( )` then merges the results of these parallel computations back into a single, well-behaved Observable sequence. @@ -136,7 +136,7 @@ Kick off your work for each item inside [`flatMap`](Transforming-Observables#fla ## parallelMerge( ) #### combine multiple Observables into a smaller number of Observables, to facilitate parallelism -​ +​ Use the `parallelMerge( )` method to take an Observable that emits a large number of Observables and to reduce it to an Observable that emits a particular, smaller number of Observables that emit the same set of items as the original larger set of Observables: for instance a number of Observables that matches the number of parallel processes that you want to use when processing the emissions from the complete set of Observables. @@ -144,7 +144,7 @@ Use the `parallelMerge( )` method to take an Observable that emits a large num ## pivot( ) #### combine multiple sets of grouped observables so that they are arranged primarily by group rather than by set -​ +​ If you combine multiple sets of grouped observables, such as those created by [`groupBy( )` and `groupByUntil( )`](Transforming-Observables#wiki-groupby-and-groupbyuntil), then even if those grouped observables have been grouped by a similar differentiation function, the resulting grouping will be primarily based on which set the observable came from, not on which group the observable belonged to. @@ -152,13 +152,13 @@ An example may make this clearer. Imagine you use `groupBy( )` to group the em The result will be a grouped observable that emits two groups: the grouped observable resulting from transforming Observable1, and the grouped observable resulting from transforming Observable2. Each of those grouped observables emit observables that in turn emit the odds and evens from the source observables. You can use `pivot( )` to change this around: by applying `pivot( )` to this grouped observable it will transform into one that emits two different groups: the odds group and the evens group, with each of these groups emitting a separate observable corresponding to which source observable its set of integers came from. Here is an illustration: -​ +​ *** ## publishLast( ) #### represent an Observable as a Connectable Observable that emits only the last item emitted by the source Observable - + #### see also: * RxJS: `publishLast` diff --git a/src/main/java/io/reactivex/rxjava3/core/Completable.java b/src/main/java/io/reactivex/rxjava3/core/Completable.java index 6662dceb0c..f261d1a782 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Completable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Completable.java @@ -2172,7 +2172,7 @@ public final Completable lift(@NonNull CompletableOperator onLift) { * Maps the signal types of this {@code Completable} into a {@link Notification} of the same kind * and emits it as a single success value to downstream. *

- * + * *

*
Scheduler:
*
{@code materialize} does not operate by default on a particular {@link Scheduler}.
diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 33c89e1343..2843a2909a 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -53,7 +53,7 @@ *

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

- * + * *

* The {@code Flowable} follows the protocol *


@@ -163,7 +163,7 @@ public abstract class Flowable<@NonNull T> implements Publisher {
      * Mirrors the one {@link Publisher} in an {@link Iterable} of several {@code Publisher}s that first either emits an item or sends
      * a termination notification.
      * 

- * + * *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by the winning @@ -193,7 +193,7 @@ public static Flowable amb(@NonNull Iterable<@NonNull ? extends Publisher * Mirrors the one {@link Publisher} in an array of several {@code Publisher}s that first either emits an item or sends * a termination notification. *

- * + * *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by the winning @@ -646,7 +646,7 @@ public static Flowable combineLatestDelayError(@NonNull Iterable<@NonN * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s @@ -692,7 +692,7 @@ public static Flowable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s @@ -743,7 +743,7 @@ public static Flowable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s @@ -799,7 +799,7 @@ public static Flowable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s @@ -860,7 +860,7 @@ public static Flowable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s @@ -925,7 +925,7 @@ public static Flowable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s @@ -996,7 +996,7 @@ public static Flowable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s @@ -1071,7 +1071,7 @@ public static Flowable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s @@ -1147,7 +1147,7 @@ public static Flowable combineLatest( * Concatenates elements of each {@link Publisher} provided via an {@link Iterable} sequence into a single sequence * of elements without interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} @@ -1177,7 +1177,7 @@ public static Flowable concat(@NonNull Iterable<@NonNull ? extends Publis * Returns a {@code Flowable} that emits the items emitted by each of the {@link Publisher}s emitted by the source * {@code Publisher}, one after the other, without interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. Both the outer and inner {@code Publisher} @@ -1207,7 +1207,7 @@ public static Flowable concat(@NonNull Publisher<@NonNull ? extends Publi * Returns a {@code Flowable} that emits the items emitted by each of the {@link Publisher}s emitted by the source * {@code Publisher}, one after the other, without interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. Both the outer and inner {@code Publisher} @@ -1241,7 +1241,7 @@ public static Flowable concat(@NonNull Publisher<@NonNull ? extends Publi * Returns a {@code Flowable} that emits the items emitted by two {@link Publisher}s, one after the other, without * interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} @@ -1275,7 +1275,7 @@ public static Flowable concat(@NonNull Publisher<@NonNull ? extends T> so * Returns a {@code Flowable} that emits the items emitted by three {@link Publisher}s, one after the other, without * interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} @@ -1314,7 +1314,7 @@ public static Flowable concat( * Returns a {@code Flowable} that emits the items emitted by four {@link Publisher}s, one after the other, without * interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} @@ -1357,7 +1357,7 @@ public static Flowable concat( *

* Note: named this way because of overload conflict with {@code concat(Publisher>}). *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} @@ -1392,7 +1392,7 @@ public static Flowable concatArray(@NonNull Publisher<@NonNull ? extends * Concatenates a variable number of {@link Publisher} sources and delays errors from any of them * till all terminate. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} @@ -1997,7 +1997,7 @@ public static Flowable create(@NonNull FlowableOnSubscribe source, @No * that subscribes. That is, for each subscriber, the actual {@code Publisher} that subscriber observes is * determined by the factory function. *

- * + * *

* The defer {@code Subscriber} allows you to defer or delay emitting items from a {@code Publisher} until such time as a * {@code Subscriber} subscribes to the {@code Publisher}. This allows a {@code Subscriber} to easily obtain updates or a @@ -2032,7 +2032,7 @@ public static Flowable defer(@NonNull Supplier - * + * *

*
Backpressure:
*
This source doesn't produce any elements and effectively ignores downstream backpressure.
@@ -2058,7 +2058,7 @@ public static Flowable empty() { * Returns a {@code Flowable} that invokes a {@link Subscriber}'s {@link Subscriber#onError onError} method when the * {@code Subscriber} subscribes to it. *

- * + * *

*
Backpressure:
*
This source doesn't produce any elements and effectively ignores downstream backpressure.
@@ -2087,7 +2087,7 @@ public static Flowable error(@NonNull Supplier suppl * Returns a {@code Flowable} that invokes a {@link Subscriber}'s {@link Subscriber#onError onError} method when the * {@code Subscriber} subscribes to it. *

- * + * *

*
Backpressure:
*
This source doesn't produce any elements and effectively ignores downstream backpressure.
@@ -2148,7 +2148,7 @@ public static Flowable fromAction(@NonNull Action action) { /** * Converts an array into a {@link Publisher} that emits the items in the array. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and iterates the given {@code array} @@ -2185,7 +2185,7 @@ public static Flowable fromAction(@NonNull Action action) { * Returns a {@code Flowable} that, when a {@link Subscriber} subscribes to it, invokes a function you specify and then * emits the value returned from that function. *

- * + * *

* This allows you to defer the execution of the function you specify until a {@code Subscriber} subscribes to the * {@link Publisher}. That is to say, it makes the function "lazy." @@ -2250,7 +2250,7 @@ public static Flowable fromCompletable(@NonNull CompletableSource complet /** * Converts a {@link Future} into a {@link Publisher}. *

- * + * *

* The operator calls {@link Future#get()}, which is a blocking method, on the subscription thread. * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a @@ -2292,7 +2292,7 @@ public static Flowable fromCompletable(@NonNull CompletableSource complet /** * Converts a {@link Future} into a {@link Publisher}, with a timeout on the {@code Future}. *

- * + * *

* The operator calls {@link Future#get(long, TimeUnit)}, which is a blocking method, on the subscription thread. * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a @@ -2339,7 +2339,7 @@ public static Flowable fromCompletable(@NonNull CompletableSource complet /** * Converts an {@link Iterable} sequence into a {@link Publisher} that emits the items in the sequence. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and iterates the given {@code iterable} @@ -2559,7 +2559,7 @@ public static Flowable fromSingle(@NonNull SingleSource source) { * Returns a {@code Flowable} that, when a {@link Subscriber} subscribes to it, invokes a supplier function you specify and then * emits the value returned from that function. *

- * + * *

* This allows you to defer the execution of the function you specify until a {@code Subscriber} subscribes to the * {@link Publisher}. That is to say, it makes the function "lazy." @@ -2775,7 +2775,7 @@ public static Flowable generate(@NonNull Supplier initialState, @No * Returns a {@code Flowable} that emits a {@code 0L} after the {@code initialDelay} and ever-increasing numbers * after each {@code period} of time thereafter. *

- * + * *

*
Backpressure:
*
The operator generates values based on time and ignores downstream backpressure which @@ -2808,7 +2808,7 @@ public static Flowable interval(long initialDelay, long period, @NonNull T * Returns a {@code Flowable} that emits a {@code 0L} after the {@code initialDelay} and ever-increasing numbers * after each {@code period} of time thereafter, on a specified {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator generates values based on time and ignores downstream backpressure which @@ -2844,7 +2844,7 @@ public static Flowable interval(long initialDelay, long period, @NonNull T /** * Returns a {@code Flowable} that emits a sequential number every specified interval of time. *

- * + * *

*
Backpressure:
*
The operator signals a {@link MissingBackpressureException} if the downstream @@ -2873,7 +2873,7 @@ public static Flowable interval(long period, @NonNull TimeUnit unit) { * Returns a {@code Flowable} that emits a sequential number every specified interval of time, on a * specified {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator generates values based on time and ignores downstream backpressure which @@ -2978,7 +2978,7 @@ public static Flowable intervalRange(long start, long count, long initialD /** * Returns a {@code Flowable} that signals the given (constant reference) item and then completes. *

- * + * *

* Note that the item is taken and re-emitted as is and not computed by any means by {@code just}. Use {@link #fromCallable(Callable)} * to generate a single item on demand (when {@link Subscriber}s subscribe to it). @@ -3018,7 +3018,7 @@ public static Flowable intervalRange(long start, long count, long initialD /** * Converts two items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -3050,7 +3050,7 @@ public static Flowable intervalRange(long start, long count, long initialD /** * Converts three items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -3085,7 +3085,7 @@ public static Flowable intervalRange(long start, long count, long initialD /** * Converts four items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -3124,7 +3124,7 @@ public static Flowable intervalRange(long start, long count, long initialD /** * Converts five items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -3166,7 +3166,7 @@ public static Flowable intervalRange(long start, long count, long initialD /** * Converts six items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -3211,7 +3211,7 @@ public static Flowable intervalRange(long start, long count, long initialD /** * Converts seven items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -3260,7 +3260,7 @@ public static Flowable intervalRange(long start, long count, long initialD /** * Converts eight items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -3312,7 +3312,7 @@ public static Flowable intervalRange(long start, long count, long initialD /** * Converts nine items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -3367,7 +3367,7 @@ public static Flowable intervalRange(long start, long count, long initialD /** * Converts ten items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -3427,7 +3427,7 @@ public static Flowable intervalRange(long start, long count, long initialD * Flattens an {@link Iterable} of {@link Publisher}s into one {@code Publisher}, without any transformation, while limiting the * number of concurrent subscriptions to these {@code Publisher}s. *

- * + * *

* You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. @@ -3479,7 +3479,7 @@ public static Flowable merge(@NonNull Iterable<@NonNull ? extends Publish * Flattens an array of {@link Publisher}s into one {@code Publisher}, without any transformation, while limiting the * number of concurrent subscriptions to these {@code Publisher}s. *

- * + * *

* You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. @@ -3531,7 +3531,7 @@ public static Flowable mergeArray(int maxConcurrency, int bufferSize, @No /** * Flattens an {@link Iterable} of {@link Publisher}s into one {@code Publisher}, without any transformation. *

- * + * *

* You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. @@ -3577,7 +3577,7 @@ public static Flowable merge(@NonNull Iterable<@NonNull ? extends Publish * Flattens an {@link Iterable} of {@link Publisher}s into one {@code Publisher}, without any transformation, while limiting the * number of concurrent subscriptions to these {@code Publisher}s. *

- * + * *

* You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. @@ -3627,7 +3627,7 @@ public static Flowable merge(@NonNull Iterable<@NonNull ? extends Publish * Flattens a {@link Publisher} that emits {@code Publisher}s into a single {@code Publisher} that emits the items emitted by * thos {@code Publisher}s , without any transformation. *

- * + * *

* You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. @@ -3674,7 +3674,7 @@ public static Flowable merge(@NonNull Publisher<@NonNull ? extends Publis * those {@code Publisher}s, without any transformation, while limiting the maximum number of concurrent * subscriptions to these {@code Publisher}s. *

- * + * *

* You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. @@ -3724,7 +3724,7 @@ public static Flowable merge(@NonNull Publisher<@NonNull ? extends Publis /** * Flattens an array of {@link Publisher}s into one {@code Publisher}, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. @@ -3770,7 +3770,7 @@ public static Flowable mergeArray(@NonNull Publisher<@NonNull ? extends T /** * Flattens two {@link Publisher}s into a single {@code Publisher}, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. @@ -3819,7 +3819,7 @@ public static Flowable merge(@NonNull Publisher<@NonNull ? extends T> sou /** * Flattens three {@link Publisher}s into a single {@code Publisher}, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. @@ -3871,7 +3871,7 @@ public static Flowable merge(@NonNull Publisher<@NonNull ? extends T> sou /** * Flattens four {@link Publisher}s into a single {@code Publisher}, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. @@ -3934,7 +3934,7 @@ public static Flowable merge( * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Subscriber}s once. @@ -3971,7 +3971,7 @@ public static Flowable mergeDelayError(@NonNull Iterable<@NonNull ? exten * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Subscriber}s once. @@ -4013,7 +4013,7 @@ public static Flowable mergeDelayError(@NonNull Iterable<@NonNull ? exten * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Subscriber}s once. @@ -4056,7 +4056,7 @@ public static Flowable mergeArrayDelayError(int maxConcurrency, int buffe * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Subscriber}s once. @@ -4096,7 +4096,7 @@ public static Flowable mergeDelayError(@NonNull Iterable<@NonNull ? exten * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Subscriber}s once. @@ -4134,7 +4134,7 @@ public static Flowable mergeDelayError(@NonNull Publisher<@NonNull ? exte * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Subscriber}s once. @@ -4175,7 +4175,7 @@ public static Flowable mergeDelayError(@NonNull Publisher<@NonNull ? exte * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Subscriber}s once. @@ -4213,7 +4213,7 @@ public static Flowable mergeArrayDelayError(@NonNull Publisher<@NonNull ? * notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from * propagating that error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

* Even if both merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Subscriber}s once. @@ -4255,7 +4255,7 @@ public static Flowable mergeDelayError(@NonNull Publisher<@NonNull ? exte * from propagating that error notification until all of the merged {@code Publisher}s have finished emitting * items. *

- * + * *

* Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Subscriber}s once. @@ -4300,7 +4300,7 @@ public static Flowable mergeDelayError(@NonNull Publisher<@NonNull ? exte * will refrain from propagating that error notification until all of the merged {@code Publisher}s have finished * emitting items. *

- * + * *

* Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Subscriber}s once. @@ -4343,7 +4343,7 @@ public static Flowable mergeDelayError( /** * Returns a {@code Flowable} that never sends any items or notifications to a {@link Subscriber}. *

- * + * *

* This {@link Publisher} is useful primarily for testing purposes. *

@@ -4370,7 +4370,7 @@ public static Flowable never() { /** * Returns a {@code Flowable} that emits a sequence of {@link Integer}s within a specified range. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals values on-demand (i.e., when requested).
@@ -4413,7 +4413,7 @@ public static Flowable range(int start, int count) { /** * Returns a {@code Flowable} that emits a sequence of {@link Long}s within a specified range. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals values on-demand (i.e., when requested).
@@ -4460,7 +4460,7 @@ public static Flowable rangeLong(long start, long count) { * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link Publisher} sequences are the * same by comparing the items emitted by each {@code Publisher} pairwise. *

- * + * *

*
Backpressure:
*
This operator honors downstream backpressure and expects both of its sources @@ -4492,7 +4492,7 @@ public static Single sequenceEqual(@NonNull Publisher<@NonNull ? ex * same by comparing the items emitted by each {@code Publisher} pairwise based on the results of a specified * equality function. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor @@ -4527,7 +4527,7 @@ public static Single sequenceEqual(@NonNull Publisher<@NonNull ? ex * same by comparing the items emitted by each {@code Publisher} pairwise based on the results of a specified * equality function. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor @@ -4568,7 +4568,7 @@ public static Single sequenceEqual(@NonNull Publisher<@NonNull ? ex * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link Publisher} sequences are the * same by comparing the items emitted by each {@code Publisher} pairwise. *

- * + * *

*
Backpressure:
*
This operator honors downstream backpressure and expects both of its sources @@ -4602,7 +4602,7 @@ public static Single sequenceEqual(@NonNull Publisher<@NonNull ? ex * Converts a {@link Publisher} that emits {@code Publisher}s into a {@code Publisher} that emits the items emitted by the * most recently emitted of those {@code Publisher}s. *

- * + * *

* {@code switchOnNext} subscribes to a {@code Publisher} that emits {@code Publisher}s. Each time it observes one of * these emitted {@code Publisher}s, the {@code Publisher} returned by {@code switchOnNext} begins emitting the items @@ -4644,7 +4644,7 @@ public static Flowable switchOnNext(@NonNull Publisher<@NonNull ? extends * Converts a {@link Publisher} that emits {@code Publisher}s into a {@code Publisher} that emits the items emitted by the * most recently emitted of those {@code Publisher}s. *

- * + * *

* {@code switchOnNext} subscribes to a {@code Publisher} that emits {@code Publisher}s. Each time it observes one of * these emitted {@code Publisher}s, the {@code Publisher} returned by {@code switchOnNext} begins emitting the items @@ -4683,7 +4683,7 @@ public static Flowable switchOnNext(@NonNull Publisher<@NonNull ? extends * Converts a {@link Publisher} that emits {@code Publisher}s into a {@code Publisher} that emits the items emitted by the * most recently emitted of those {@code Publisher}s and delays any exception until all {@code Publisher}s terminate. *

- * + * *

* {@code switchOnNext} subscribes to a {@code Publisher} that emits {@code Publisher}s. Each time it observes one of * these emitted {@code Publisher}s, the {@code Publisher} returned by {@code switchOnNext} begins emitting the items @@ -4723,7 +4723,7 @@ public static Flowable switchOnNextDelayError(@NonNull Publisher<@NonNull * Converts a {@link Publisher} that emits {@code Publisher}s into a {@code Publisher} that emits the items emitted by the * most recently emitted of those {@code Publisher}s and delays any exception until all {@code Publisher}s terminate. *

- * + * *

* {@code switchOnNext} subscribes to a {@code Publisher} that emits {@code Publisher}s. Each time it observes one of * these emitted {@code Publisher}s, the {@code Publisher} returned by {@code switchOnNext} begins emitting the items @@ -4765,7 +4765,7 @@ public static Flowable switchOnNextDelayError(@NonNull Publisher<@NonNull /** * Returns a {@code Flowable} that emits {@code 0L} after a specified delay, and then completes. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. If the downstream needs a slower rate @@ -4794,7 +4794,7 @@ public static Flowable timer(long delay, @NonNull TimeUnit unit) { * Returns a {@code Flowable} that emits {@code 0L} after a specified delay, on a specified {@link Scheduler}, and then * completes. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. If the downstream needs a slower rate @@ -4860,7 +4860,7 @@ public static Flowable unsafeCreate(@NonNull Publisher onSubscribe) { * that resource and calls the provided {@code resourceDisposer} function if this inner source terminates or the * downstream cancels the flow. *

- * + * *

*
Backpressure:
*
The operator is a pass-through for backpressure and otherwise depends on the @@ -4897,7 +4897,7 @@ public static Flowable using( * that resource and calls the provided {@code resourceDisposer} function if this inner source terminates or the * downstream disposes the flow; doing it before these end-states have been reached if {@code eager == true}, after otherwise. *

- * + * *

*
Backpressure:
*
The operator is a pass-through for backpressure and otherwise depends on the @@ -4963,7 +4963,7 @@ public static Flowable using( * use {@link #doOnCancel(Action)} as well or use {@code using()} to do cleanup in case of completion * or cancellation. *

- * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. @@ -5018,7 +5018,7 @@ public static Flowable zip(@NonNull Iterable<@NonNull ? extends Publis * use {@link #doOnCancel(Action)} as well or use {@code using()} to do cleanup in case of completion * or cancellation. *

- * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. @@ -5062,7 +5062,7 @@ public static Flowable zip(@NonNull Iterable<@NonNull ? extends Publis * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of * two items emitted, in sequence, by two other {@link Publisher}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the new {@code Publisher} * will be the result of the function applied to the first item emitted by {@code o1} and the first item @@ -5124,7 +5124,7 @@ public static Flowable zip( * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of * two items emitted, in sequence, by two other {@link Publisher}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the new {@code Publisher} * will be the result of the function applied to the first item emitted by {@code o1} and the first item @@ -5187,7 +5187,7 @@ public static Flowable zip( * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of * two items emitted, in sequence, by two other {@link Publisher}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the new {@code Publisher} * will be the result of the function applied to the first item emitted by {@code o1} and the first item @@ -5252,7 +5252,7 @@ public static Flowable zip( * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of * three items emitted, in sequence, by three other {@link Publisher}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the new {@code Publisher} * will be the result of the function applied to the first item emitted by {@code o1}, the first item @@ -5319,7 +5319,7 @@ public static Flowable zip( * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of * four items emitted, in sequence, by four other {@link Publisher}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the new {@code Publisher} * will be the result of the function applied to the first item emitted by {@code o1}, the first item @@ -5392,7 +5392,7 @@ public static Flowable zip( * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of * five items emitted, in sequence, by five other {@link Publisher}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the new {@code Publisher} * will be the result of the function applied to the first item emitted by {@code o1}, the first item @@ -5469,7 +5469,7 @@ public static Flowable zip( * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of * six items emitted, in sequence, by six other {@link Publisher}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the new {@code Publisher} * will be the result of the function applied to the first item emitted by each source {@code Publisher}, the @@ -5550,7 +5550,7 @@ public static Flowable zip( * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of * seven items emitted, in sequence, by seven other {@link Publisher}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the new {@code Publisher} * will be the result of the function applied to the first item emitted by each source {@code Publisher}, the @@ -5636,7 +5636,7 @@ public static Flowable zip( * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of * eight items emitted, in sequence, by eight other {@link Publisher}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the new {@code Publisher} * will be the result of the function applied to the first item emitted by each source {@code Publisher}, the @@ -5726,7 +5726,7 @@ public static Flowable zip( * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of * nine items emitted, in sequence, by nine other {@link Publisher}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the new {@code Publisher} * will be the result of the function applied to the first item emitted by each source {@code Publisher}, the @@ -5843,7 +5843,7 @@ public static Flowable zip( * use {@link #doOnCancel(Action)} as well or use {@code using()} to do cleanup in case of completion * or cancellation. *

- * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. @@ -5893,7 +5893,7 @@ public static Flowable zipArray(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded @@ -5921,7 +5921,7 @@ public final Single all(@NonNull Predicate predicate) { * Mirrors the {@link Publisher} (current or provided) that first either emits an item or sends a termination * notification. *

- * + * *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by the winning @@ -5951,7 +5951,7 @@ public final Flowable ambWith(@NonNull Publisher<@NonNull ? extends T> other) * specified condition, otherwise {@code false}. Note: this always emits {@code false} if the * current {@code Flowable} is empty. *

- * + * *

* In Rx.Net this is the {@code any} operator but we renamed it in RxJava to better match Java naming * idioms. @@ -6050,7 +6050,7 @@ public final T blockingFirst(@NonNull T defaultItem) { * {@link Consumer} with each upstream item on the current thread until the * upstream terminates. *

- * + * *

* Note: the method will only return if the upstream terminates or the current * thread is interrupted. @@ -6091,7 +6091,7 @@ public final void blockingForEach(@NonNull Consumer onNext) { * {@link Consumer} with each upstream item on the current thread until the * upstream terminates. *

- * + * *

* Note: the method will only return if the upstream terminates or the current * thread is interrupted. @@ -6142,7 +6142,7 @@ public final void blockingForEach(@NonNull Consumer onNext, int buffe /** * Converts this {@code Flowable} into an {@link Iterable}. *

- * + * *

*
Backpressure:
*
The operator expects the upstream to honor backpressure otherwise the returned @@ -6165,7 +6165,7 @@ public final Iterable blockingIterable() { /** * Converts this {@code Flowable} into an {@link Iterable}. *

- * + * *

*
Backpressure:
*
The operator expects the upstream to honor backpressure otherwise the returned @@ -6193,7 +6193,7 @@ public final Iterable blockingIterable(int bufferSize) { * Returns the last item emitted by this {@code Flowable}, or throws * {@link NoSuchElementException} if this {@code Flowable} emits no items. *

- * + * *

*
Backpressure:
*
The operator consumes the current {@code Flowable} in an unbounded manner @@ -6229,7 +6229,7 @@ public final T blockingLast() { * Returns the last item emitted by this {@code Flowable}, or a default value if it emits no * items. *

- * + * *

*
Backpressure:
*
The operator consumes the current {@code Flowable} in an unbounded manner @@ -6292,7 +6292,7 @@ public final Iterable blockingLatest() { * Returns an {@link Iterable} that always returns the item most recently emitted by this * {@code Flowable}. *

- * + * *

*
Backpressure:
*
The operator consumes the current {@code Flowable} in an unbounded manner @@ -6321,7 +6321,7 @@ public final Iterable blockingMostRecent(@NonNull T initialItem) { * Returns an {@link Iterable} that blocks until this {@code Flowable} emits another item, then * returns that item. *

- * + * *

*
Backpressure:
*
The operator consumes the current {@code Flowable} in an unbounded manner @@ -6345,7 +6345,7 @@ public final Iterable blockingNext() { * If this {@code Flowable} completes after emitting a single item, return that item, otherwise * throw a {@link NoSuchElementException}. *

- * + * *

*
Backpressure:
*
The operator consumes the current {@code Flowable} in an unbounded manner @@ -6374,7 +6374,7 @@ public final T blockingSingle() { * more than one item, throw an {@link IllegalArgumentException}; if it emits no items, return a default * value. *

- * + * *

*
Backpressure:
*
The operator consumes the current {@code Flowable} in an unbounded manner @@ -6661,7 +6661,7 @@ public final void blockingSubscribe(@NonNull Subscriber<@NonNull ? super T> subs * current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and expects the current {@code Flowable} to honor it as @@ -6692,7 +6692,7 @@ public final Flowable> buffer(int count) { * current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and expects the current {@code Flowable} to honor it as @@ -6727,7 +6727,7 @@ public final Flowable> buffer(int count, int skip) { * current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and expects the current {@code Flowable} to honor it as @@ -6770,7 +6770,7 @@ public final > Flowable buffer(int count, int * current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and expects the current {@code Flowable} to honor it as @@ -6807,7 +6807,7 @@ public final > Flowable buffer(int count, @No * current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} @@ -6843,7 +6843,7 @@ public final Flowable> buffer(long timespan, long timeskip, @NonNull Tim * notification the event is passed on immediately without first emitting the buffer it is in the process of * assembling. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} @@ -6881,7 +6881,7 @@ public final Flowable> buffer(long timespan, long timeskip, @NonNull Tim * notification the event is passed on immediately without first emitting the buffer it is in the process of * assembling. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} @@ -6926,7 +6926,7 @@ public final > Flowable buffer(long timespan, * notification the event is passed on immediately without first emitting the buffer it is in the process of * assembling. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} @@ -6960,7 +6960,7 @@ public final Flowable> buffer(long timespan, @NonNull TimeUnit unit) { * notification from the current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event * is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} @@ -6998,7 +6998,7 @@ public final Flowable> buffer(long timespan, @NonNull TimeUnit unit, int * current {@code Flowable} issues an {@code onError} notification the event is passed on immediately without first emitting the * buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} @@ -7038,7 +7038,7 @@ public final Flowable> buffer(long timespan, @NonNull TimeUnit unit, @No * current {@code Flowable} issues an {@code onError} notification the event is passed on immediately without first emitting the * buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} @@ -7091,7 +7091,7 @@ public final > Flowable buffer( * if the current {@code Flowable} issues an {@code onError} notification the event is passed on immediately without first emitting * the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} @@ -7126,7 +7126,7 @@ public final Flowable> buffer(long timespan, @NonNull TimeUnit unit, @No * {@code PFlowable}, {@code openingIndicator} or {@code closingIndicator} issues an {@code onError} notification the event is passed * on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it is instead controlled by the given {@code Publisher}s and @@ -7163,7 +7163,7 @@ public final Flowable> buffer( * {@code Flowable}, {@code openingIndicator} or {@code closingIndicator} issues an {@code onError} notification the event is passed * on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it is instead controlled by the given {@code Publisher}s and @@ -7205,7 +7205,7 @@ public final > Flowable b * Returns a {@code Flowable} that emits non-overlapping buffered items from the current {@code Flowable} each time the * specified boundary {@link Publisher} emits an item. *

- * + * *

* Completion of either the source or the boundary {@code Publisher} causes the returned {@code Publisher} to emit the * latest buffer and complete. If either the current {@code Flowable} or the boundary {@code Publisher} issues an {@code onError} notification @@ -7240,7 +7240,7 @@ public final Flowable> buffer(@NonNull Publisher boundaryIndicato * Returns a {@code Flowable} that emits non-overlapping buffered items from the current {@code Flowable} each time the * specified boundary {@link Publisher} emits an item. *

- * + * *

* Completion of either the source or the boundary {@code Publisher} causes the returned {@code Publisher} to emit the * latest buffer and complete. If either the current {@code Flowable} or the boundary {@code Publisher} issues an {@code onError} notification @@ -7279,7 +7279,7 @@ public final Flowable> buffer(@NonNull Publisher boundaryIndicato * Returns a {@code Flowable} that emits non-overlapping buffered items from the current {@code Flowable} each time the * specified boundary {@link Publisher} emits an item. *

- * + * *

* Completion of either the source or the boundary {@code Publisher} causes the returned {@code Publisher} to emit the * latest buffer and complete. If either the current {@code Flowable} or the boundary {@code Publisher} issues an {@code onError} notification @@ -7320,7 +7320,7 @@ public final > Flowable buffer(@NonNull Pu * Returns a {@code Flowable} that subscribes to this {@link Publisher} lazily, caches all of its events * and replays them, in the same order as received, to all the downstream subscribers. *

- * + * *

* This is useful when you want a {@code Publisher} to cache responses and you can't control the * subscribe/cancel behavior of all the {@link Subscriber}s. @@ -7380,7 +7380,7 @@ public final Flowable cache() { * Returns a {@code Flowable} that subscribes to this {@link Publisher} lazily, caches all of its events * and replays them, in the same order as received, to all the downstream subscribers. *

- * + * *

* This is useful when you want a {@code Publisher} to cache responses and you can't control the * subscribe/cancel behavior of all the {@link Subscriber}s. @@ -7446,7 +7446,7 @@ public final Flowable cacheWithInitialCapacity(int initialCapacity) { * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable}, converted to the specified * type. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s @@ -7476,7 +7476,7 @@ public final Flowable cast(@NonNull Class clazz) { * Collects items emitted by the finite source {@link Publisher} into a single mutable data structure and returns * a {@link Single} that emits this structure. *

- * + * *

* This is a simplified version of {@code reduce} that does not need to return the state on each pass. *

@@ -7516,7 +7516,7 @@ public final Single collect(@NonNull Supplier initialItemSup * Collects items emitted by the finite source {@link Publisher} into a single mutable data structure and returns * a {@link Single} that emits this structure. *

- * + * *

* This is a simplified version of {@code reduce} that does not need to return the state on each pass. *

@@ -7587,7 +7587,7 @@ public final Flowable compose(@NonNull FlowableTransformer - * + * *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, * on the upstream thread signaling the new item to be mapped or on the thread where the inner source terminates. To ensure @@ -7624,7 +7624,7 @@ public final Flowable concatMap(@NonNull Function - * + * *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, * on the upstream thread signaling the new item to be mapped or on the thread where the inner source terminates. To ensure @@ -7675,7 +7675,7 @@ public final Flowable concatMap(@NonNull Function - * + * *

* The difference between {@link #concatMap(Function, int)} and this operator is that this operator guarantees the {@code mapper} * function is executed on the specified scheduler. @@ -7721,7 +7721,7 @@ public final Flowable concatMap(@NonNull Function - * + * *

*
Backpressure:
*
The operator expects the upstream to support backpressure. If this {@code Flowable} violates the rule, the operator will @@ -7750,7 +7750,7 @@ public final Completable concatMapCompletable(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure. If this {@code Flowable} violates the rule, the operator will @@ -7787,7 +7787,7 @@ public final Completable concatMapCompletable(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure. If this {@code Flowable} violates the rule, the operator will @@ -7817,7 +7817,7 @@ public final Completable concatMapCompletableDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure. If this {@code Flowable} violates the rule, the operator will @@ -7853,7 +7853,7 @@ public final Completable concatMapCompletableDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure. If this {@code Flowable} violates the rule, the operator will @@ -8233,7 +8233,7 @@ public final Flowable concatMapIterable(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors @@ -8266,7 +8266,7 @@ public final Flowable concatMapMaybe(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors @@ -8306,7 +8306,7 @@ public final Flowable concatMapMaybe(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors @@ -8339,7 +8339,7 @@ public final Flowable concatMapMaybeDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors @@ -8378,7 +8378,7 @@ public final Flowable concatMapMaybeDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors @@ -8423,7 +8423,7 @@ public final Flowable concatMapMaybeDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors @@ -8456,7 +8456,7 @@ public final Flowable concatMapSingle(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors @@ -8496,7 +8496,7 @@ public final Flowable concatMapSingle(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors @@ -8529,7 +8529,7 @@ public final Flowable concatMapSingleDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors @@ -8568,7 +8568,7 @@ public final Flowable concatMapSingleDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors @@ -8612,7 +8612,7 @@ public final Flowable concatMapSingleDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. Both this and the {@code other} {@link Publisher}s @@ -8641,7 +8641,7 @@ public final Flowable concatWith(@NonNull Publisher<@NonNull ? extends T> oth * Returns a {@code Flowable} that emits the items from this {@code Flowable} followed by the success item or error event * of the other {@link SingleSource}. *

- * + * *

*
Backpressure:
*
The operator supports backpressure and makes sure the success item of the other {@code SingleSource} @@ -8668,7 +8668,7 @@ public final Flowable concatWith(@NonNull SingleSource other) { * Returns a {@code Flowable} that emits the items from this {@code Flowable} followed by the success item or terminal events * of the other {@link MaybeSource}. *

- * + * *

*
Backpressure:
*
The operator supports backpressure and makes sure the success item of the other {@code MaybeSource} @@ -8695,7 +8695,7 @@ public final Flowable concatWith(@NonNull MaybeSource other) { * Returns a {@code Flowable} that emits items from this {@code Flowable} and when it completes normally, the * other {@link CompletableSource} is subscribed to and the returned {@code Flowable} emits its terminal events. *

- * + * *

*
Backpressure:
*
The operator does not interfere with backpressure between the current {@code Flowable} and the @@ -8724,7 +8724,7 @@ public final Flowable concatWith(@NonNull CompletableSource other) { * Returns a {@link Single} that emits a {@link Boolean} that indicates whether the current {@code Flowable} emitted a * specified item. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -8752,7 +8752,7 @@ public final Single contains(@NonNull Object item) { * Returns a {@link Single} that counts the total number of items emitted by the current {@code Flowable} and emits * this count as a 64-bit {@link Long}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -8776,7 +8776,7 @@ public final Single count() { * Returns a {@code Flowable} that mirrors the current {@code Flowable}, except that it drops items emitted by the * current {@code Flowable} that are followed by another item within a computed debounce duration. *

- * + * *

* The delivery of the item happens on the thread of the first {@code onNext} or {@code onComplete} * signal of the generated {@link Publisher} sequence, @@ -8819,7 +8819,7 @@ public final Flowable debounce(@NonNull FunctionNote: If items keep being emitted by the current {@code Flowable} faster than the timeout then no items * will be emitted by the resulting {@code Flowable}. *

- * + * *

* Delivery of the item after the grace period happens on the {@code computation} {@link Scheduler}'s * {@code Worker} which if takes too long, a newer item may arrive from the upstream, causing the @@ -8862,7 +8862,7 @@ public final Flowable debounce(long timeout, @NonNull TimeUnit unit) { * Note: If items keep being emitted by the current {@code Flowable} faster than the timeout then no items * will be emitted by the resulting {@code Flowable}. *

- * + * *

* Delivery of the item after the grace period happens on the given {@code Scheduler}'s * {@code Worker} which if takes too long, a newer item may arrive from the upstream, causing the @@ -8905,7 +8905,7 @@ public final Flowable debounce(long timeout, @NonNull TimeUnit unit, @NonNull * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} or a specified default item * if the current {@code Flowable} is empty. *

- * + * *

*
Backpressure:
*
If the current {@code Flowable} is empty, this operator is guaranteed to honor backpressure from downstream. @@ -8935,7 +8935,7 @@ public final Flowable defaultIfEmpty(@NonNull T defaultItem) { * Returns a {@code Flowable} that delays the emissions of the current {@code Flowable} via another {@link Publisher} on a * per-item basis. *

- * + * *

* Note: the resulting {@code Flowable} will immediately propagate any {@code onError} notification * from the current {@code Flowable}. @@ -8971,7 +8971,7 @@ public final Flowable delay(@NonNull Function - * + * *

*
Backpressure:
*
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
@@ -8999,7 +8999,7 @@ public final Flowable delay(long time, @NonNull TimeUnit unit) { * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} shifted forward in time by a * specified delay. If {@code delayError} is {@code true}, error notifications will also be delayed. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
@@ -9030,7 +9030,7 @@ public final Flowable delay(long time, @NonNull TimeUnit unit, boolean delayE * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} shifted forward in time by a * specified delay. The {@code onError} notification from the current {@code Flowable} is not delayed. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
@@ -9060,7 +9060,7 @@ public final Flowable delay(long time, @NonNull TimeUnit unit, @NonNull Sched * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} shifted forward in time by a * specified delay. If {@code delayError} is {@code true}, error notifications will also be delayed. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
@@ -9096,7 +9096,7 @@ public final Flowable delay(long time, @NonNull TimeUnit unit, @NonNull Sched * Returns a {@code Flowable} that delays the subscription to and emissions from the current {@code Flowable} via another * {@link Publisher} on a per-item basis. *

- * + * *

* Note: the resulting {@code Flowable} will immediately propagate any {@code onError} notification * from the current {@code Flowable}. @@ -9163,7 +9163,7 @@ public final Flowable delaySubscription(@NonNull Publisher subscriptio /** * Returns a {@code Flowable} that delays the subscription to the current {@code Flowable} by a given amount of time. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
@@ -9191,7 +9191,7 @@ public final Flowable delaySubscription(long time, @NonNull TimeUnit unit) { * Returns a {@code Flowable} that delays the subscription to the current {@code Flowable} by a given amount of time, * both waiting and subscribing on a given {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
@@ -9222,7 +9222,7 @@ public final Flowable delaySubscription(long time, @NonNull TimeUnit unit, @N * {@link Notification} objects extracted from the source items via a selector function * into their respective {@link Subscriber} signal types. *

- * + * *

* The intended use of the {@code selector} function is to perform a * type-safe identity mapping (see example) on a source that is already of type @@ -9280,7 +9280,7 @@ public final Flowable dematerialize(@NonNull Function<@NonNull ? super T, * Returns a {@code Flowable} that emits all items emitted by the current {@code Flowable} that are distinct * based on {@link Object#equals(Object)} comparison. *

- * + * *

* It is recommended the elements' class {@code T} in the flow overrides the default {@code Object.equals()} and {@link Object#hashCode()} to provide * a meaningful comparison between items as the default Java implementation only considers reference equivalence. @@ -9322,7 +9322,7 @@ public final Flowable distinct() { * to a key selector function and based on {@link Object#equals(Object)} comparison of the objects * returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} and {@link Object#hashCode()} to provide * a meaningful comparison between the key objects as the default Java implementation only considers reference equivalence. @@ -9367,7 +9367,7 @@ public final Flowable distinct(@NonNull Function keySelecto * to a key selector function and based on {@link Object#equals(Object)} comparison of the objects * returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} and {@link Object#hashCode()} to provide * a meaningful comparison between the key objects as the default Java implementation only considers reference equivalence. @@ -9405,7 +9405,7 @@ public final Flowable distinct(@NonNull Function keySelecto * Returns a {@code Flowable} that emits all items emitted by the current {@code Flowable} that are distinct from their * immediate predecessors based on {@link Object#equals(Object)} comparison. *

- * + * *

* It is recommended the elements' class {@code T} in the flow overrides the default {@code Object.equals()} to provide * a meaningful comparison between items as the default Java implementation only considers reference equivalence. @@ -9447,7 +9447,7 @@ public final Flowable distinctUntilChanged() { * immediate predecessors, according to a key selector function and based on {@link Object#equals(Object)} comparison * of those objects returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} to provide * a meaningful comparison between the key objects as the default Java implementation only considers reference equivalence. @@ -9494,7 +9494,7 @@ public final Flowable distinctUntilChanged(@NonNull Function - * + * *

* Note that the operator always retains the latest item from upstream regardless of the comparison result * and uses it in the next comparison with the next upstream item. @@ -9594,7 +9594,7 @@ public final Flowable doAfterNext(@NonNull Consumer onAfterNext) { * Registers an {@link Action} to be called when this {@link Publisher} invokes either * {@link Subscriber#onComplete onComplete} or {@link Subscriber#onError onError}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -9622,7 +9622,7 @@ public final Flowable doAfterTerminate(@NonNull Action onAfterTerminate) { /** * Calls the cancel {@link Action} if the downstream cancels the sequence. *

- * + * *

* The action is shared between subscriptions and thus may be called concurrently from multiple * threads; the action must be thread-safe. @@ -9654,7 +9654,7 @@ public final Flowable doOnCancel(@NonNull Action onCancel) { /** * Invokes an {@link Action} just before the current {@code Flowable} calls {@code onComplete}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s @@ -9682,7 +9682,7 @@ public final Flowable doOnComplete(@NonNull Action onComplete) { * Calls the appropriate onXXX consumer (shared between all subscribers) whenever a signal with the same type * passes through, before forwarding them to downstream. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s @@ -9712,7 +9712,7 @@ private Flowable doOnEach(@NonNull Consumer onNext, @NonNull Consu * Invokes a {@link Consumer} with a {@link Notification} instances matching the signals emitted by the current {@code Flowable} * before they are forwarded to the downstream. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s @@ -9750,7 +9750,7 @@ public final Flowable doOnEach(@NonNull Consumer<@NonNull ? super Notificatio * {@code onNext} or the {@code onComplete} method of the supplied {@code Subscriber} throws, the downstream will be * terminated and will receive this thrown exception. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s @@ -9786,7 +9786,7 @@ public final Flowable doOnEach(@NonNull Subscriber<@NonNull ? super T> subscr * In case the {@code onError} action throws, the downstream will receive a composite exception containing * the original exception and the exception thrown by {@code onError}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s @@ -9814,7 +9814,7 @@ public final Flowable doOnError(@NonNull Consumer onError) * Calls the appropriate {@code onXXX} method (shared between all {@link Subscriber}s) for the lifecycle events of * the sequence (subscription, cancellation, requesting). *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s @@ -9848,7 +9848,7 @@ public final Flowable doOnLifecycle(@NonNull Consumer o /** * Calls the given {@link Consumer} with the value emitted by the current {@code Flowable} before forwarding it to the downstream. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s @@ -9909,7 +9909,7 @@ public final Flowable doOnRequest(@NonNull LongConsumer onRequest) { * subscription from the downstream before forwarding it to the subscriber's * {@link Subscriber#onSubscribe(Subscription) onSubscribe} method. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s @@ -9936,7 +9936,7 @@ public final Flowable doOnSubscribe(@NonNull Consumer o * Calls the given {@link Action} when the current {@code Flowable} completes normally or with an error before those signals * are forwarded to the downstream. *

- * + * *

* This differs from {@code doAfterTerminate} in that this happens before the {@code onComplete} or * {@code onError} notification. @@ -9968,7 +9968,7 @@ public final Flowable doOnTerminate(@NonNull Action onTerminate) { * Returns a {@link Maybe} that emits the single item at a specified index in a sequence of emissions from * this {@code Flowable} or completes if this {@code Flowable} sequence has fewer elements than index. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in a bounded manner.
@@ -9997,7 +9997,7 @@ public final Maybe elementAt(long index) { * Returns a {@link Single} that emits the item found at a specified index in a sequence of emissions from * this {@code Flowable}, or a default item if that index is out of range. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in a bounded manner.
@@ -10031,7 +10031,7 @@ public final Single elementAt(long index, @NonNull T defaultItem) { * Returns a {@link Single} that emits the item found at a specified index in a sequence of emissions from * this {@code Flowable} or signals a {@link NoSuchElementException} if this {@code Flowable} has fewer elements than index. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in a bounded manner.
@@ -10060,7 +10060,7 @@ public final Single elementAtOrError(long index) { /** * Filters items emitted by the current {@code Flowable} by only emitting those that satisfy a specified predicate. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -10162,7 +10162,7 @@ public final Single firstOrError() { * by the current {@code Flowable}, where that function returns a {@link Publisher}, and then merging those resulting * {@code Publisher}s and emitting the results of this merger. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -10194,7 +10194,7 @@ public final Flowable flatMap(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -10230,7 +10230,7 @@ public final Flowable flatMap(@NonNull Function --> - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -10267,7 +10267,7 @@ public final Flowable flatMap(@NonNull Function --> - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -10307,7 +10307,7 @@ public final Flowable flatMap(@NonNull Function --> - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -10359,7 +10359,7 @@ public final Flowable flatMap(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -10403,7 +10403,7 @@ public final Flowable flatMap( * {@code Flowable} and then flattens the {@link Publisher}s returned from these functions and emits the resulting items, * while limiting the maximum number of concurrent subscriptions to these {@code Publisher}s. * - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -10452,7 +10452,7 @@ public final Flowable flatMap( * Returns a {@code Flowable} that emits the results of a specified function to the pair of values emitted by the * current {@code Flowable} and a specified collection {@link Publisher}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -10489,7 +10489,7 @@ public final Flowable flatMap(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -10530,7 +10530,7 @@ public final Flowable flatMap(@NonNull Function --> - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -10575,7 +10575,7 @@ public final Flowable flatMap(@NonNull Function --> - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -10626,7 +10626,7 @@ public final Flowable flatMap(@NonNull Function --> - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed @@ -11104,7 +11104,7 @@ public final Disposable forEachWhile(@NonNull Predicate onNext, @NonN * source terminates, the next emission by the source having the same key will trigger a new * {@code GroupedFlowable} emission. *

- * + * *

* Note: A {@code GroupedFlowable} will cache the items it is to emit until such time as it * is subscribed to. For this reason, in order to avoid memory leaks, you should not simply ignore those @@ -11162,7 +11162,7 @@ public final Flowable> groupBy(@NonNull Function - * + * *

* Note: A {@code GroupedFlowable} will cache the items it is to emit until such time as it * is subscribed to. For this reason, in order to avoid memory leaks, you should not simply ignore those @@ -11221,7 +11221,7 @@ public final Flowable> groupBy(@NonNull Function - * + * *

* Note: A {@code GroupedFlowable} will cache the items it is to emit until such time as it * is subscribed to. For this reason, in order to avoid memory leaks, you should not simply ignore those @@ -11285,7 +11285,7 @@ public final Flowable> groupBy(@NonNull Function - * + * *

* Note: A {@code GroupedFlowable} will cache the items it is to emit until such time as it * is subscribed to. For this reason, in order to avoid memory leaks, you should not simply ignore those @@ -11350,7 +11350,7 @@ public final Flowable> groupBy(@NonNull Function - * + * *

* Note: A {@code GroupedFlowable} will cache the items it is to emit until such time as it * is subscribed to. For this reason, in order to avoid memory leaks, you should not simply ignore those @@ -11463,7 +11463,7 @@ public final Flowable> groupBy(@NonNull Function

* *

- * + * *

* Note: A {@code GroupedFlowable} will cache the items it is to emit until such time as it * is subscribed to. For this reason, in order to avoid memory leaks, you should not simply ignore those @@ -11544,7 +11544,7 @@ public final Flowable> groupBy(@NonNull Function - * + * *

*
Backpressure:
*
The operator doesn't support backpressure and consumes all participating {@code Publisher}s in @@ -11616,7 +11616,7 @@ public final Flowable hide() { /** * Ignores all items emitted by the current {@code Flowable} and only calls {@code onComplete} or {@code onError}. *

- * + * *

*
Backpressure:
*
This operator ignores backpressure as it doesn't emit any elements and consumes the current {@code Flowable} @@ -11642,7 +11642,7 @@ public final Completable ignoreElements() { * In Rx.Net this is negated as the {@code any} {@link Subscriber} but we renamed this in RxJava to better match Java * naming idioms. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -11668,7 +11668,7 @@ public final Single isEmpty() { * There are no guarantees in what order the items get combined when multiple * items from one or both source {@code Publisher}s overlap. *

- * + * *

*
Backpressure:
*
The operator doesn't support backpressure and consumes all participating {@code Publisher}s in @@ -11717,7 +11717,7 @@ public final Flowable join( * Returns a {@link Maybe} that emits the last item emitted by this {@code Flowable} or completes if * this {@code Flowable} is empty. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -11741,7 +11741,7 @@ public final Maybe lastElement() { * Returns a {@link Single} that emits only the last item emitted by this {@code Flowable}, or a default item * if this {@code Flowable} completes without emitting any items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -11949,7 +11949,7 @@ public final Flowable lift(@NonNull FlowableOperator - * + * *
*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -11979,7 +11979,7 @@ public final Flowable lift(@NonNull FlowableOperatorand notifications from the current * {@code Flowable} into emissions marked with their original types within {@link Notification} objects. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and expects it from the current {@code Flowable}. @@ -12003,7 +12003,7 @@ public final Flowable> materialize() { /** * Flattens this and another {@link Publisher} into a single {@code Publisher}, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code mergeWith} method. @@ -12033,7 +12033,7 @@ public final Flowable mergeWith(@NonNull Publisher<@NonNull ? extends T> othe /** * Merges the sequence of items of this {@code Flowable} with the success value of the other {@link SingleSource}. *

- * + * *

* The success value of the other {@code SingleSource} can get interleaved at any point of this * {@code Flowable} sequence. @@ -12063,7 +12063,7 @@ public final Flowable mergeWith(@NonNull SingleSource other) { * Merges the sequence of items of this {@code Flowable} with the success value of the other {@link MaybeSource} * or waits for both to complete normally if the {@code MaybeSource} is empty. *

- * + * *

* The success value of the other {@code MaybeSource} can get interleaved at any point of this * {@code Flowable} sequence. @@ -12093,7 +12093,7 @@ public final Flowable mergeWith(@NonNull MaybeSource other) { * Relays the items of this {@code Flowable} and completes only when the other {@link CompletableSource} completes * as well. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -12123,7 +12123,7 @@ public final Flowable mergeWith(@NonNull CompletableSource other) { *

Note that {@code onError} notifications will cut ahead of {@code onNext} notifications on the emission thread if {@code Scheduler} is truly * asynchronous. If strict event ordering is required, consider using the {@link #observeOn(Scheduler, boolean)} overload. *

- * + * *

* This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s Worker thread, * which may result in a longer than expected occupation of this thread. In other terms, @@ -12171,7 +12171,7 @@ public final Flowable observeOn(@NonNull Scheduler scheduler) { * Signals the items and terminal signals of the current {@code Flowable} on the specified {@link Scheduler}, * asynchronously with a bounded buffer and optionally delays {@code onError} notifications. *

- * + * *

* This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s Worker thread, * which may result in a longer than expected occupation of this thread. In other terms, @@ -12223,7 +12223,7 @@ public final Flowable observeOn(@NonNull Scheduler scheduler, boolean delayEr * Signals the items and terminal signals of the current {@code Flowable} on the specified {@link Scheduler}, * asynchronously with a bounded buffer of configurable size and optionally delays {@code onError} notifications. *

- * + * *

* This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s Worker thread, * which may result in a longer than expected occupation of this thread. In other terms, @@ -12278,7 +12278,7 @@ public final Flowable observeOn(@NonNull Scheduler scheduler, boolean delayEr /** * Filters the items emitted by the current {@code Flowable}, only emitting those of the specified type. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -12307,7 +12307,7 @@ public final Flowable ofType(@NonNull Class clazz) { * Buffers an unlimited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the * downstream to consume the items at its own place. *

- * + * *

* An error from the current {@code Flowable} will cut ahead of any unconsumed item. Use {@link #onBackpressureBuffer(boolean)} * to have the operator keep the original signal order. @@ -12335,7 +12335,7 @@ public final Flowable onBackpressureBuffer() { * Buffers an unlimited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the * downstream to consume the items at its own place, optionally delaying an error until all buffered items have been consumed. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded @@ -12364,7 +12364,7 @@ public final Flowable onBackpressureBuffer(boolean delayError) { * {@link MissingBackpressureException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered * items, and canceling the flow. *

- * + * *

* An error from the current {@code Flowable} will cut ahead of any unconsumed item. Use {@link #onBackpressureBuffer(int, boolean)} * to have the operator keep the original signal order. @@ -12397,7 +12397,7 @@ public final Flowable onBackpressureBuffer(int capacity) { * {@link MissingBackpressureException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered * items, and canceling the flow. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded @@ -12431,7 +12431,7 @@ public final Flowable onBackpressureBuffer(int capacity, boolean delayError) * {@link MissingBackpressureException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered * items, and canceling the flow. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded @@ -12468,7 +12468,7 @@ public final Flowable onBackpressureBuffer(int capacity, boolean delayError, * {@link MissingBackpressureException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered * items, canceling the flow and calling the {@code onOverflow} action. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded @@ -12508,7 +12508,7 @@ public final Flowable onBackpressureBuffer(int capacity, boolean delayError, * {@link MissingBackpressureException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered * items, canceling the flow and calling the {@code onOverflow} action. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded @@ -12549,7 +12549,7 @@ public final Flowable onBackpressureBuffer(int capacity, @NonNull Action onOv * * *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded @@ -12581,7 +12581,7 @@ public final Flowable onBackpressureBuffer(long capacity, @Nullable Action on * Drops items from the current {@code Flowable} if the downstream is not ready to receive new items (indicated * by a lack of {@link Subscription#request(long)} calls from it). *

- * + * *

* If the downstream request count hits 0 then the resulting {@code Flowable} will refrain from calling {@code onNext} until * the {@link Subscriber} invokes {@code request(n)} again to increase the request count. @@ -12609,7 +12609,7 @@ public final Flowable onBackpressureDrop() { * by a lack of {@link Subscription#request(long)} calls from it) and calls the given {@link Consumer} with such * dropped items. *

- * + * *

* If the downstream request count hits 0 then the resulting {@code Flowable} will refrain from calling {@code onNext} until * the {@link Subscriber} invokes {@code request(n)} again to increase the request count. @@ -12641,7 +12641,7 @@ public final Flowable onBackpressureDrop(@NonNull Consumer onDrop) * new items (indicated by a lack of {@link Subscription#request(long)} calls from it) and emits this latest * item when the downstream becomes ready. *

- * + * *

* Its behavior is logically equivalent to {@code blockingLatest()} with the exception that * the downstream is not blocking while requesting more values. @@ -12725,7 +12725,7 @@ public final Flowable onErrorComplete(@NonNull Predicate p * Resumes the flow with a {@link Publisher} returned for the failure {@link Throwable} of the current {@code Flowable} by a * function instead of signaling the error via {@code onError}. *

- * + * *

* By default, when a {@code Publisher} encounters an error that prevents it from emitting the expected item to * its {@link Subscriber}, the {@code Publisher} invokes its {@code Subscriber}'s {@code onError} method, and then quits @@ -12770,7 +12770,7 @@ public final Flowable onErrorResumeNext(@NonNull Function - * + * *

* By default, when a {@code Publisher} encounters an error that prevents it from emitting the expected item to * its {@link Subscriber}, the {@code Publisher} invokes its {@code Subscriber}'s {@code onError} method, and then quits @@ -12815,7 +12815,7 @@ public final Flowable onErrorResumeWith(@NonNull Publisher<@NonNull ? extends * Ends the flow with a last item returned by a function for the {@link Throwable} error signaled by the current * {@code Flowable} instead of signaling the error via {@code onError}. *

- * + * *

* By default, when a {@link Publisher} encounters an error that prevents it from emitting the expected item to * its {@link Subscriber}, the {@code Publisher} invokes its {@code Subscriber}'s {@code onError} method, and then quits @@ -12855,7 +12855,7 @@ public final Flowable onErrorReturn(@NonNull Function - * + * *

* By default, when a {@link Publisher} encounters an error that prevents it from emitting the expected item to * its {@link Subscriber}, the {@code Publisher} invokes its {@code Subscriber}'s {@code onError} method, and then quits @@ -13015,7 +13015,7 @@ public final ParallelFlowable parallel(int parallelism, int prefetch) { * {@link ConnectableFlowable#connect connect} method is called before it begins emitting items to those * {@link Subscriber}s that have subscribed to it. *

- * + * *

*
Backpressure:
*
The returned {@code ConnectableFlowable} honors backpressure for each of its {@code Subscriber}s @@ -13040,7 +13040,7 @@ public final ConnectableFlowable publish() { * Returns a {@code Flowable} that emits the results of invoking a specified selector on items emitted by a * {@link ConnectableFlowable} that shares a single subscription to the underlying sequence. *

- * + * *

*
Backpressure:
*
The operator expects the current {@code Flowable} to honor backpressure and if this expectation is @@ -13074,7 +13074,7 @@ public final Flowable publish(@NonNull Function, ? ex * Returns a {@code Flowable} that emits the results of invoking a specified selector on items emitted by a * {@link ConnectableFlowable} that shares a single subscription to the underlying sequence. *

- * + * *

*
Backpressure:
*
The operator expects the current {@code Flowable} to honor backpressure and if this expectation is @@ -13114,7 +13114,7 @@ public final Flowable publish(@NonNull Function, ? ex * {@link ConnectableFlowable#connect connect} method is called before it begins emitting items to those * {@link Subscriber}s that have subscribed to it. *

- * + * *

*
Backpressure:
*
The returned {@code ConnectableFlowable} honors backpressure for each of its {@code Subscriber}s @@ -13172,7 +13172,7 @@ public final Flowable rebatchRequests(int n) { * {@code Flowable} into the same function, and so on until all items have been emitted by the current and finite {@code Flowable}, * and emits the final result from the final call to your function as its sole item. *

- * + * *

* This technique, which is called "reduce" here, is sometimes called "aggregate," "fold," "accumulate," * "compress," or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method @@ -13212,7 +13212,7 @@ public final Maybe reduce(@NonNull BiFunction reducer) { * emitted by the current {@code Flowable} into the same function, and so on until all items have been emitted by the * current and finite {@code Flowable}, emitting the final result from the final call to your function as its sole item. *

- * + * *

* This technique, which is called "reduce" here, is sometimes called "aggregate," "fold," "accumulate," * "compress," or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method @@ -13276,7 +13276,7 @@ public final Maybe reduce(@NonNull BiFunction reducer) { * all items have been emitted by the current and finite {@code Flowable}, emitting the final result from the final call to your * function as its sole item. *

- * + * *

* This technique, which is called "reduce" here, is sometimes called "aggregate", "fold", "accumulate", * "compress", or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method @@ -13317,7 +13317,7 @@ public final Maybe reduce(@NonNull BiFunction reducer) { /** * Returns a {@code Flowable} that repeats the sequence of items emitted by the current {@code Flowable} indefinitely. *

- * + * *

*
Backpressure:
*
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. @@ -13341,7 +13341,7 @@ public final Flowable repeat() { * Returns a {@code Flowable} that repeats the sequence of items emitted by the current {@code Flowable} at most * {@code count} times. *

- * + * *

*
Backpressure:
*
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. @@ -13376,7 +13376,7 @@ public final Flowable repeat(long times) { * Returns a {@code Flowable} that repeats the sequence of items emitted by the current {@code Flowable} until * the provided stop function returns {@code true}. *

- * + * *

*
Backpressure:
*
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. @@ -13410,7 +13410,7 @@ public final Flowable repeatUntil(@NonNull BooleanSupplier stop) { * call {@code onComplete} or {@code onError} on the child subscription. Otherwise, this {@code Publisher} will * resubscribe to the current {@code Flowable}. *

- * + * *

*
Backpressure:
*
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. @@ -13440,7 +13440,7 @@ public final Flowable repeatWhen(@NonNull Function, * {@code Flowable} resembles an ordinary {@code Flowable}, except that it does not begin emitting items when it is * subscribed to, but only when its {@code connect} method is called. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -13465,7 +13465,7 @@ public final ConnectableFlowable replay() { * Returns a {@code Flowable} that emits items that are the results of invoking a specified selector on the items * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -13501,7 +13501,7 @@ public final Flowable replay(@NonNull Function, ? ext * Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -13542,7 +13542,7 @@ public final Flowable replay(@NonNull Function, ? ext * Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -13585,7 +13585,7 @@ public final Flowable replay(@NonNull Function, ? ext * Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -13627,7 +13627,7 @@ public final Flowable replay(@NonNull Function, ? ext * Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -13678,7 +13678,7 @@ public final Flowable replay(@NonNull Function, ? ext * Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -13728,7 +13728,7 @@ public final Flowable replay(@NonNull Function, ? ext * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}, * replaying all items that were emitted within a specified time window. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -13764,7 +13764,7 @@ public final Flowable replay(@NonNull Function, ? ext * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}, * replaying all items that were emitted within a specified time window. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -13806,7 +13806,7 @@ public final Flowable replay(@NonNull Function, ? ext * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}, * replaying all items that were emitted within a specified time window. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -13856,7 +13856,7 @@ public final Flowable replay(@NonNull Function, ? ext * To ensure no beyond-bufferSize items are referenced, * use the {@link #replay(int, boolean)} overload with {@code eagerTruncate = true}. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -13887,7 +13887,7 @@ public final ConnectableFlowable replay(int bufferSize) { * an ordinary {@code Flowable}, except that it does not begin emitting items when it is subscribed to, but only * when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -13926,7 +13926,7 @@ public final ConnectableFlowable replay(int bufferSize, boolean eagerTruncate * {@code Flowable} resembles an ordinary {@code Flowable}, except that it does not begin emitting items when it is * subscribed to, but only when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -13967,7 +13967,7 @@ public final ConnectableFlowable replay(int bufferSize, long time, @NonNull T * connectable {@code Flowable} resembles an ordinary {@code Flowable}, except that it does not begin emitting items * when it is subscribed to, but only when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -14017,7 +14017,7 @@ public final ConnectableFlowable replay(int bufferSize, long time, @NonNull T * {@code bufferSize} source emissions. To ensure no out-of-date or beyond-bufferSize items * are referenced, set {@code eagerTruncate = true}. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child @@ -14061,7 +14061,7 @@ public final ConnectableFlowable replay(int bufferSize, long time, @NonNull T * resembles an ordinary {@code Flowable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

* Note that the internal buffer may retain strong references to the oldest item. To ensure no out-of-date items * are referenced, use the {@link #replay(long, TimeUnit, Scheduler, boolean)} overload with {@code eagerTruncate = true}. @@ -14096,7 +14096,7 @@ public final ConnectableFlowable replay(long time, @NonNull TimeUnit unit) { * resembles an ordinary {@code Flowable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

* Note that the internal buffer may retain strong references to the oldest item. To ensure no out-of-date items * are referenced, use the {@link #replay(long, TimeUnit, Scheduler, boolean)} overload with {@code eagerTruncate = true}. @@ -14136,7 +14136,7 @@ public final ConnectableFlowable replay(long time, @NonNull TimeUnit unit, @N * resembles an ordinary {@code Flowable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

* Note that the internal buffer may retain strong references to the oldest item. To ensure no out-of-date items * are referenced, set {@code eagerTruncate = true}. @@ -14176,7 +14176,7 @@ public final ConnectableFlowable replay(long time, @NonNull TimeUnit unit, @N * Returns a {@code Flowable} that mirrors the current {@code Flowable}, resubscribing to it if it calls {@code onError} * (infinite retry count). *

- * + * *

* If the current {@code Flowable} calls {@link Subscriber#onError}, this method will resubscribe to the current * {@code Flowable} rather than propagating the {@code onError} call. @@ -14208,7 +14208,7 @@ public final Flowable retry() { * Returns a {@code Flowable} that mirrors the current {@code Flowable}, resubscribing to it if it calls {@code onError} * and the predicate returns {@code true} for that specific exception and retry count. *

- * + * *

*
Backpressure:
*
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. @@ -14239,7 +14239,7 @@ public final Flowable retry(@NonNull BiPredicate<@NonNull ? super Integer, @N * Returns a {@code Flowable} that mirrors the current {@code Flowable}, resubscribing to it if it calls {@code onError} * up to a specified number of retries. *

- * + * *

* If the current {@code Flowable} calls {@link Subscriber#onError}, this method will resubscribe to the current * {@code Flowable} for a maximum of {@code count} resubscriptions rather than propagating the @@ -14352,7 +14352,7 @@ public final Flowable retryUntil(@NonNull BooleanSupplier stop) { * {@code onComplete} or {@code onError} on the child subscription. Otherwise, this {@code Publisher} will * resubscribe to the current {@code Flowable}. *

- * + * *

* Example: * @@ -14462,7 +14462,7 @@ public final void safeSubscribe(@NonNull Subscriber<@NonNull ? super T> subscrib * Returns a {@code Flowable} that emits the most recently emitted item (if any) emitted by the current {@code Flowable} * within periodic time intervals. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -14528,7 +14528,7 @@ public final Flowable sample(long period, @NonNull TimeUnit unit, boolean emi * Returns a {@code Flowable} that emits the most recently emitted item (if any) emitted by the current {@code Flowable} * within periodic time intervals, where the intervals are defined on a particular {@link Scheduler}. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -14674,7 +14674,7 @@ public final Flowable sample(@NonNull Publisher sampler, boolean emitL * {@code Floawble} into the same function, and so on until all items have been emitted by the current {@code Flowable}, * emitting the result of each of these iterations. *

- * + * *

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

@@ -14708,7 +14708,7 @@ public final Flowable scan(@NonNull BiFunction accumulator) { * the current {@code Flowable} into the same function, and so on until all items have been emitted by the current * {@code Flowable}, emitting the result of each of these iterations. *

- * + * *

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

@@ -14762,7 +14762,7 @@ public final Flowable scan(@NonNull BiFunction accumulator) { * the current {@code Flowable} into the same function, and so on until all items have been emitted by the current * {@code Flowable}, emitting the result of each of these iterations. *

- * + * *

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

@@ -14807,7 +14807,7 @@ public final Flowable scan(@NonNull BiFunction accumulator) { * {@code onNext} from two different threads concurrently. You can force such a {@code Publisher} to be * well-behaved and sequential by applying the {@code serialize} method to it. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -14834,7 +14834,7 @@ public final Flowable serialize() { *

* This is an alias for {@link #publish()}.{@link ConnectableFlowable#refCount() refCount()}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure and expects the current {@code Flowable} to honor backpressure as well. @@ -14860,7 +14860,7 @@ public final Flowable share() { * signals exactly one item or signals an {@link IllegalArgumentException} if this {@code Flowable} signals * more than one item. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -14885,7 +14885,7 @@ public final Maybe singleElement() { * emits only a single item, or a default item if the current {@code Flowable} emits no items. If the current * {@code Flowable} emits more than one item, an {@link IllegalArgumentException} is signaled instead. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -14939,7 +14939,7 @@ public final Single singleOrError() { * Returns a {@code Flowable} that skips the first {@code count} items emitted by the current {@code Flowable} and emits * the remainder. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -14972,7 +14972,7 @@ public final Flowable skip(long count) { * Returns a {@code Flowable} that skips values emitted by the current {@code Flowable} before a specified time window * elapses. *

- * + * *

*
Backpressure:
*
The operator doesn't support backpressure as it uses time to skip an arbitrary number of elements and @@ -15002,7 +15002,7 @@ public final Flowable skip(long time, @NonNull TimeUnit unit) { * Returns a {@code Flowable} that skips values emitted by the current {@code Flowable} before a specified time window * on a specified {@link Scheduler} elapses. *

- * + * *

*
Backpressure:
*
The operator doesn't support backpressure as it uses time to skip an arbitrary number of elements and @@ -15033,7 +15033,7 @@ public final Flowable skip(long time, @NonNull TimeUnit unit, @NonNull Schedu * Returns a {@code Flowable} that drops a specified number of items from the end of the sequence emitted by the * current {@code Flowable}. *

- * + * *

* This {@link Subscriber} accumulates a queue long enough to store the first {@code count} items. As more items are * received, items are taken from the front of the queue and emitted by the resulting {@code Flowable}. This causes @@ -15071,7 +15071,7 @@ public final Flowable skipLast(int count) { * Returns a {@code Flowable} that drops items emitted by the current {@code Flowable} during a specified time window * before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -15103,7 +15103,7 @@ public final Flowable skipLast(long time, @NonNull TimeUnit unit) { * Returns a {@code Flowable} that drops items emitted by the current {@code Flowable} during a specified time window * before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -15138,7 +15138,7 @@ public final Flowable skipLast(long time, @NonNull TimeUnit unit, boolean del * Returns a {@code Flowable} that drops items emitted by the current {@code Flowable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -15171,7 +15171,7 @@ public final Flowable skipLast(long time, @NonNull TimeUnit unit, @NonNull Sc * Returns a {@code Flowable} that drops items emitted by the current {@code Flowable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -15207,7 +15207,7 @@ public final Flowable skipLast(long time, @NonNull TimeUnit unit, @NonNull Sc * Returns a {@code Flowable} that drops items emitted by the current {@code Flowable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -15251,7 +15251,7 @@ public final Flowable skipLast(long time, @NonNull TimeUnit unit, @NonNull Sc * Returns a {@code Flowable} that skips items emitted by the current {@code Flowable} until a second {@link Publisher} emits * an item. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -15281,7 +15281,7 @@ public final Flowable skipUntil(@NonNull Publisher other) { * Returns a {@code Flowable} that skips all items emitted by the current {@code Flowable} as long as a specified * condition holds {@code true}, but emits all further source items as soon as the condition becomes {@code false}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -15367,7 +15367,7 @@ public final Flowable sorted(@NonNull Comparator<@NonNull ? super T> comparat * Returns a {@code Flowable} that emits the items in a specified {@link Iterable} before it begins to emit items * emitted by the current {@code Flowable}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The Current {@code Flowable} @@ -15473,7 +15473,7 @@ public final Flowable startWith(@NonNull MaybeSource other) { * Returns a {@code Flowable} that emits the items in a specified {@link Publisher} before it begins to emit * items emitted by the current {@code Flowable}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. Both this and the {@code other} {@code Publisher}s @@ -15502,7 +15502,7 @@ public final Flowable startWith(@NonNull Publisher<@NonNull ? extends T> othe * Returns a {@code Flowable} that emits a specified item before it begins to emit items emitted by the current * {@code Flowable}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The current {@code Flowable} @@ -15534,7 +15534,7 @@ public final Flowable startWithItem(@NonNull T item) { * Returns a {@code Flowable} that emits the specified items before it begins to emit items emitted by the current * {@code Flowable}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The current {@code Flowable} @@ -15815,7 +15815,7 @@ public final void subscribe(@NonNull FlowableSubscriber<@NonNull ? super T> subs * chain, it is recommended to use {@code subscribeOn(scheduler, false)} instead * to avoid same-pool deadlock because requests may pile up behind an eager/blocking emitter. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -15850,7 +15850,7 @@ public final Flowable subscribeOn(@NonNull Scheduler scheduler) { * chain, it is recommended to have {@code requestOn} {@code false} to avoid same-pool deadlock * because requests may pile up behind an eager/blocking emitter. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -15919,7 +15919,7 @@ public final Flowable switchIfEmpty(@NonNull Publisher<@NonNull ? extends T> * The resulting {@code Flowable} completes if both the current {@code Flowable} and the last inner {@code Publisher}, if any, complete. * If the current {@code Flowable} signals an {@code onError}, the inner {@code Publisher} is canceled and the error delivered in-sequence. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an @@ -15955,7 +15955,7 @@ public final Flowable switchMap(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an @@ -16090,7 +16090,7 @@ public final Completable switchMapCompletableDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an @@ -16128,7 +16128,7 @@ public final Flowable switchMapDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an @@ -16180,7 +16180,7 @@ Flowable switchMap0(Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The main {@code Flowable} is consumed in an @@ -16221,7 +16221,7 @@ public final Flowable switchMapMaybe(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The main {@code Flowable} is consumed in an @@ -16254,7 +16254,7 @@ public final Flowable switchMapMaybeDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The main {@code Flowable} is consumed in an @@ -16295,7 +16295,7 @@ public final Flowable switchMapSingle(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The main {@code Flowable} is consumed in an @@ -16326,7 +16326,7 @@ public final Flowable switchMapSingleDelayError(@NonNull Function - * + * *

* This method returns a {@code Flowable} that will invoke a subscribing {@link Subscriber}'s * {@link Subscriber#onNext onNext} function a maximum of {@code count} times before invoking @@ -16380,7 +16380,7 @@ public final Flowable take(long count) { * If time runs out before the {@code Flowable} completes normally, the {@code onComplete} event will be * signaled on the default {@code computation} {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -16412,7 +16412,7 @@ public final Flowable take(long time, @NonNull TimeUnit unit) { * If time runs out before the {@code Flowable} completes normally, the {@code onComplete} event will be * signaled on the provided {@code Scheduler}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -16443,7 +16443,7 @@ public final Flowable take(long time, @NonNull TimeUnit unit, @NonNull Schedu * Returns a {@code Flowable} that emits at most the last {@code count} items emitted by the current {@code Flowable}. If the source emits fewer than * {@code count} items then all of its items are emitted. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream if the {@code count} is non-zero; ignores @@ -16481,7 +16481,7 @@ public final Flowable takeLast(int count) { * Returns a {@code Flowable} that emits at most a specified number of items from the current {@code Flowable} that were * emitted in a specified window of time before the current {@code Flowable} completed. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -16515,7 +16515,7 @@ public final Flowable takeLast(long count, long time, @NonNull TimeUnit unit) * emitted in a specified window of time before the current {@code Flowable} completed, where the timing information is * provided by a given {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -16551,7 +16551,7 @@ public final Flowable takeLast(long count, long time, @NonNull TimeUnit unit, * emitted in a specified window of time before the current {@code Flowable} completed, where the timing information is * provided by a given {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -16597,7 +16597,7 @@ public final Flowable takeLast(long count, long time, @NonNull TimeUnit unit, * Returns a {@code Flowable} that emits the items from the current {@code Flowable} that were emitted in a specified * window of time before the current {@code Flowable} completed. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -16628,7 +16628,7 @@ public final Flowable takeLast(long time, @NonNull TimeUnit unit) { * Returns a {@code Flowable} that emits the items from the current {@code Flowable} that were emitted in a specified * window of time before the current {@code Flowable} completed. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -16663,7 +16663,7 @@ public final Flowable takeLast(long time, @NonNull TimeUnit unit, boolean del * window of time before the current {@code Flowable} completed, where the timing information is provided by a specified * {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -16697,7 +16697,7 @@ public final Flowable takeLast(long time, @NonNull TimeUnit unit, @NonNull Sc * window of time before the current {@code Flowable} completed, where the timing information is provided by a specified * {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -16734,7 +16734,7 @@ public final Flowable takeLast(long time, @NonNull TimeUnit unit, @NonNull Sc * window of time before the current {@code Flowable} completed, where the timing information is provided by a specified * {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an @@ -16773,7 +16773,7 @@ public final Flowable takeLast(long time, @NonNull TimeUnit unit, @NonNull Sc * Returns a {@code Flowable} that emits items emitted by the current {@code Flowable}, checks the specified predicate * for each item, and then completes when the condition is satisfied. *

- * + * *

* The difference between this operator and {@link #takeWhile(Predicate)} is that here, the condition is * evaluated after the item is emitted. @@ -16807,7 +16807,7 @@ public final Flowable takeUntil(@NonNull Predicate stopPredicate) * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} until a second {@link Publisher} * emits an item. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -16838,7 +16838,7 @@ public final Flowable takeUntil(@NonNull Publisher other) { * Returns a {@code Flowable} that emits items emitted by the current {@code Flowable} so long as each item satisfied a * specified condition, and then completes as soon as this condition is not satisfied. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -16870,7 +16870,7 @@ public final Flowable takeWhile(@NonNull Predicate predicate) { * This differs from {@link #throttleLast} in that this only tracks the passage of time whereas * {@link #throttleLast} ticks at scheduled intervals. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -16902,7 +16902,7 @@ public final Flowable throttleFirst(long windowDuration, @NonNull TimeUnit un * This differs from {@link #throttleLast} in that this only tracks the passage of time whereas * {@link #throttleLast} ticks at scheduled intervals. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -16939,7 +16939,7 @@ public final Flowable throttleFirst(long skipDuration, @NonNull TimeUnit unit * This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas * {@link #throttleFirst} does not tick, it just tracks the passage of time. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -16973,7 +16973,7 @@ public final Flowable throttleLast(long intervalDuration, @NonNull TimeUnit u * This differs from {@link #throttleFirst(long, TimeUnit, Scheduler)} in that this ticks along at a scheduled interval whereas * {@code throttleFirst} does not tick, it just tracks the passage of time. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -17171,7 +17171,7 @@ public final Flowable throttleLatest(long timeout, @NonNull TimeUnit unit, @N * Note: If items keep being emitted by the current {@code Flowable} faster than the timeout then no items * will be emitted by the resulting {@code Flowable}. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -17207,7 +17207,7 @@ public final Flowable throttleWithTimeout(long timeout, @NonNull TimeUnit uni * Note: If items keep being emitted by the current {@code Flowable} faster than the timeout then no items * will be emitted by the resulting {@code Flowable}. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -17242,7 +17242,7 @@ public final Flowable throttleWithTimeout(long timeout, @NonNull TimeUnit uni * Returns a {@code Flowable} that emits records of the time interval between consecutive items emitted by the * current {@code Flowable}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -17267,7 +17267,7 @@ public final Flowable> timeInterval() { * Returns a {@code Flowable} that emits records of the time interval between consecutive items emitted by the * current {@code Flowable}, where this interval is computed on a specified {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -17295,7 +17295,7 @@ public final Flowable> timeInterval(@NonNull Scheduler scheduler) { * Returns a {@code Flowable} that emits records of the time interval between consecutive items emitted by the * current {@code Flowable}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -17322,7 +17322,7 @@ public final Flowable> timeInterval(@NonNull TimeUnit unit) { * Returns a {@code Flowable} that emits records of the time interval between consecutive items emitted by the * current {@code Flowable}, where this interval is computed on a specified {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -17355,7 +17355,7 @@ public final Flowable> timeInterval(@NonNull TimeUnit unit, @NonNull Sc * time after the emission of the previous item, where that period of time is measured by a {@link Publisher} that * is a function of the previous item. *

- * + * *

* Note: The arrival of the first source item is never timed out. *

@@ -17391,7 +17391,7 @@ public final Flowable timeout(@NonNull Function - * + * *

* Note: The arrival of the first source item is never timed out. *

@@ -17429,7 +17429,7 @@ public final Flowable timeout(@NonNull Function - * + * *
*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -17459,7 +17459,7 @@ public final Flowable timeout(long timeout, @NonNull TimeUnit unit) { * item. If the next item isn't emitted within the specified timeout duration starting from its predecessor, * the current {@code Flowable} is disposed and the resulting {@code Flowable} begins instead to mirror a fallback {@link Publisher}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} @@ -17495,7 +17495,7 @@ public final Flowable timeout(long timeout, @NonNull TimeUnit unit, @NonNull * starting from its predecessor, the current {@code Flowable} is disposed and the resulting {@code Flowable} begins * instead to mirror a fallback {@link Publisher}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} @@ -17533,7 +17533,7 @@ public final Flowable timeout(long timeout, @NonNull TimeUnit unit, @NonNull * specified timeout duration starting from its predecessor, the resulting {@code Flowable} terminates and * notifies {@link Subscriber}s of a {@link TimeoutException}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -17565,7 +17565,7 @@ public final Flowable timeout(long timeout, @NonNull TimeUnit unit, @NonNull * {@link TimeoutException} if either the first item emitted by the current {@code Flowable} or any subsequent item * doesn't arrive within time windows defined by other {@link Publisher}s. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. Both this and the returned {@code Publisher}s @@ -17605,7 +17605,7 @@ public final Flowable timeout(@NonNull Publisher firstTimeoutIndica * the first item emitted by the current {@code Flowable} or any subsequent item doesn't arrive within time windows * defined by other {@code Publisher}s. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} @@ -17665,7 +17665,7 @@ private Flowable timeout0( * Returns a {@code Flowable} that emits each item emitted by the current {@code Flowable}, wrapped in a * {@link Timed} object. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -17690,7 +17690,7 @@ public final Flowable> timestamp() { * Returns a {@code Flowable} that emits each item emitted by the current {@code Flowable}, wrapped in a * {@link Timed} object whose timestamps are provided by a specified {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -17718,7 +17718,7 @@ public final Flowable> timestamp(@NonNull Scheduler scheduler) { * Returns a {@code Flowable} that emits each item emitted by the current {@code Flowable}, wrapped in a * {@link Timed} object. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -17745,7 +17745,7 @@ public final Flowable> timestamp(@NonNull TimeUnit unit) { * Returns a {@code Flowable} that emits each item emitted by the current {@code Flowable}, wrapped in a * {@link Timed} object whose timestamps are provided by a specified {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure @@ -17800,7 +17800,7 @@ public final R to(@NonNull FlowableConverter converter) { * Returns a {@link Single} that emits a single item, a list composed of all the items emitted by the * finite upstream source {@link Publisher}. *

- * + * *

* Normally, a {@code Publisher} that returns multiple items will do so by invoking its {@link Subscriber}'s * {@link Subscriber#onNext onNext} method for each such item. You can change this behavior by having the @@ -17834,7 +17834,7 @@ public final Single> toList() { * Returns a {@link Single} that emits a single item, a list composed of all the items emitted by the * finite source {@link Publisher}. *

- * + * *

* Normally, a {@code Publisher} that returns multiple items will do so by invoking its {@link Subscriber}'s * {@link Subscriber#onNext onNext} method for each such item. You can change this behavior by having the @@ -17872,7 +17872,7 @@ public final Single> toList(int capacityHint) { * Returns a {@link Single} that emits a single item, a list composed of all the items emitted by the * finite source {@link Publisher}. *

- * + * *

* Normally, a {@code Publisher} that returns multiple items will do so by invoking its {@link Subscriber}'s * {@link Subscriber#onNext onNext} method for each such item. You can change this behavior by having the @@ -17911,7 +17911,7 @@ public final > Single toList(@NonNull Supplie * Returns a {@link Single} that emits a single {@link HashMap} containing all items emitted by the finite source {@link Publisher}, * mapped by the keys returned by a specified {@code keySelector} function. *

- * + * *

* If more than one source item maps to the same key, the {@code HashMap} will contain the latest of those items. *

@@ -17946,7 +17946,7 @@ public final Single> toMap(@NonNull Function - * + * *

* If more than one source item maps to the same key, the {@code HashMap} will contain a single entry that * corresponds to the latest of those items. @@ -17986,7 +17986,7 @@ public final Single> toMap(@NonNull Function - * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -18027,7 +18027,7 @@ public final Single> toMap(@NonNull Function - * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -18062,7 +18062,7 @@ public final Single>> toMultimap(@NonNull Function - * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -18100,7 +18100,7 @@ public final Single>> toMultimap(@NonNull Function - * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -18148,7 +18148,7 @@ public final Single>> toMultimap( * contains an {@link ArrayList} of values, extracted by a specified {@code valueSelector} function from items * emitted by the finite source {@link Publisher} and keyed by the {@code keySelector} function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -18214,7 +18214,7 @@ public final Observable toObservable() { * all other items emitted by this {@code Flowable}, no items will be emitted and the * sequence is terminated with a {@link ClassCastException}. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -18241,7 +18241,7 @@ public final Single> toSortedList() { * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the finite source {@link Publisher}, in a * sorted order based on a specified comparison function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -18274,7 +18274,7 @@ public final Single> toSortedList(@NonNull Comparator compara * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the finite source {@link Publisher}, in a * sorted order based on a specified comparison function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -18316,7 +18316,7 @@ public final Single> toSortedList(@NonNull Comparator compara * all other items emitted by this {@code Flowable}, no items will be emitted and the * sequence is terminated with a {@link ClassCastException}. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -18380,7 +18380,7 @@ public final Flowable unsubscribeOn(@NonNull Scheduler scheduler) { * {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the current window and * propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window will only contain one element. The behavior is @@ -18413,7 +18413,7 @@ public final Flowable> window(long count) { * the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the current window * and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18450,7 +18450,7 @@ public final Flowable> window(long count, long skip) { * the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the current window * and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18493,7 +18493,7 @@ public final Flowable> window(long count, long skip, int bufferSize) * {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18535,7 +18535,7 @@ public final Flowable> window(long timespan, long timeskip, @NonNull * {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18579,7 +18579,7 @@ public final Flowable> window(long timespan, long timeskip, @NonNull * {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18630,7 +18630,7 @@ public final Flowable> window(long timespan, long timeskip, @NonNull * {@code timespan} argument. When the current {@code Flowable} completes or encounters an error, the resulting * {@code Flowable} emits the current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18670,7 +18670,7 @@ public final Flowable> window(long timespan, @NonNull TimeUnit unit) * reached first). When the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} * emits the current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18714,7 +18714,7 @@ public final Flowable> window(long timespan, @NonNull TimeUnit unit, * reached first). When the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} * emits the current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18759,7 +18759,7 @@ public final Flowable> window(long timespan, @NonNull TimeUnit unit, * {@code timespan} argument. When the current {@code Flowable} completes or encounters an error, the resulting * {@code Flowable} emits the current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18803,7 +18803,7 @@ public final Flowable> window(long timespan, @NonNull TimeUnit unit, * first). When the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18849,7 +18849,7 @@ public final Flowable> window(long timespan, @NonNull TimeUnit unit, * first). When the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18897,7 +18897,7 @@ public final Flowable> window(long timespan, @NonNull TimeUnit unit, * first). When the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18950,7 +18950,7 @@ public final Flowable> window( * where the boundary of each window is determined by the items emitted from a specified boundary-governing * {@link Publisher}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -18985,7 +18985,7 @@ public final Flowable> window(@NonNull Publisher boundaryIndi * where the boundary of each window is determined by the items emitted from a specified boundary-governing * {@link Publisher}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -19026,7 +19026,7 @@ public final Flowable> window(@NonNull Publisher boundaryIndi * the {@code windowOpenings} {@link Publisher} emits an item and when the {@code Publisher} returned by * {@code closingSelector} emits an item. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -19069,7 +19069,7 @@ public final Flowable> window( * the {@code windowOpenings} {@link Publisher} emits an item and when the {@code Publisher} returned by * {@code closingSelector} emits an item. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -19116,7 +19116,7 @@ public final Flowable> window( * Merges the specified {@link Publisher} into the current {@code Flowable} sequence by using the {@code resultSelector} * function only when the current {@code Flowable} (this instance) emits an item. *

- * + * * *

*
Backpressure:
@@ -19357,7 +19357,7 @@ public final Flowable withLatestFrom(@NonNull Iterable<@NonNull ? extends * Returns a {@code Flowable} that emits items that are the result of applying a specified function to pairs of * values, one each from the current {@code Flowable} and a specified {@link Iterable} sequence. *

- * + * *

* Note that the {@code other} {@code Iterable} is evaluated as items are observed from the current {@code Flowable}; it is * not pre-consumed. This allows you to zip infinite streams on either side. @@ -19409,7 +19409,7 @@ public final Flowable withLatestFrom(@NonNull Iterable<@NonNull ? extends * use {@link #doOnCancel(Action)} as well or use {@code using()} to do cleanup in case of completion * or cancellation. *

- * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. @@ -19457,7 +19457,7 @@ public final Flowable zipWith(@NonNull Publisher<@NonNull ? extends U> * use {@link #doOnCancel(Action)} as well or use {@code using()} to do cleanup in case of completion * or cancellation. *

- * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. @@ -19508,7 +19508,7 @@ public final Flowable zipWith(@NonNull Publisher<@NonNull ? extends U> * use {@link #doOnCancel(Action)} as well or use {@code using()} to do cleanup in case of completion * or cancellation. *

- * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. diff --git a/src/main/java/io/reactivex/rxjava3/core/Maybe.java b/src/main/java/io/reactivex/rxjava3/core/Maybe.java index dba5a15219..f5ff68247a 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Maybe.java +++ b/src/main/java/io/reactivex/rxjava3/core/Maybe.java @@ -898,7 +898,7 @@ public static Maybe defer(@NonNull Supplier - * + * *
*
Scheduler:
*
{@code empty} does not operate by default on a particular {@link Scheduler}.
@@ -1239,7 +1239,7 @@ public static Maybe fromRunnable(@NonNull Runnable run) { * subscribes to the returned {@code Maybe}. In other terms, this source operator evaluates the given * {@code Supplier} "lazily". *

- * + * *

* Note that the {@code null} handling of this operator differs from the similar source operators in the other * {@link io.reactivex.rxjava3.core base reactive classes}. Those operators signal a {@link NullPointerException} if the value returned by their @@ -1946,7 +1946,7 @@ public static Flowable mergeDelayError( /** * Returns a {@code Maybe} that never sends any items or notifications to a {@link MaybeObserver}. *

- * + * *

* This {@code Maybe} is useful primarily for testing purposes. *

@@ -3628,7 +3628,7 @@ public final Maybe doOnDispose(@NonNull Action onDispose) { /** * Invokes an {@link Action} just before the current {@code Maybe} calls {@code onComplete}. *

- * + * *

*
Scheduler:
*
{@code doOnComplete} does not operate by default on a particular {@link Scheduler}.
@@ -3658,7 +3658,7 @@ public final Maybe doOnComplete(@NonNull Action onComplete) { * Calls the shared {@link Consumer} with the error sent via {@code onError} for each * {@link MaybeObserver} that subscribes to the current {@code Maybe}. *

- * + * *

*
Scheduler:
*
{@code doOnError} does not operate by default on a particular {@link Scheduler}.
@@ -3793,7 +3793,7 @@ public final Maybe doOnTerminate(@NonNull Action onTerminate) { * Calls the shared {@link Consumer} with the success value sent via {@code onSuccess} for each * {@link MaybeObserver} that subscribes to the current {@code Maybe}. *

- * + * *

*
Scheduler:
*
{@code doOnSuccess} does not operate by default on a particular {@link Scheduler}.
@@ -4345,7 +4345,7 @@ public final Maybe map(@NonNull Function mapper) * Maps the signal types of this {@code Maybe} into a {@link Notification} of the same kind * and emits it as a {@link Single}'s {@code onSuccess} value to downstream. *

- * + * *

*
Scheduler:
*
{@code materialize} does not operate by default on a particular {@link Scheduler}.
diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index 9972112585..2e0aff91ba 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -52,7 +52,7 @@ *

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

- * + * *

* The design of this class was derived from the * Reactive-Streams design and specification @@ -105,7 +105,7 @@ public abstract class Observable<@NonNull T> implements ObservableSource { * Mirrors the one {@link ObservableSource} in an {@link Iterable} of several {@code ObservableSource}s that first either emits an item or sends * a termination notification. *

- * + * *

*
Scheduler:
*
{@code amb} does not operate by default on a particular {@link Scheduler}.
@@ -131,7 +131,7 @@ public static Observable amb(@NonNull Iterable<@NonNull ? extends Observa * Mirrors the one {@link ObservableSource} in an array of several {@code ObservableSource}s that first either emits an item or sends * a termination notification. *

- * + * *

*
Scheduler:
*
{@code ambArray} does not operate by default on a particular {@link Scheduler}.
@@ -191,7 +191,7 @@ public static int bufferSize() { * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -235,7 +235,7 @@ public static Observable combineLatest( * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -288,7 +288,7 @@ public static Observable combineLatest( * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatestArray} does not operate by default on a particular {@link Scheduler}.
@@ -332,7 +332,7 @@ public static Observable combineLatestArray( * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatestArray} does not operate by default on a particular {@link Scheduler}.
@@ -380,7 +380,7 @@ public static Observable combineLatestArray( * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -421,7 +421,7 @@ public static Observable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -467,7 +467,7 @@ public static Observable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -518,7 +518,7 @@ public static Observable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -574,7 +574,7 @@ public static Observable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -634,7 +634,7 @@ public static Observable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -700,7 +700,7 @@ public static Observable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -770,7 +770,7 @@ public static Observable combineLatest( * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -841,7 +841,7 @@ public static Observable combineLates * the {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

- * + * *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a @@ -898,7 +898,7 @@ public static Observable combineLatestArrayDelayError( * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatestArrayDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -953,7 +953,7 @@ public static Observable combineLatestArrayDelayError(@NonNull Observa * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatestDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -997,7 +997,7 @@ public static Observable combineLatestDelayError(@NonNull Iterable<@No * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatestDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -1036,7 +1036,7 @@ public static Observable combineLatestDelayError(@NonNull Iterable<@No * Concatenates elements of each {@link ObservableSource} provided via an {@link Iterable} sequence into a single sequence * of elements without interleaving them. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -1059,7 +1059,7 @@ public static Observable concat(@NonNull Iterable<@NonNull ? extends Obse * Returns an {@code Observable} that emits the items emitted by each of the {@link ObservableSource}s emitted by the * {@code ObservableSource}, one after the other, without interleaving them. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -1083,7 +1083,7 @@ public static Observable concat(@NonNull ObservableSource - * + * *
*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -1113,7 +1113,7 @@ public static Observable concat(@NonNull ObservableSource - * + * *
*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -1141,7 +1141,7 @@ public static Observable concat(@NonNull ObservableSource so * Returns an {@code Observable} that emits the items emitted by three {@link ObservableSource}s, one after the other, without * interleaving them. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -1174,7 +1174,7 @@ public static Observable concat( * Returns an {@code Observable} that emits the items emitted by four {@link ObservableSource}s, one after the other, without * interleaving them. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -1211,7 +1211,7 @@ public static Observable concat( *

* Note: named this way because of overload conflict with {@code concat(ObservableSource)} *

- * + * *

*
Scheduler:
*
{@code concatArray} does not operate by default on a particular {@link Scheduler}.
@@ -1241,7 +1241,7 @@ public static Observable concatArray(@NonNull ObservableSource - * + * *
*
Scheduler:
*
{@code concatArrayDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -1389,7 +1389,7 @@ public static Observable concatArrayEagerDelayError(int maxConcurrency, i * by subscribing to each {@code ObservableSource}, one after the other, one at a time and delays any errors till * the all inner {@code ObservableSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -1413,7 +1413,7 @@ public static Observable concatDelayError(@NonNull Iterable<@NonNull ? ex * by subscribing to each inner {@code ObservableSource}, one after the other, one at a time and delays any errors till the * all inner and the outer {@code ObservableSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -1435,7 +1435,7 @@ public static Observable concatDelayError(@NonNull ObservableSource - * + * *
*
Scheduler:
*
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -1722,7 +1722,7 @@ public static Observable concatEagerDelayError(@NonNull ObservableSource< * disposes the flow (making {@link ObservableEmitter#isDisposed} return {@code true}), * other observers subscribed to the same returned {@code Observable} are not affected. *

- * + * *

* You should call the {@code ObservableEmitter}'s {@code onNext}, {@code onError} and {@code onComplete} methods in a serialized fashion. The * rest of its methods are thread-safe. @@ -1752,7 +1752,7 @@ public static Observable create(@NonNull ObservableOnSubscribe source) * that subscribes. That is, for each subscriber, the actual {@code ObservableSource} that subscriber observes is * determined by the factory function. *

- * + * *

* The {@code defer} operator allows you to defer or delay emitting items from an {@code ObservableSource} until such time as an * {@code Observer} subscribes to the {@code ObservableSource}. This allows an {@code Observer} to easily obtain updates or a @@ -1783,7 +1783,7 @@ public static Observable defer(@NonNull Supplier - * + * *

*
Scheduler:
*
{@code empty} does not operate by default on a particular {@link Scheduler}.
@@ -1887,7 +1887,7 @@ public static Observable fromAction(@NonNull Action action) { /** * Converts an array into an {@link ObservableSource} that emits the items in the array. *

- * + * *

*
Scheduler:
*
{@code fromArray} does not operate by default on a particular {@link Scheduler}.
@@ -1920,7 +1920,7 @@ public static Observable fromArray(@NonNull T... items) { * Returns an {@code Observable} that, when an observer subscribes to it, invokes a function you specify and then * emits the value returned from that function. *

- * + * *

* This allows you to defer the execution of the function you specify until an observer subscribes to the * {@code Observable}. That is to say, it makes the function "lazy." @@ -2059,7 +2059,7 @@ public static Observable fromFuture(@NonNull Future future, /** * Converts an {@link Iterable} sequence into an {@code Observable} that emits the items in the sequence. *

- * + * *

*
Scheduler:
*
{@code fromIterable} does not operate by default on a particular {@link Scheduler}.
@@ -2201,7 +2201,7 @@ public static Observable fromSingle(@NonNull SingleSource source) { * Returns an {@code Observable} that, when an observer subscribes to it, invokes a supplier function you specify and then * emits the value returned from that function. *

- * + * *

* This allows you to defer the execution of the function you specify until an observer subscribes to the * {@code Observable}. That is to say, it makes the function "lazy." @@ -2238,7 +2238,7 @@ public static Observable fromSupplier(@NonNull Supplier supp /** * Returns a cold, synchronous and stateless generator of values. *

- * + * *

* Note that the {@link Emitter#onNext}, {@link Emitter#onError} and * {@link Emitter#onComplete} methods provided to the function via the {@link Emitter} instance should be called synchronously, @@ -2269,7 +2269,7 @@ public static Observable generate(@NonNull Consumer> generator /** * Returns a cold, synchronous and stateful generator of values. *

- * + * *

* Note that the {@link Emitter#onNext}, {@link Emitter#onError} and * {@link Emitter#onComplete} methods provided to the function via the {@link Emitter} instance should be called synchronously, @@ -2301,7 +2301,7 @@ public static Observable generate(@NonNull Supplier initialState, @ /** * Returns a cold, synchronous and stateful generator of values. *

- * + * *

* Note that the {@link Emitter#onNext}, {@link Emitter#onError} and * {@link Emitter#onComplete} methods provided to the function via the {@link Emitter} instance should be called synchronously, @@ -2338,7 +2338,7 @@ public static Observable generate( /** * Returns a cold, synchronous and stateful generator of values. *

- * + * *

* Note that the {@link Emitter#onNext}, {@link Emitter#onError} and * {@link Emitter#onComplete} methods provided to the function via the {@link Emitter} instance should be called synchronously, @@ -2370,7 +2370,7 @@ public static Observable generate(@NonNull Supplier initialState, @ /** * Returns a cold, synchronous and stateful generator of values. *

- * + * *

* Note that the {@link Emitter#onNext}, {@link Emitter#onError} and * {@link Emitter#onComplete} methods provided to the function via the {@link Emitter} instance should be called synchronously, @@ -2409,7 +2409,7 @@ public static Observable generate(@NonNull Supplier initialState, @ * Returns an {@code Observable} that emits a {@code 0L} after the {@code initialDelay} and ever increasing numbers * after each {@code period} of time thereafter. *

- * + * *

*
Scheduler:
*
{@code interval} operates by default on the {@code computation} {@link Scheduler}.
@@ -2437,7 +2437,7 @@ public static Observable interval(long initialDelay, long period, @NonNull * Returns an {@code Observable} that emits a {@code 0L} after the {@code initialDelay} and ever increasing numbers * after each {@code period} of time thereafter, on a specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -2469,7 +2469,7 @@ public static Observable interval(long initialDelay, long period, @NonNull /** * Returns an {@code Observable} that emits a sequential number every specified interval of time. *

- * + * *

*
Scheduler:
*
{@code interval} operates by default on the {@code computation} {@link Scheduler}.
@@ -2494,7 +2494,7 @@ public static Observable interval(long period, @NonNull TimeUnit unit) { * Returns an {@code Observable} that emits a sequential number every specified interval of time, on a * specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -2522,7 +2522,7 @@ public static Observable interval(long period, @NonNull TimeUnit unit, @No *

* The sequence completes immediately after the last value (start + count - 1) has been reached. *

- * + * *

*
Scheduler:
*
{@code intervalRange} by default operates on the {@link Schedulers#computation() computation} {@link Scheduler}.
@@ -2551,7 +2551,7 @@ public static Observable intervalRange(long start, long count, long initia *

* The sequence completes immediately after the last value (start + count - 1) has been reached. *

- * *

+ * *
*
Scheduler:
*
you provide the {@link Scheduler}.
*
@@ -2629,7 +2629,7 @@ public static Observable just(@NonNull T item) { /** * Converts two items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2658,7 +2658,7 @@ public static Observable just(@NonNull T item1, @NonNull T item2) { /** * Converts three items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2690,7 +2690,7 @@ public static Observable just(@NonNull T item1, @NonNull T item2, @NonNul /** * Converts four items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2725,7 +2725,7 @@ public static Observable just(@NonNull T item1, @NonNull T item2, @NonNul /** * Converts five items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2764,7 +2764,7 @@ public static Observable just(@NonNull T item1, @NonNull T item2, @NonNul /** * Converts six items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2806,7 +2806,7 @@ public static Observable just(@NonNull T item1, @NonNull T item2, @NonNul /** * Converts seven items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2852,7 +2852,7 @@ public static Observable just(@NonNull T item1, @NonNull T item2, @NonNul /** * Converts eight items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2901,7 +2901,7 @@ public static Observable just(@NonNull T item1, @NonNull T item2, @NonNul /** * Converts nine items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2953,7 +2953,7 @@ public static Observable just(@NonNull T item1, @NonNull T item2, @NonNul /** * Converts ten items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -3010,7 +3010,7 @@ public static Observable just(@NonNull T item1, @NonNull T item2, @NonNul * Flattens an {@link Iterable} of {@link ObservableSource}s into one {@code Observable}, without any transformation, while limiting the * number of concurrent subscriptions to these {@code ObservableSource}s. *

- * + * *

* You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. @@ -3058,7 +3058,7 @@ public static Observable merge(@NonNull Iterable<@NonNull ? extends Obser * Flattens an array of {@link ObservableSource}s into one {@code Observable}, without any transformation, while limiting the * number of concurrent subscriptions to these {@code ObservableSource}s. *

- * + * *

* You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. @@ -3106,7 +3106,7 @@ public static Observable mergeArray(int maxConcurrency, int bufferSize, @ /** * Flattens an {@link Iterable} of {@link ObservableSource}s into one {@code Observable}, without any transformation. *

- * + * *

* You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. @@ -3148,7 +3148,7 @@ public static Observable merge(@NonNull Iterable<@NonNull ? extends Obser * Flattens an {@link Iterable} of {@link ObservableSource}s into one {@code Observable}, without any transformation, while limiting the * number of concurrent subscriptions to these {@code ObservableSource}s. *

- * + * *

* You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. @@ -3194,7 +3194,7 @@ public static Observable merge(@NonNull Iterable<@NonNull ? extends Obser * Flattens an {@link ObservableSource} that emits {@code ObservableSource}s into a single {@code Observable} that emits the items emitted by * those {@code ObservableSource}s, without any transformation. *

- * + * *

* You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. @@ -3238,7 +3238,7 @@ public static Observable merge(@NonNull ObservableSource - * + * *

* You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. @@ -3286,7 +3286,7 @@ public static Observable merge(@NonNull ObservableSource - * + * *

* You can combine items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. @@ -3331,7 +3331,7 @@ public static Observable merge(@NonNull ObservableSource sou /** * Flattens three {@link ObservableSource}s into a single {@code Observable}, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. @@ -3381,7 +3381,7 @@ public static Observable merge( /** * Flattens four {@link ObservableSource}s into a single {@code Observable}, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. @@ -3434,7 +3434,7 @@ public static Observable merge( /** * Flattens an array of {@link ObservableSource}s into one {@code Observable}, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. @@ -3482,7 +3482,7 @@ public static Observable mergeArray(@NonNull ObservableSource - * + * *

* Even if multiple merged {@code ObservableSource}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Observer}s once. @@ -3515,7 +3515,7 @@ public static Observable mergeDelayError(@NonNull Iterable<@NonNull ? ext * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code ObservableSource}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Observer}s once. @@ -3553,7 +3553,7 @@ public static Observable mergeDelayError(@NonNull Iterable<@NonNull ? ext * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code ObservableSource}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Observer}s once. @@ -3592,7 +3592,7 @@ public static Observable mergeArrayDelayError(int maxConcurrency, int buf * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code ObservableSource}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Observer}s once. @@ -3628,7 +3628,7 @@ public static Observable mergeDelayError(@NonNull Iterable<@NonNull ? ext * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code ObservableSource}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Observer}s once. @@ -3663,7 +3663,7 @@ public static Observable mergeDelayError(@NonNull ObservableSource - * + * *

* Even if multiple merged {@code ObservableSource}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Observer}s once. @@ -3702,7 +3702,7 @@ public static Observable mergeDelayError(@NonNull ObservableSource - * + * *

* Even if both merged {@code ObservableSource}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Observer}s once. @@ -3741,7 +3741,7 @@ public static Observable mergeDelayError( * from propagating that error notification until all of the merged {@code ObservableSource}s have finished emitting * items. *

- * + * *

* Even if multiple merged {@code ObservableSource}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Observer}s once. @@ -3784,7 +3784,7 @@ public static Observable mergeDelayError( * will refrain from propagating that error notification until all of the merged {@code ObservableSource}s have finished * emitting items. *

- * + * *

* Even if multiple merged {@code ObservableSource}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Observer}s once. @@ -3829,7 +3829,7 @@ public static Observable mergeDelayError( * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

* Even if multiple merged {@code ObservableSource}s send {@code onError} notifications, {@code mergeDelayError} will only * invoke the {@code onError} method of its {@code Observer}s once. @@ -3857,7 +3857,7 @@ public static Observable mergeArrayDelayError(@NonNull ObservableSource - * + * *

* The returned {@code Observable} is useful primarily for testing purposes. *

@@ -3881,7 +3881,7 @@ public static Observable never() { /** * Returns an {@code Observable} that emits a sequence of {@link Integer}s within a specified range. *

- * + * *

*
Scheduler:
*
{@code range} does not operate by default on a particular {@link Scheduler}.
@@ -3966,7 +3966,7 @@ public static Observable rangeLong(long start, long count) { * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link ObservableSource} sequences are the * same by comparing the items emitted by each {@code ObservableSource} pairwise. *

- * + * *

*
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
@@ -3994,7 +3994,7 @@ public static Single sequenceEqual(@NonNull ObservableSource - * + * *
*
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
@@ -4026,7 +4026,7 @@ public static Single sequenceEqual( * same by comparing the items emitted by each {@code ObservableSource} pairwise based on the results of a specified * equality function. *

- * + * *

*
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
@@ -4064,7 +4064,7 @@ public static Single sequenceEqual( * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link ObservableSource} sequences are the * same by comparing the items emitted by each {@code ObservableSource} pairwise. *

- * + * *

*
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
@@ -4095,7 +4095,7 @@ public static Single sequenceEqual(@NonNull ObservableSource - * + * *

* {@code switchOnNext} subscribes to an {@code ObservableSource} that emits {@code ObservableSource}s. Each time it observes one of * these emitted {@code ObservableSource}s, the {@code ObservableSource} returned by {@code switchOnNext} begins emitting the items @@ -4133,7 +4133,7 @@ public static Observable switchOnNext(@NonNull ObservableSource - * + * *

* {@code switchOnNext} subscribes to an {@code ObservableSource} that emits {@code ObservableSource}s. Each time it observes one of * these emitted {@code ObservableSource}s, the {@code ObservableSource} returned by {@code switchOnNext} begins emitting the items @@ -4165,7 +4165,7 @@ public static Observable switchOnNext(@NonNull ObservableSource - * + * *

* {@code switchOnNext} subscribes to an {@code ObservableSource} that emits {@code ObservableSource}s. Each time it observes one of * these emitted {@code ObservableSource}s, the {@code ObservableSource} returned by {@code switchOnNext} begins emitting the items @@ -4199,7 +4199,7 @@ public static Observable switchOnNextDelayError(@NonNull ObservableSource * Converts an {@link ObservableSource} that emits {@code ObservableSource}s into an {@code Observable} that emits the items emitted by the * most recently emitted of those {@code ObservableSource}s and delays any exception until all {@code ObservableSource}s terminate. *

- * + * *

* {@code switchOnNext} subscribes to an {@code ObservableSource} that emits {@code ObservableSource}s. Each time it observes one of * these emitted {@code ObservableSource}s, the {@code ObservableSource} returned by {@code switchOnNext} begins emitting the items @@ -4238,7 +4238,7 @@ public static Observable switchOnNextDelayError(@NonNull ObservableSource /** * Returns an {@code Observable} that emits {@code 0L} after a specified delay, and then completes. *

- * + * *

*
Scheduler:
*
{@code timer} operates by default on the {@code computation} {@link Scheduler}.
@@ -4263,7 +4263,7 @@ public static Observable timer(long delay, @NonNull TimeUnit unit) { * Returns an {@code Observable} that emits {@code 0L} after a specified delay, on a specified {@link Scheduler}, and then * completes. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -4322,7 +4322,7 @@ public static Observable unsafeCreate(@NonNull ObservableSource onSubs * that resource and calls the provided {@code resourceDisposer} function if this inner source terminates or the * downstream disposes the flow. *

- * + * *

*
Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
@@ -4355,7 +4355,7 @@ public static Observable using( * that resource and calls the provided {@code disposer} function if this inner source terminates or the * downstream disposes the flow; doing it before these end-states have been reached if {@code eager == true}, after otherwise. *

- * + * *

*
Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
@@ -4445,7 +4445,7 @@ public static Observable wrap(@NonNull ObservableSource source) { * {@code Function} passed to the method would trigger a {@link ClassCastException}. * *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4500,7 +4500,7 @@ public static Observable zip(@NonNull Iterable<@NonNull ? extends Obse * {@code Function} passed to the method would trigger a {@link ClassCastException}. * *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4539,7 +4539,7 @@ public static Observable zip(@NonNull Iterable<@NonNull ? extends Obse * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of * two items emitted, in sequence, by two other {@link ObservableSource}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the resulting {@code Observable} * will be the result of the function applied to the first item emitted by {@code o1} and the first item @@ -4596,7 +4596,7 @@ public static Observable zip( * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of * two items emitted, in sequence, by two other {@link ObservableSource}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the resulting {@code Observable} * will be the result of the function applied to the first item emitted by {@code o1} and the first item @@ -4654,7 +4654,7 @@ public static Observable zip( * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of * two items emitted, in sequence, by two other {@link ObservableSource}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the resulting {@code Observable} * will be the result of the function applied to the first item emitted by {@code o1} and the first item @@ -4714,7 +4714,7 @@ public static Observable zip( * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of * three items emitted, in sequence, by three other {@link ObservableSource}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the resulting {@code Observable} * will be the result of the function applied to the first item emitted by {@code o1}, the first item @@ -4777,7 +4777,7 @@ public static Observable zip( * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of * four items emitted, in sequence, by four other {@link ObservableSource}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the resulting {@code Observable} * will be the result of the function applied to the first item emitted by {@code o1}, the first item @@ -4845,7 +4845,7 @@ public static Observable zip( * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of * five items emitted, in sequence, by five other {@link ObservableSource}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the resulting {@code Observable} * will be the result of the function applied to the first item emitted by {@code o1}, the first item @@ -4917,7 +4917,7 @@ public static Observable zip( * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of * six items emitted, in sequence, by six other {@link ObservableSource}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the resulting {@code Observable} * will be the result of the function applied to the first item emitted by each source {@code ObservableSource}, the @@ -4992,7 +4992,7 @@ public static Observable zip( * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of * seven items emitted, in sequence, by seven other {@link ObservableSource}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the resulting {@code Observable} * will be the result of the function applied to the first item emitted by each source {@code ObservableSource}, the @@ -5073,7 +5073,7 @@ public static Observable zip( * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of * eight items emitted, in sequence, by eight other {@link ObservableSource}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the resulting {@code Observable} * will be the result of the function applied to the first item emitted by each source {@code ObservableSource}, the @@ -5158,7 +5158,7 @@ public static Observable zip( * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of * nine items emitted, in sequence, by nine other {@link ObservableSource}s. *

- * + * *

* {@code zip} applies this function in strict sequence, so the first item emitted by the resulting {@code Observable} * will be the result of the function applied to the first item emitted by each source {@code ObservableSource}, the @@ -5273,7 +5273,7 @@ public static Observable zip( * {@code Function} passed to the method would trigger a {@link ClassCastException}. * *

- * + * *

*
Scheduler:
*
{@code zipArray} does not operate by default on a particular {@link Scheduler}.
@@ -5344,7 +5344,7 @@ public final Single all(@NonNull Predicate predicate) { * Mirrors the current {@code Observable} or the other {@link ObservableSource} provided of which the first either emits an item or sends a termination * notification. *

- * + * *

*
Scheduler:
*
{@code ambWith} does not operate by default on a particular {@link Scheduler}.
@@ -5370,7 +5370,7 @@ public final Observable ambWith(@NonNull ObservableSource other) * specified condition, otherwise {@code false}. Note: this always emits {@code false} if the * current {@code Observable} is empty. *

- * + * *

* In Rx.Net this is the {@code any} {@link Observer} but we renamed it in RxJava to better match Java naming * idioms. @@ -5462,7 +5462,7 @@ public final T blockingFirst(@NonNull T defaultItem) { * {@link Consumer} with each upstream item on the current thread until the * upstream terminates. *

- * + * *

* Note: the method will only return if the upstream terminates or the current * thread is interrupted. @@ -5499,7 +5499,7 @@ public final void blockingForEach(@NonNull Consumer onNext) { * {@link Consumer} with each upstream item on the current thread until the * upstream terminates. *

- * + * *

* Note: the method will only return if the upstream terminates or the current * thread is interrupted. @@ -5549,7 +5549,7 @@ public final void blockingForEach(@NonNull Consumer onNext, int capac * subscribes to the current {@code Observable} and blocks * until the current {@code Observable} emits items or terminates. *

- * + * *

*
Scheduler:
*
{@code blockingIterable} does not operate by default on a particular {@link Scheduler}.
@@ -5570,7 +5570,7 @@ public final Iterable blockingIterable() { * subscribes to the current {@code Observable} and blocks * until the current {@code Observable} emits items or terminates. *

- * + * *

*
Scheduler:
*
{@code blockingIterable} does not operate by default on a particular {@link Scheduler}.
@@ -5593,7 +5593,7 @@ public final Iterable blockingIterable(int capacityHint) { * Returns the last item emitted by the current {@code Observable}, or throws * {@link NoSuchElementException} if the current {@code Observable} emits no items. *

- * + * *

*
Scheduler:
*
{@code blockingLast} does not operate by default on a particular {@link Scheduler}.
@@ -5625,7 +5625,7 @@ public final T blockingLast() { * Returns the last item emitted by the current {@code Observable}, or a default value if it emits no * items. *

- * + * *

*
Scheduler:
*
{@code blockingLast} does not operate by default on a particular {@link Scheduler}.
@@ -5683,7 +5683,7 @@ public final Iterable blockingLatest() { * Returns an {@link Iterable} that always returns the item most recently emitted by the current * {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code blockingMostRecent} does not operate by default on a particular {@link Scheduler}.
@@ -5708,7 +5708,7 @@ public final Iterable blockingMostRecent(@NonNull T initialItem) { * Returns an {@link Iterable} that blocks until the current {@code Observable} emits another item, then * returns that item. *

- * + * *

*
Scheduler:
*
{@code blockingNext} does not operate by default on a particular {@link Scheduler}.
@@ -5728,7 +5728,7 @@ public final Iterable blockingNext() { * If the current {@code Observable} completes after emitting a single item, return that item, otherwise * throw a {@link NoSuchElementException}. *

- * + * *

*
Scheduler:
*
{@code blockingSingle} does not operate by default on a particular {@link Scheduler}.
@@ -5757,7 +5757,7 @@ public final T blockingSingle() { * more than one item, throw an {@link IllegalArgumentException}; if it emits no items, return a default * value. *

- * + * *

*
Scheduler:
*
{@code blockingSingle} does not operate by default on a particular {@link Scheduler}.
@@ -5935,7 +5935,7 @@ public final void blockingSubscribe(@NonNull Observer observer) { * from the current {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -5961,7 +5961,7 @@ public final void blockingSubscribe(@NonNull Observer observer) { * from the current {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -5991,7 +5991,7 @@ public final void blockingSubscribe(@NonNull Observer observer) { * from the current {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -6029,7 +6029,7 @@ public final > Observable buffer(int count, i * from the current {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -6061,7 +6061,7 @@ public final > Observable buffer(int count, i * from the current {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} operates by default on the {@code computation} {@link Scheduler}.
@@ -6093,7 +6093,7 @@ public final > Observable buffer(int count, i * {@code Observable} issues an {@code onError} notification the event is passed on immediately without first emitting the * buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -6127,7 +6127,7 @@ public final > Observable buffer(int count, i * {@code Observable} issues an {@code onError} notification the event is passed on immediately without first emitting the * buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -6167,7 +6167,7 @@ public final > Observable buffer(int count, i * {@code Observable} issues an {@code onError} notification the event is passed on immediately without first emitting the * buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} operates by default on the {@code computation} {@link Scheduler}.
@@ -6198,7 +6198,7 @@ public final > Observable buffer(int count, i * {@code onError} notification the event is passed on immediately without first emitting the buffer it is in the process of * assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} operates by default on the {@code computation} {@link Scheduler}.
@@ -6232,7 +6232,7 @@ public final > Observable buffer(int count, i * that if the current {@code Observable} issues an {@code onError} notification the event is passed on immediately without * first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -6268,7 +6268,7 @@ public final > Observable buffer(int count, i * that if the current {@code Observable} issues an {@code onError} notification the event is passed on immediately without * first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -6317,7 +6317,7 @@ public final > Observable buffer(int count, i * {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -6348,7 +6348,7 @@ public final > Observable buffer(int count, i * current {@code Observable}, {@code openingIndicator} or {@code closingIndicator} issues an {@code onError} notification the * event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -6381,7 +6381,7 @@ public final > Observable buffer(int count, i * current {@code Observable}, {@code openingIndicator} or {@code closingIndicator} issues an {@code onError} notification the * event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -6419,7 +6419,7 @@ public final > Observable buffer(int count, i * Returns an {@code Observable} that emits non-overlapping buffered items from the current {@code Observable} each time the * specified boundary {@link ObservableSource} emits an item. *

- * + * *

* Completion of either the source or the boundary {@code ObservableSource} causes the returned {@code ObservableSource} to emit the * latest buffer and complete. If either the current {@code Observable} or the boundary {@code ObservableSource} issues an @@ -6450,7 +6450,7 @@ public final > Observable buffer(int count, i * Returns an {@code Observable} that emits non-overlapping buffered items from the current {@code Observable} each time the * specified boundary {@link ObservableSource} emits an item. *

- * + * *

* Completion of either the source or the boundary {@code ObservableSource} causes the returned {@code ObservableSource} to emit the * latest buffer and complete. If either the current {@code Observable} or the boundary {@code ObservableSource} issues an @@ -6485,7 +6485,7 @@ public final > Observable buffer(int count, i * Returns an {@code Observable} that emits non-overlapping buffered items from the current {@code Observable} each time the * specified boundary {@link ObservableSource} emits an item. *

- * + * *

* Completion of either the source or the boundary {@code ObservableSource} causes the returned {@code ObservableSource} to emit the * latest buffer and complete. If either the current {@code Observable} or the boundary {@code ObservableSource} issues an @@ -6522,7 +6522,7 @@ public final > Observable buffer(int count, i * Returns an {@code Observable} that subscribes to the current {@code Observable} lazily, caches all of its events * and replays them, in the same order as received, to all the downstream observers. *

- * + * *

* This is useful when you want an {@code Observable} to cache responses and you can't control the * subscribe/dispose behavior of all the {@link Observer}s. @@ -6578,7 +6578,7 @@ public final Observable cache() { * Returns an {@code Observable} that subscribes to the current {@code Observable} lazily, caches all of its events * and replays them, in the same order as received, to all the downstream observers. *

- * + * *

* This is useful when you want an {@code Observable} to cache responses and you can't control the * subscribe/dispose behavior of all the {@link Observer}s. @@ -6640,7 +6640,7 @@ public final Observable cacheWithInitialCapacity(int initialCapacity) { * Returns an {@code Observable} that emits the items emitted by the current {@code Observable}, converted to the specified * type. *

- * + * *

*
Scheduler:
*
{@code cast} does not operate by default on a particular {@link Scheduler}.
@@ -6666,7 +6666,7 @@ public final Observable cast(@NonNull Class clazz) { * Collects items emitted by the finite source {@code Observable} into a single mutable data structure and returns * a {@link Single} that emits this structure. *

- * + * *

* This is a simplified version of {@code reduce} that does not need to return the state on each pass. *

@@ -6701,7 +6701,7 @@ public final Single collect(@NonNull Supplier initialItemSup * Collects items emitted by the finite source {@code Observable} into a single mutable data structure and returns * a {@link Single} that emits this structure. *

- * + * *

* This is a simplified version of {@code reduce} that does not need to return the state on each pass. *

@@ -6764,7 +6764,7 @@ public final Observable compose(@NonNull ObservableTransformer - * + * *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, * on the upstream thread signaling the new item to be mapped or on the thread where the inner source terminates. To ensure @@ -6795,7 +6795,7 @@ public final Observable concatMap(@NonNull Function - * + * *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, * on the upstream thread signaling the new item to be mapped or on the thread where the inner source terminates. To ensure @@ -6839,7 +6839,7 @@ public final Observable concatMap(@NonNull Function - * + * *

* The difference between {@link #concatMap(Function, int)} and this operator is that this operator guarantees the {@code mapper} * function is executed on the specified scheduler. @@ -7174,7 +7174,7 @@ public final Completable concatMapCompletable(@NonNull Function - * + * *

*
Scheduler:
*
{@code concatMapCompletableDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -7200,7 +7200,7 @@ public final Completable concatMapCompletableDelayError(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapCompletableDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -7232,7 +7232,7 @@ public final Completable concatMapCompletableDelayError(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapCompletableDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -7297,7 +7297,7 @@ public final Observable concatMapIterable(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapMaybe} does not operate by default on a particular {@link Scheduler}.
@@ -7325,7 +7325,7 @@ public final Observable concatMapMaybe(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapMaybe} does not operate by default on a particular {@link Scheduler}.
@@ -7358,7 +7358,7 @@ public final Observable concatMapMaybe(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapMaybeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -7386,7 +7386,7 @@ public final Observable concatMapMaybeDelayError(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapMaybeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -7420,7 +7420,7 @@ public final Observable concatMapMaybeDelayError(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapMaybeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -7458,7 +7458,7 @@ public final Observable concatMapMaybeDelayError(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapSingle} does not operate by default on a particular {@link Scheduler}.
@@ -7486,7 +7486,7 @@ public final Observable concatMapSingle(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapSingle} does not operate by default on a particular {@link Scheduler}.
@@ -7519,7 +7519,7 @@ public final Observable concatMapSingle(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapSingleDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -7547,7 +7547,7 @@ public final Observable concatMapSingleDelayError(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapSingleDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -7581,7 +7581,7 @@ public final Observable concatMapSingleDelayError(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatMapSingleDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -7618,7 +7618,7 @@ public final Observable concatMapSingleDelayError(@NonNull Function - * + * *
*
Scheduler:
*
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
@@ -7642,7 +7642,7 @@ public final Observable concatWith(@NonNull ObservableSource oth * Returns an {@code Observable} that emits the items from the current {@code Observable} followed by the success item or error event * of the {@code other} {@link SingleSource}. *

- * + * *

*
Scheduler:
*
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
@@ -7665,7 +7665,7 @@ public final Observable concatWith(@NonNull SingleSource other) * Returns an {@code Observable} that emits the items from the current {@code Observable} followed by the success item or terminal events * of the other {@link MaybeSource}. *

- * + * *

*
Scheduler:
*
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
@@ -7688,7 +7688,7 @@ public final Observable concatWith(@NonNull MaybeSource other) { * Returns an {@code Observable} that emits items from the current {@code Observable} and when it completes normally, the * other {@link CompletableSource} is subscribed to and the returned {@code Observable} emits its terminal events. *

- * + * *

*
Scheduler:
*
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
@@ -7711,7 +7711,7 @@ public final Observable concatWith(@NonNull CompletableSource other) { * Returns a {@link Single} that emits a {@link Boolean} that indicates whether the current {@code Observable} emitted a * specified item. *

- * + * *

*
Scheduler:
*
{@code contains} does not operate by default on a particular {@link Scheduler}.
@@ -7735,7 +7735,7 @@ public final Single contains(@NonNull Object item) { * Returns a {@link Single} that counts the total number of items emitted by the current {@code Observable} and emits * this count as a 64-bit {@link Long}. *

- * + * *

*
Scheduler:
*
{@code count} does not operate by default on a particular {@link Scheduler}.
@@ -7756,7 +7756,7 @@ public final Single count() { * current {@code Observable} that are followed by another item within a computed debounce duration * denoted by an item emission or completion from a generated inner {@link ObservableSource} for that original item. *

- * + * *

* The delivery of the item happens on the thread of the first {@code onNext} or {@code onComplete} * signal of the generated {@code ObservableSource} sequence, @@ -7794,7 +7794,7 @@ public final Observable debounce(@NonNull FunctionNote: If items keep being emitted by the current {@code Observable} faster than the timeout then no items * will be emitted by the resulting {@code Observable}. *

- * + * *

* Delivery of the item after the grace period happens on the {@code computation} {@link Scheduler}'s * {@code Worker} which if takes too long, a newer item may arrive from the upstream, causing the @@ -7833,7 +7833,7 @@ public final Observable debounce(long timeout, @NonNull TimeUnit unit) { * Note: If items keep being emitted by the current {@code Observable} faster than the timeout then no items * will be emitted by the resulting {@code Observable}. *

- * + * *

* Delivery of the item after the grace period happens on the given {@code Scheduler}'s * {@code Worker} which if takes too long, a newer item may arrive from the upstream, causing the @@ -7872,7 +7872,7 @@ public final Observable debounce(long timeout, @NonNull TimeUnit unit, @NonNu * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} or a specified default item * if the current {@code Observable} is empty. *

- * + * *

*
Scheduler:
*
{@code defaultIfEmpty} does not operate by default on a particular {@link Scheduler}.
@@ -7896,7 +7896,7 @@ public final Observable defaultIfEmpty(@NonNull T defaultItem) { * Returns an {@code Observable} that delays the emissions of the current {@code Observable} via * a per-item derived {@link ObservableSource}'s item emission or termination, on a per source item basis. *

- * + * *

* Note: the resulting {@code Observable} will immediately propagate any {@code onError} notification * from the current {@code Observable}. @@ -7927,7 +7927,7 @@ public final Observable delay(@NonNull Function - * + * *

*
Scheduler:
*
This version of {@code delay} operates by default on the {@code computation} {@link Scheduler}.
@@ -7954,7 +7954,7 @@ public final Observable delay(long time, @NonNull TimeUnit unit) { * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} shifted forward in time by a * specified delay. If {@code delayError} is {@code true}, error notifications will also be delayed. *

- * + * *

*
Scheduler:
*
This version of {@code delay} operates by default on the {@code computation} {@link Scheduler}.
@@ -7983,7 +7983,7 @@ public final Observable delay(long time, @NonNull TimeUnit unit, boolean dela * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} shifted forward in time by a * specified delay. An error notification from the current {@code Observable} is not delayed. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -8010,7 +8010,7 @@ public final Observable delay(long time, @NonNull TimeUnit unit, @NonNull Sch * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} shifted forward in time by a * specified delay. If {@code delayError} is {@code true}, error notifications will also be delayed. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -8043,7 +8043,7 @@ public final Observable delay(long time, @NonNull TimeUnit unit, @NonNull Sch * Returns an {@code Observable} that delays the subscription to and emissions from the current {@code Observable} via * {@link ObservableSource}s for the subscription itself and on a per-item basis. *

- * + * *

* Note: the resulting {@code Observable} will immediately propagate any {@code onError} notification * from the current {@code Observable}. @@ -8079,7 +8079,7 @@ public final Observable delay(@NonNull ObservableSource subscriptio * Returns an {@code Observable} that delays the subscription to the current {@code Observable} * until the other {@link ObservableSource} emits an element or completes normally. *

- * + * *

*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
@@ -8103,7 +8103,7 @@ public final Observable delaySubscription(@NonNull ObservableSource su /** * Returns an {@code Observable} that delays the subscription to the current {@code Observable} by a given amount of time. *

- * + * *

*
Scheduler:
*
This version of {@code delaySubscription} operates by default on the {@code computation} {@link Scheduler}.
@@ -8128,7 +8128,7 @@ public final Observable delaySubscription(long time, @NonNull TimeUnit unit) * Returns an {@code Observable} that delays the subscription to the current {@code Observable} by a given amount of time, * both waiting and subscribing on a given {@link Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -8156,7 +8156,7 @@ public final Observable delaySubscription(long time, @NonNull TimeUnit unit, * {@link Notification} objects extracted from the source items via a selector function * into their respective {@link Observer} signal types. *

- * + * *

* The intended use of the {@code selector} function is to perform a * type-safe identity mapping (see example) on a source that is already of type @@ -8210,7 +8210,7 @@ public final Observable dematerialize(@NonNull Function - * + * *

* It is recommended the elements' class {@code T} in the flow overrides the default {@code Object.equals()} * and {@link Object#hashCode()} to provide meaningful comparison between items as the default Java @@ -8248,7 +8248,7 @@ public final Observable distinct() { * to a key selector function and based on {@link Object#equals(Object)} comparison of the objects * returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} * and {@link Object#hashCode()} to provide meaningful comparison between the key objects as the default @@ -8290,7 +8290,7 @@ public final Observable distinct(@NonNull Function keySelec * to a key selector function and based on {@link Object#equals(Object)} comparison of the objects * returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} * and {@link Object#hashCode()} to provide meaningful comparison between the key objects as @@ -8324,7 +8324,7 @@ public final Observable distinct(@NonNull Function keySelec * Returns an {@code Observable} that emits all items emitted by the current {@code Observable} that are distinct from their * immediate predecessors based on {@link Object#equals(Object)} comparison. *

- * + * *

* It is recommended the elements' class {@code T} in the flow overrides the default {@code Object.equals()} to provide * meaningful comparison between items as the default Java implementation only considers reference equivalence. @@ -8362,7 +8362,7 @@ public final Observable distinctUntilChanged() { * immediate predecessors, according to a key selector function and based on {@link Object#equals(Object)} comparison * of those objects returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} to provide * meaningful comparison between the key objects as the default Java implementation only considers reference equivalence. @@ -8405,7 +8405,7 @@ public final Observable distinctUntilChanged(@NonNull Function - * + * *

* Note that the operator always retains the latest item from upstream regardless of the comparison result * and uses it in the next comparison with the next upstream item. @@ -8442,7 +8442,7 @@ public final Observable distinctUntilChanged(@NonNull BiPredicate - * + * *

*
Scheduler:
*
{@code doAfterNext} does not operate by default on a particular {@link Scheduler}.
@@ -8467,7 +8467,7 @@ public final Observable doAfterNext(@NonNull Consumer onAfterNext) * Registers an {@link Action} to be called when the current {@code Observable} invokes either * {@link Observer#onComplete onComplete} or {@link Observer#onError onError}. *

- * + * *

*
Scheduler:
*
{@code doAfterTerminate} does not operate by default on a particular {@link Scheduler}.
@@ -8526,7 +8526,7 @@ public final Observable doFinally(@NonNull Action onFinally) { * If the action throws a runtime exception, that exception is rethrown by the {@code dispose()} call, * sometimes as a {@link CompositeException} if there were multiple exceptions along the way. *

- * + * *

*
Scheduler:
*
{@code doOnDispose} does not operate by default on a particular {@link Scheduler}.
@@ -8548,7 +8548,7 @@ public final Observable doOnDispose(@NonNull Action onDispose) { /** * Returns an {@code Observable} that invokes an {@link Action} when the current {@code Observable} calls {@code onComplete}. *

- * + * *

*
Scheduler:
*
{@code doOnComplete} does not operate by default on a particular {@link Scheduler}.
@@ -8571,7 +8571,7 @@ public final Observable doOnComplete(@NonNull Action onComplete) { * Calls the appropriate {@code onXXX} consumer (shared between all {@link Observer}s) whenever a signal with the same type * passes through, before forwarding them to the downstream. *

- * + * *

*
Scheduler:
*
{@code doOnEach} does not operate by default on a particular {@link Scheduler}.
@@ -8596,7 +8596,7 @@ private Observable doOnEach(@NonNull Consumer onNext, @NonNull Con * Returns an {@code Observable} that invokes a {@link Consumer} with the appropriate {@link Notification} * object when the current {@code Observable} signals an item or terminates. *

- * + * *

*
Scheduler:
*
{@code doOnEach} does not operate by default on a particular {@link Scheduler}.
@@ -8630,7 +8630,7 @@ public final Observable doOnEach(@NonNull Consumer> o * {@code onNext} or the {@code onComplete} method of the supplied observer throws, the downstream will be * terminated and will receive this thrown exception. *

- * + * *

*
Scheduler:
*
{@code doOnEach} does not operate by default on a particular {@link Scheduler}.
@@ -8662,7 +8662,7 @@ public final Observable doOnEach(@NonNull Observer observer) { * In case the {@code onError} action throws, the downstream will receive a composite exception containing * the original exception and the exception thrown by {@code onError}. *

- * + * *

*
Scheduler:
*
{@code doOnError} does not operate by default on a particular {@link Scheduler}.
@@ -8685,7 +8685,7 @@ public final Observable doOnError(@NonNull Consumer onErro * Calls the appropriate {@code onXXX} method (shared between all {@link Observer}s) for the lifecycle events of * the sequence (subscription, disposal). *

- * + * *

*
Scheduler:
*
{@code doOnLifecycle} does not operate by default on a particular {@link Scheduler}.
@@ -8711,7 +8711,7 @@ public final Observable doOnLifecycle(@NonNull Consumer o /** * Calls the given {@link Consumer} with the value emitted by the current {@code Observable} before forwarding it to the downstream. *

- * + * *

*
Scheduler:
*
{@code doOnNext} does not operate by default on a particular {@link Scheduler}.
@@ -8736,7 +8736,7 @@ public final Observable doOnNext(@NonNull Consumer onNext) { * current {@code Observable} is reference counted, in which case the current {@code Observable} will invoke * the given action for the first subscription. *

- * + * *

*
Scheduler:
*
{@code doOnSubscribe} does not operate by default on a particular {@link Scheduler}.
@@ -8873,7 +8873,7 @@ public final Single elementAtOrError(long index) { /** * Filters items emitted by the current {@code Observable} by only emitting those that satisfy a specified {@link Predicate}. *

- * + * *

*
Scheduler:
*
{@code filter} does not operate by default on a particular {@link Scheduler}.
@@ -8962,7 +8962,7 @@ public final Single firstOrError() { * by the current {@code Observable}, where that function returns an {@link ObservableSource}, and then merging those returned * {@code ObservableSource}s and emitting the results of this merger. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -9098,7 +9098,7 @@ public final Observable flatMap(@NonNull Function - * + * *
*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -9136,7 +9136,7 @@ public final Observable flatMap( * {@code Observable} and then flattens the {@link ObservableSource}s returned from these functions and emits the resulting items, * while limiting the maximum number of concurrent subscriptions to these {@code ObservableSource}s. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -9209,7 +9209,7 @@ public final Observable flatMap(@NonNull Function - * + * *
*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -9240,7 +9240,7 @@ public final Observable flatMap(@NonNull Function - * + * *
*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -9275,7 +9275,7 @@ public final Observable flatMap(@NonNull Function - * + * *
*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -9314,7 +9314,7 @@ public final Observable flatMap(@NonNull Function - * + * *
*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -9357,7 +9357,7 @@ public final Observable flatMap(@NonNull Function - * + * *
*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -9498,7 +9498,7 @@ public final Observable flatMapIterable(@NonNull Function - * + * *
*
Scheduler:
*
{@code flatMapMaybe} does not operate by default on a particular {@link Scheduler}.
@@ -9520,7 +9520,7 @@ public final Observable flatMapMaybe(@NonNull Function - * + * *
*
Scheduler:
*
{@code flatMapMaybe} does not operate by default on a particular {@link Scheduler}.
@@ -9544,7 +9544,7 @@ public final Observable flatMapMaybe(@NonNull Function - * + * *
*
Scheduler:
*
{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}.
@@ -9566,7 +9566,7 @@ public final Observable flatMapSingle(@NonNull Function - * + * *
*
Scheduler:
*
{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}.
@@ -9706,7 +9706,7 @@ public final Disposable forEachWhile(@NonNull Predicate onNext, @NonN * Groups the items emitted by the current {@code Observable} according to a specified criterion, and emits these * grouped items as {@link GroupedObservable}s. *

- * + * *

* Each emitted {@code GroupedObservable} allows only a single {@link Observer} to subscribe to it during its * lifetime and if this {@code Observer} calls {@code dispose()} before the @@ -9748,7 +9748,7 @@ public final Observable> groupBy(@NonNull Function - * + * *

* Each emitted {@code GroupedObservable} allows only a single {@link Observer} to subscribe to it during its * lifetime and if this {@code Observer} calls {@code dispose()} before the @@ -9793,7 +9793,7 @@ public final Observable> groupBy(@NonNull Function - * + * *

* Each emitted {@code GroupedObservable} allows only a single {@link Observer} to subscribe to it during its * lifetime and if this {@code Observer} calls {@code dispose()} before the @@ -9839,7 +9839,7 @@ public final Observable> groupBy(@NonNull Functio * Groups the items emitted by the current {@code Observable} according to a specified criterion, and emits these * grouped items as {@link GroupedObservable}s. *

- * + * *

* Each emitted {@code GroupedObservable} allows only a single {@link Observer} to subscribe to it during its * lifetime and if this {@code Observer} calls {@code dispose()} before the @@ -9888,7 +9888,7 @@ public final Observable> groupBy(@NonNull Functio * Groups the items emitted by the current {@code Observable} according to a specified criterion, and emits these * grouped items as {@link GroupedObservable}s. *

- * + * *

* Each emitted {@code GroupedObservable} allows only a single {@link Observer} to subscribe to it during its * lifetime and if this {@code Observer} calls {@code dispose()} before the @@ -9947,7 +9947,7 @@ public final Observable> groupBy(@NonNull Functio * There are no guarantees in what order the items get combined when multiple * items from one or both source {@code ObservableSource}s overlap. *

- * + * *

*
Scheduler:
*
{@code groupJoin} does not operate by default on a particular {@link Scheduler}.
@@ -10015,7 +10015,7 @@ public final Observable hide() { /** * Ignores all items emitted by the current {@code Observable} and only calls {@code onComplete} or {@code onError}. *

- * + * *

*
Scheduler:
*
{@code ignoreElements} does not operate by default on a particular {@link Scheduler}.
@@ -10037,7 +10037,7 @@ public final Completable ignoreElements() { * In Rx.Net this is negated as the {@code any} {@link Observer} but we renamed this in RxJava to better match Java * naming idioms. *

- * + * *

*
Scheduler:
*
{@code isEmpty} does not operate by default on a particular {@link Scheduler}.
@@ -10059,7 +10059,7 @@ public final Single isEmpty() { * There are no guarantees in what order the items get combined when multiple * items from one or both source {@code ObservableSource}s overlap. *

- * + * *

*
Scheduler:
*
{@code join} does not operate by default on a particular {@link Scheduler}.
@@ -10105,7 +10105,7 @@ public final Observable join( * Returns a {@link Maybe} that emits the last item emitted by the current {@code Observable} or * completes if the current {@code Observable} is empty. *

- * + * *

*
Scheduler:
*
{@code lastElement} does not operate by default on a particular {@link Scheduler}.
@@ -10125,7 +10125,7 @@ public final Maybe lastElement() { * Returns a {@link Single} that emits only the last item emitted by the current {@code Observable}, or a default item * if the current {@code Observable} completes without emitting any items. *

- * + * *

*
Scheduler:
*
{@code last} does not operate by default on a particular {@link Scheduler}.
@@ -10320,7 +10320,7 @@ public final Observable lift(@NonNull ObservableOperator - * + * *
*
Scheduler:
*
{@code map} does not operate by default on a particular {@link Scheduler}.
@@ -10345,7 +10345,7 @@ public final Observable map(@NonNull Function map * Returns an {@code Observable} that represents all of the emissions and notifications from the current * {@code Observable} into emissions marked with their original types within {@link Notification} objects. *

- * + * *

*
Scheduler:
*
{@code materialize} does not operate by default on a particular {@link Scheduler}.
@@ -10365,7 +10365,7 @@ public final Observable> materialize() { /** * Flattens the current {@code Observable} and another {@link ObservableSource} into a single {@code Observable} sequence, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code mergeWith} method. @@ -10391,7 +10391,7 @@ public final Observable mergeWith(@NonNull ObservableSource othe /** * Merges the sequence of items of the current {@code Observable} with the success value of the other {@link SingleSource}. *

- * + * *

* The success value of the other {@code SingleSource} can get interleaved at any point of the current * {@code Observable} sequence. @@ -10417,7 +10417,7 @@ public final Observable mergeWith(@NonNull SingleSource other) { * Merges the sequence of items of the current {@code Observable} with the success value of the other {@link MaybeSource} * or waits both to complete normally if the {@code MaybeSource} is empty. *

- * + * *

* The success value of the other {@code MaybeSource} can get interleaved at any point of the current * {@code Observable} sequence. @@ -10443,7 +10443,7 @@ public final Observable mergeWith(@NonNull MaybeSource other) { * Relays the items of the current {@code Observable} and completes only when the other {@link CompletableSource} completes * as well. *

- * + * *

*
Scheduler:
*
{@code mergeWith} does not operate by default on a particular {@link Scheduler}.
@@ -10469,7 +10469,7 @@ public final Observable mergeWith(@NonNull CompletableSource other) { *

Note that {@code onError} notifications will cut ahead of {@code onNext} notifications on the emission thread if {@code Scheduler} is truly * asynchronous. If strict event ordering is required, consider using the {@link #observeOn(Scheduler, boolean)} overload. *

- * + * *

* This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s worker thread, * which may result in a longer than expected occupation of this thread. In other terms, @@ -10504,7 +10504,7 @@ public final Observable observeOn(@NonNull Scheduler scheduler) { * Returns an {@code Observable} to perform the current {@code Observable}'s emissions and notifications on a specified {@link Scheduler}, * asynchronously with an unbounded buffer with {@link Flowable#bufferSize()} "island size" and optionally delays {@code onError} notifications. *

- * + * *

* This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s worker thread, * which may result in a longer than expected occupation of this thread. In other terms, @@ -10543,7 +10543,7 @@ public final Observable observeOn(@NonNull Scheduler scheduler, boolean delay * Returns an {@code Observable} to perform the current {@code Observable}'s emissions and notifications on a specified {@link Scheduler}, * asynchronously with an unbounded buffer of configurable "island size" and optionally delays {@code onError} notifications. *

- * + * *

* This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s worker thread, * which may result in a longer than expected occupation of this thread. In other terms, @@ -10585,7 +10585,7 @@ public final Observable observeOn(@NonNull Scheduler scheduler, boolean delay /** * Filters the items emitted by the current {@code Observable}, only emitting those of the specified type. *

- * + * *

*
Scheduler:
*
{@code ofType} does not operate by default on a particular {@link Scheduler}.
@@ -10653,7 +10653,7 @@ public final Observable onErrorComplete(@NonNull Predicate * Resumes the flow with an {@link ObservableSource} returned for the failure {@link Throwable} of the current {@code Observable} by a * function instead of signaling the error via {@code onError}. *

- * + * *

* By default, when an {@code ObservableSource} encounters an error that prevents it from emitting the expected item to * its {@link Observer}, the {@code ObservableSource} invokes its {@code Observer}'s {@code onError} method, and then quits @@ -10691,7 +10691,7 @@ public final Observable onErrorResumeNext(@NonNull Function - * + * *

* By default, when an {@code ObservableSource} encounters an error that prevents it from emitting the expected item to * its {@link Observer}, the {@code ObservableSource} invokes its {@code Observer}'s {@code onError} method, and then quits @@ -10729,7 +10729,7 @@ public final Observable onErrorResumeWith(@NonNull ObservableSource - * + * *

* By default, when an {@link ObservableSource} encounters an error that prevents it from emitting the expected item to * its {@link Observer}, the {@code ObservableSource} invokes its {@code Observer}'s {@code onError} method, and then quits @@ -10763,7 +10763,7 @@ public final Observable onErrorReturn(@NonNull Function - * + * *

* By default, when an {@link ObservableSource} encounters an error that prevents it from emitting the expected item to * its {@link Observer}, the {@code ObservableSource} invokes its {@code Observer}'s {@code onError} method, and then quits @@ -10819,7 +10819,7 @@ public final Observable onTerminateDetach() { * {@link ConnectableObservable#connect connect} method is called before it begins emitting items to those * {@link Observer}s that have subscribed to it. *

- * + * *

*
Scheduler:
*
{@code publish} does not operate by default on a particular {@link Scheduler}.
@@ -10869,7 +10869,7 @@ public final Observable publish(@NonNull Function, * {@code Observable} into the same function, and so on until all items have been emitted by the current and finite {@code Observable}, * and emits the final result from the final call to your function as its sole item. *

- * + * *

* This technique, which is called "reduce" here, is sometimes called "aggregate," "fold," "accumulate," * "compress," or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method @@ -10905,7 +10905,7 @@ public final Maybe reduce(@NonNull BiFunction reducer) { * emitted by the current {@code Observable} into the same function, and so on until all items have been emitted by the * current and finite {@code Observable}, emitting the final result from the final call to your function as its sole item. *

- * + * *

* This technique, which is called "reduce" here, is sometimes called "aggregate," "fold," "accumulate," * "compress," or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method @@ -10965,7 +10965,7 @@ public final Maybe reduce(@NonNull BiFunction reducer) { * and so on until all items have been emitted by the current and finite {@code Observable}, emitting the final result * from the final call to your function as its sole item. *

- * + * *

* This technique, which is called "reduce" here, is sometimes called "aggregate," "fold," "accumulate," * "compress," or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method @@ -11084,7 +11084,7 @@ public final Observable repeatUntil(@NonNull BooleanSupplier stop) { * call {@code onComplete} or {@code onError} on the child subscription. Otherwise, the current {@code Observable} * will be resubscribed. *

- * + * *

*
Scheduler:
*
{@code repeatWhen} does not operate by default on a particular {@link Scheduler}.
@@ -11110,7 +11110,7 @@ public final Observable repeatWhen(@NonNull Function - * + * *
*
Scheduler:
*
This version of {@code replay} does not operate by default on a particular {@link Scheduler}.
@@ -11472,7 +11472,7 @@ public final Observable replay(@NonNull Function, ? * an ordinary {@code Observable}, except that it does not begin emitting items when it is subscribed to, but only * when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -11504,7 +11504,7 @@ public final ConnectableObservable replay(int bufferSize) { * an ordinary {@code Observable}, except that it does not begin emitting items when it is subscribed to, but only * when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -11537,7 +11537,7 @@ public final ConnectableObservable replay(int bufferSize, boolean eagerTrunca * {@code Observable} resembles an ordinary {@code Observable}, except that it does not begin emitting items when it is * subscribed to, but only when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -11578,7 +11578,7 @@ public final ConnectableObservable replay(int bufferSize, long time, @NonNull * To ensure no out-of-date or beyond-bufferSize items are referenced, * use the {@link #replay(int, long, TimeUnit, Scheduler, boolean)} overload with {@code eagerTruncate = true}. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -11615,7 +11615,7 @@ public final ConnectableObservable replay(int bufferSize, long time, @NonNull * connectable {@code Observable} resembles an ordinary {@code Observable}, except that it does not begin emitting items * when it is subscribed to, but only when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -11659,7 +11659,7 @@ public final ConnectableObservable replay(int bufferSize, long time, @NonNull * resembles an ordinary {@code Observable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

*
Scheduler:
*
This version of {@code replay} operates by default on the {@code computation} {@link Scheduler}.
@@ -11686,7 +11686,7 @@ public final ConnectableObservable replay(long time, @NonNull TimeUnit unit) * resembles an ordinary {@code Observable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

* Note that the internal buffer may retain strong references to the oldest item. To ensure no out-of-date items * are referenced, use the {@link #replay(long, TimeUnit, Scheduler, boolean)} overload with {@code eagerTruncate = true}. @@ -11721,7 +11721,7 @@ public final ConnectableObservable replay(long time, @NonNull TimeUnit unit, * resembles an ordinary {@code Observable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

* Note that the internal buffer may retain strong references to the oldest item. To ensure no out-of-date items * are referenced, set {@code eagerTruncate = true}. @@ -11756,7 +11756,7 @@ public final ConnectableObservable replay(long time, @NonNull TimeUnit unit, * Returns an {@code Observable} that mirrors the current {@code Observable}, resubscribing to it if it calls {@code onError} * (infinite retry count). *

- * + * *

* If the current {@code Observable} calls {@link Observer#onError}, this method will resubscribe to the current * {@code Observable} rather than propagating the {@code onError} call. @@ -11811,7 +11811,7 @@ public final Observable retry(@NonNull BiPredicate - * + * *

* If the current {@code Observable} calls {@link Observer#onError}, this method will resubscribe to the current * {@code Observable} for a maximum of {@code count} resubscriptions rather than propagating the @@ -11913,7 +11913,7 @@ public final Observable retryUntil(@NonNull BooleanSupplier stop) { * {@code onComplete} or {@code onError} on the child subscription. Otherwise, the current {@code Observable} * will be resubscribed. *

- * + * *

* Example: * @@ -12015,7 +12015,7 @@ public final void safeSubscribe(@NonNull Observer observer) { * Returns an {@code Observable} that emits the most recently emitted item (if any) emitted by the current {@code Observable} * within periodic time intervals. *

- * + * *

*
Scheduler:
*
{@code sample} operates by default on the {@code computation} {@link Scheduler}.
@@ -12073,7 +12073,7 @@ public final Observable sample(long period, @NonNull TimeUnit unit, boolean e * Returns an {@code Observable} that emits the most recently emitted item (if any) emitted by the current {@code Observable} * within periodic time intervals, where the intervals are defined on a particular {@link Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -12201,7 +12201,7 @@ public final Observable sample(@NonNull ObservableSource sampler, bool * {@code Observable} into the same function, and so on until all items have been emitted by the current {@code Observable}, * emitting the result of each of these iterations. *

- * + * *

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

@@ -12231,7 +12231,7 @@ public final Observable scan(@NonNull BiFunction accumulator) { * the current {@code Observable} into the same function, and so on until all items have been emitted by the current * {@code Observable}, emitting the result of each of these iterations. *

- * + * *

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

@@ -12281,7 +12281,7 @@ public final Observable scan(@NonNull R initialValue, @NonNull BiFunction * the current {@code Observable} into the same function, and so on until all items have been emitted by the current * {@code Observable}, emitting the result of each of these iterations. *

- * + * *

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

@@ -12322,7 +12322,7 @@ public final Observable scanWith(@NonNull Supplier seedSupplier, @NonN * {@code onNext} from two different threads concurrently. You can force such an {@code Observable} to be * well-behaved and sequential by applying the {@code serialize} method to it. *

- * + * *

*
Scheduler:
*
{@code serialize} does not operate by default on a particular {@link Scheduler}.
@@ -12345,7 +12345,7 @@ public final Observable serialize() { *

* This is an alias for {@link #publish()}.{@link ConnectableObservable#refCount() refCount()}. *

- * + * *

*
Scheduler:
*
{@code share} does not operate by default on a particular {@link Scheduler}.
@@ -12366,7 +12366,7 @@ public final Observable share() { * emitted by the current {@code Observable}, or signals an {@link IllegalArgumentException} if the current * {@code Observable} emits more than one item. *

- * + * *

*
Scheduler:
*
{@code singleElement} does not operate by default on a particular {@link Scheduler}.
@@ -12387,7 +12387,7 @@ public final Maybe singleElement() { * emits only a single item, or a default item if the current {@code Observable} emits no items. If the current * {@code Observable} emits more than one item, an {@link IllegalArgumentException} is signaled instead. *

- * + * *

*
Scheduler:
*
{@code single} does not operate by default on a particular {@link Scheduler}.
@@ -12433,7 +12433,7 @@ public final Single singleOrError() { * Returns an {@code Observable} that skips the first {@code count} items emitted by the current {@code Observable} and emits * the remainder. *

- * + * *

*
Scheduler:
*
This version of {@code skip} does not operate by default on a particular {@link Scheduler}.
@@ -12462,7 +12462,7 @@ public final Observable skip(long count) { * Returns an {@code Observable} that skips values emitted by the current {@code Observable} before a specified time window * elapses. *

- * + * *

*
Scheduler:
*
{@code skip} does not operate on any particular scheduler but uses the current time @@ -12488,7 +12488,7 @@ public final Observable skip(long time, @NonNull TimeUnit unit) { * Returns an {@code Observable} that skips values emitted by the current {@code Observable} before a specified time window * on a specified {@link Scheduler} elapses. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use for the timed skipping
@@ -12515,7 +12515,7 @@ public final Observable skip(long time, @NonNull TimeUnit unit, @NonNull Sche * Returns an {@code Observable} that drops a specified number of items from the end of the sequence emitted by the * current {@code Observable}. *

- * + * *

* This {@link Observer} accumulates a queue long enough to store the first {@code count} items. As more items are * received, items are taken from the front of the queue and emitted by the returned {@code Observable}. This causes @@ -12549,7 +12549,7 @@ public final Observable skipLast(int count) { * Returns an {@code Observable} that drops items emitted by the current {@code Observable} during a specified time window * before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -12577,7 +12577,7 @@ public final Observable skipLast(long time, @NonNull TimeUnit unit) { * Returns an {@code Observable} that drops items emitted by the current {@code Observable} during a specified time window * before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -12608,7 +12608,7 @@ public final Observable skipLast(long time, @NonNull TimeUnit unit, boolean d * Returns an {@code Observable} that drops items emitted by the current {@code Observable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -12637,7 +12637,7 @@ public final Observable skipLast(long time, @NonNull TimeUnit unit, @NonNull * Returns an {@code Observable} that drops items emitted by the current {@code Observable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -12669,7 +12669,7 @@ public final Observable skipLast(long time, @NonNull TimeUnit unit, @NonNull * Returns an {@code Observable} that drops items emitted by the current {@code Observable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -12709,7 +12709,7 @@ public final Observable skipLast(long time, @NonNull TimeUnit unit, @NonNull * Returns an {@code Observable} that skips items emitted by the current {@code Observable} until a second {@link ObservableSource} emits * an item. *

- * + * *

*
Scheduler:
*
{@code skipUntil} does not operate by default on a particular {@link Scheduler}.
@@ -12735,7 +12735,7 @@ public final Observable skipUntil(@NonNull ObservableSource other) { * Returns an {@code Observable} that skips all items emitted by the current {@code Observable} as long as a specified * condition holds {@code true}, but emits all further source items as soon as the condition becomes {@code false}. *

- * + * *

*
Scheduler:
*
{@code skipWhile} does not operate by default on a particular {@link Scheduler}.
@@ -12812,7 +12812,7 @@ public final Observable sorted(@NonNull Comparator comparator) { * Returns an {@code Observable} that emits the items in a specified {@link Iterable} before it begins to emit items * emitted by the current {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code startWithIterable} does not operate by default on a particular {@link Scheduler}.
@@ -12904,7 +12904,7 @@ public final Observable startWith(@NonNull MaybeSource other) { * Returns an {@code Observable} that emits the items in a specified {@link ObservableSource} before it begins to emit * items emitted by the current {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code startWith} does not operate by default on a particular {@link Scheduler}.
@@ -12954,7 +12954,7 @@ public final Observable startWithItem(@NonNull T item) { * Returns an {@code Observable} that emits the specified items before it begins to emit items emitted by the current * {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code startWithArray} does not operate by default on a particular {@link Scheduler}.
@@ -13156,7 +13156,7 @@ public final void subscribe(@NonNull Observer observer) { /** * Asynchronously subscribes {@link Observer}s to the current {@code Observable} on the specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -13210,7 +13210,7 @@ public final Observable switchIfEmpty(@NonNull ObservableSource * The resulting {@code Observable} completes if both the current {@code Observable} and the last inner {@code ObservableSource}, if any, complete. * If the current {@code Observable} signals an {@code onError}, the inner {@code ObservableSource} is disposed and the error delivered in-sequence. *

- * + * *

*
Scheduler:
*
{@code switchMap} does not operate by default on a particular {@link Scheduler}.
@@ -13240,7 +13240,7 @@ public final Observable switchMap(@NonNull Function - * + * *
*
Scheduler:
*
{@code switchMap} does not operate by default on a particular {@link Scheduler}.
@@ -13502,7 +13502,7 @@ public final Observable switchMapSingleDelayError(@NonNull Function - * + * *
*
Scheduler:
*
{@code switchMapDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -13534,7 +13534,7 @@ public final Observable switchMapDelayError(@NonNull Function - * + * *
*
Scheduler:
*
{@code switchMapDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -13574,7 +13574,7 @@ public final Observable switchMapDelayError(@NonNull Function - * + * *

* This method returns an {@code Observable} that will invoke a subscribing {@link Observer}'s * {@link Observer#onNext onNext} function a maximum of {@code count} times before invoking @@ -13611,7 +13611,7 @@ public final Observable take(long count) { * If time runs out before the {@code Observable} completes normally, the {@code onComplete} event will be * signaled on the default {@code computation} {@link Scheduler}. *

- * + * *

*
Scheduler:
*
This version of {@code take} operates by default on the {@code computation} {@code Scheduler}.
@@ -13639,7 +13639,7 @@ public final Observable take(long time, @NonNull TimeUnit unit) { * If time runs out before the {@code Observable} completes normally, the {@code onComplete} event will be * signaled on the provided {@code Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -13666,7 +13666,7 @@ public final Observable take(long time, @NonNull TimeUnit unit, @NonNull Sche * Returns an {@code Observable} that emits at most the last {@code count} items emitted by the current {@code Observable}. * If the source emits fewer than {@code count} items then all of its items are emitted. *

- * + * *

*
Scheduler:
*
This version of {@code takeLast} does not operate by default on a particular {@link Scheduler}.
@@ -13700,7 +13700,7 @@ public final Observable takeLast(int count) { * Returns an {@code Observable} that emits at most a specified number of items from the current {@code Observable} that were * emitted in a specified window of time before the current {@code Observable} completed. *

- * + * *

*
Scheduler:
*
{@code takeLast} does not operate on any particular scheduler but uses the current time @@ -13730,7 +13730,7 @@ public final Observable takeLast(long count, long time, @NonNull TimeUnit uni * emitted in a specified window of time before the current {@code Observable} completed, where the timing information is * provided by a given {@link Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use for tracking the current time
@@ -13762,7 +13762,7 @@ public final Observable takeLast(long count, long time, @NonNull TimeUnit uni * emitted in a specified window of time before the current {@code Observable} completed, where the timing information is * provided by a given {@link Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use for tracking the current time
@@ -13804,7 +13804,7 @@ public final Observable takeLast(long count, long time, @NonNull TimeUnit uni * Returns an {@code Observable} that emits the items from the current {@code Observable} that were emitted in a specified * window of time before the current {@code Observable} completed. *

- * + * *

*
Scheduler:
*
{@code takeLast} does not operate on any particular scheduler but uses the current time @@ -13830,7 +13830,7 @@ public final Observable takeLast(long time, @NonNull TimeUnit unit) { * Returns an {@code Observable} that emits the items from the current {@code Observable} that were emitted in a specified * window of time before the current {@code Observable} completed. *

- * + * *

*
Scheduler:
*
{@code takeLast} does not operate on any particular scheduler but uses the current time @@ -13861,7 +13861,7 @@ public final Observable takeLast(long time, @NonNull TimeUnit unit, boolean d * window of time before the current {@code Observable} completed, where the timing information is provided by a specified * {@link Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -13889,7 +13889,7 @@ public final Observable takeLast(long time, @NonNull TimeUnit unit, @NonNull * window of time before the current {@code Observable} completed, where the timing information is provided by a specified * {@link Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -13920,7 +13920,7 @@ public final Observable takeLast(long time, @NonNull TimeUnit unit, @NonNull * window of time before the current {@code Observable} completed, where the timing information is provided by a specified * {@link Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -13953,7 +13953,7 @@ public final Observable takeLast(long time, @NonNull TimeUnit unit, @NonNull * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} until a second {@link ObservableSource} * emits an item. *

- * + * *

*
Scheduler:
*
{@code takeUntil} does not operate by default on a particular {@link Scheduler}.
@@ -13980,7 +13980,7 @@ public final Observable takeUntil(@NonNull ObservableSource other) { * Returns an {@code Observable} that emits items emitted by the current {@code Observable}, checks the specified predicate * for each item, and then completes when the condition is satisfied. *

- * + * *

* The difference between this operator and {@link #takeWhile(Predicate)} is that here, the condition is * evaluated after the item is emitted. @@ -14010,7 +14010,7 @@ public final Observable takeUntil(@NonNull Predicate stopPredicate * Returns an {@code Observable} that emits items emitted by the current {@code Observable} so long as each item satisfied a * specified condition, and then completes as soon as this condition is not satisfied. *

- * + * *

*
Scheduler:
*
{@code takeWhile} does not operate by default on a particular {@link Scheduler}.
@@ -14038,7 +14038,7 @@ public final Observable takeWhile(@NonNull Predicate predicate) { * This differs from {@link #throttleLast} in that this only tracks passage of time whereas * {@code throttleLast} ticks at scheduled intervals. *

- * + * *

*
Scheduler:
*
{@code throttleFirst} operates by default on the {@code computation} {@link Scheduler}.
@@ -14066,7 +14066,7 @@ public final Observable throttleFirst(long windowDuration, @NonNull TimeUnit * This differs from {@link #throttleLast} in that this only tracks passage of time whereas * {@code throttleLast} ticks at scheduled intervals. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -14099,7 +14099,7 @@ public final Observable throttleFirst(long skipDuration, @NonNull TimeUnit un * This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas * {@code throttleFirst} does not tick, it just tracks passage of time. *

- * + * *

*
Scheduler:
*
{@code throttleLast} operates by default on the {@code computation} {@link Scheduler}.
@@ -14129,7 +14129,7 @@ public final Observable throttleLast(long intervalDuration, @NonNull TimeUnit * This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas * {@code throttleFirst} does not tick, it just tracks passage of time. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -14299,7 +14299,7 @@ public final Observable throttleLatest(long timeout, @NonNull TimeUnit unit, * Note: If items keep being emitted by the current {@code Observable} faster than the timeout then no items * will be emitted by the resulting {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code throttleWithTimeout} operates by default on the {@code computation} {@link Scheduler}.
@@ -14331,7 +14331,7 @@ public final Observable throttleWithTimeout(long timeout, @NonNull TimeUnit u * Note: If items keep being emitted by the current {@code Observable} faster than the timeout then no items * will be emitted by the resulting {@code Observable}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -14362,7 +14362,7 @@ public final Observable throttleWithTimeout(long timeout, @NonNull TimeUnit u * Returns an {@code Observable} that emits records of the time interval between consecutive items emitted by the * current {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code timeInterval} does not operate on any particular scheduler but uses the current time @@ -14383,7 +14383,7 @@ public final Observable> timeInterval() { * Returns an {@code Observable} that emits records of the time interval between consecutive items emitted by the * current {@code Observable}, where this interval is computed on a specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
The operator does not operate on any particular scheduler but uses the current time @@ -14407,7 +14407,7 @@ public final Observable> timeInterval(@NonNull Scheduler scheduler) { * Returns an {@code Observable} that emits records of the time interval between consecutive items emitted by the * current {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code timeInterval} does not operate on any particular scheduler but uses the current time @@ -14430,7 +14430,7 @@ public final Observable> timeInterval(@NonNull TimeUnit unit) { * Returns an {@code Observable} that emits records of the time interval between consecutive items emitted by the * current {@code Observable}, where this interval is computed on a specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
The operator does not operate on any particular scheduler but uses the current time @@ -14459,7 +14459,7 @@ public final Observable> timeInterval(@NonNull TimeUnit unit, @NonNull * time after the emission of the previous item, where that period of time is measured by an {@link ObservableSource} that * is a function of the previous item. *

- * + * *

* Note: The arrival of the first source item is never timed out. *

@@ -14489,7 +14489,7 @@ public final Observable timeout(@NonNull Function - * + * *

* Note: The arrival of the first source item is never timed out. *

@@ -14522,7 +14522,7 @@ public final Observable timeout(@NonNull Function - * + * *
*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code computation} {@link Scheduler}.
@@ -14549,7 +14549,7 @@ public final Observable timeout(long timeout, @NonNull TimeUnit unit) { * the current {@code Observable} is disposed and the resulting {@code Observable} begins instead * to mirror a fallback {@link ObservableSource}. *

- * + * *

*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code computation} {@link Scheduler}.
@@ -14579,7 +14579,7 @@ public final Observable timeout(long timeout, @NonNull TimeUnit unit, @NonNul * starting from its predecessor, the current {@code Observable} is disposed and returned {@code Observable} * begins instead to mirror a fallback {@link ObservableSource}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -14611,7 +14611,7 @@ public final Observable timeout(long timeout, @NonNull TimeUnit unit, @NonNul * specified timeout duration starting from its predecessor, the resulting {@code Observable} terminates and * notifies observers of a {@link TimeoutException}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -14639,7 +14639,7 @@ public final Observable timeout(long timeout, @NonNull TimeUnit unit, @NonNul * {@link TimeoutException} if either the first item emitted by the current {@code Observable} or any subsequent item * doesn't arrive within time windows defined by indicator {@link ObservableSource}s. *

- * + * *

*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code immediate} {@link Scheduler}.
@@ -14674,7 +14674,7 @@ public final Observable timeout(@NonNull ObservableSource firstTime * the first item emitted by the current {@code Observable} or any subsequent item doesn't arrive within time windows * defined by indicator {@code ObservableSource}s. *

- * + * *

*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code immediate} {@link Scheduler}.
@@ -14732,7 +14732,7 @@ private Observable timeout0( * Returns an {@code Observable} that emits each item emitted by the current {@code Observable}, wrapped in a * {@link Timed} object. *

- * + * *

*
Scheduler:
*
{@code timestamp} does not operate on any particular scheduler but uses the current time @@ -14753,7 +14753,7 @@ public final Observable> timestamp() { * Returns an {@code Observable} that emits each item emitted by the current {@code Observable}, wrapped in a * {@link Timed} object whose timestamps are provided by a specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
This operator does not operate on any particular scheduler but uses the current time @@ -14777,7 +14777,7 @@ public final Observable> timestamp(@NonNull Scheduler scheduler) { * Returns an {@code Observable} that emits each item emitted by the current {@code Observable}, wrapped in a * {@link Timed} object. *

- * + * *

*
Scheduler:
*
{@code timestamp} does not operate on any particular scheduler but uses the current time @@ -14800,7 +14800,7 @@ public final Observable> timestamp(@NonNull TimeUnit unit) { * Returns an {@code Observable} that emits each item emitted by the current {@code Observable}, wrapped in a * {@link Timed} object whose timestamps are provided by a specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
This operator does not operate on any particular scheduler but uses the current time @@ -14849,7 +14849,7 @@ public final R to(@NonNull ObservableConverter converter) { * Returns a {@link Single} that emits a single item, a {@link List} composed of all the items emitted by the * current and finite {@code Observable}. *

- * + * *

* Normally, an {@link ObservableSource} that returns multiple items will do so by invoking its {@link Observer}'s * {@link Observer#onNext onNext} method for each such item. You can change this behavior by having the @@ -14879,7 +14879,7 @@ public final R to(@NonNull ObservableConverter converter) { * Returns a {@link Single} that emits a single item, a {@link List} composed of all the items emitted by the * current and finite {@code Observable}. *

- * + * *

* Normally, an {@link ObservableSource} that returns multiple items will do so by invoking its {@link Observer}'s * {@link Observer#onNext onNext} method for each such item. You can change this behavior by having the @@ -14949,7 +14949,7 @@ public final R to(@NonNull ObservableConverter converter) { * current and finite {@code Observable}, mapped by the keys returned by a specified * {@code keySelector} function. *

- * + * *

* If more than one source item maps to the same key, the {@code HashMap} will contain the latest of those items. *

@@ -14980,7 +14980,7 @@ public final R to(@NonNull ObservableConverter converter) { * Returns a {@link Single} that emits a single {@link HashMap} containing values corresponding to items emitted by the * current and finite {@code Observable}, mapped by the keys and values returned by the given selector functions. *

- * + * *

* If more than one source item maps to the same key, the {@code HashMap} will contain a single entry that * corresponds to the latest of those items. @@ -15018,7 +15018,7 @@ public final Single> toMap( * Returns a {@link Single} that emits a single {@link Map} (subclass), returned by a specified {@code mapFactory} function, that * contains keys and values extracted from the items, via selector functions, emitted by the current and finite {@code Observable}. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code Map} to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -15057,7 +15057,7 @@ public final Single> toMap( * Returns a {@link Single} that emits a single {@link HashMap} that contains an {@link ArrayList} of items emitted by the * current and finite {@code Observable} keyed by a specified {@code keySelector} function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code HashMap} to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -15089,7 +15089,7 @@ public final Single> toMap( * specified {@code valueSelector} function from items emitted by the current and finite {@code Observable}, * keyed by a specified {@code keySelector} function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code HashMap} to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -15123,7 +15123,7 @@ public final Single> toMap( * contains a custom {@link Collection} of values, extracted by a specified {@code valueSelector} function from * items emitted by the current and finite {@code Observable}, and keyed by the {@code keySelector} function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code Map} to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -15167,7 +15167,7 @@ public final Single> toMap( * contains an {@link ArrayList} of values, extracted by a specified {@code valueSelector} function from items * emitted by the current and finite {@code Observable} and keyed by the {@code keySelector} function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code Map} to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -15268,7 +15268,7 @@ public final Flowable toFlowable(@NonNull BackpressureStrategy strategy) { * all other items emitted by the current {@code Observable}, no items will be emitted and the * sequence is terminated with a {@link ClassCastException}. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code List} to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -15293,7 +15293,7 @@ public final Flowable toFlowable(@NonNull BackpressureStrategy strategy) { * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the current and finite {@code Observable}, in a * sorted order based on a specified comparison function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code List} to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -15322,7 +15322,7 @@ public final Flowable toFlowable(@NonNull BackpressureStrategy strategy) { * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the current and finite {@code Observable}, in a * sorted order based on a specified comparison function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code List} to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -15360,7 +15360,7 @@ public final Flowable toFlowable(@NonNull BackpressureStrategy strategy) { * all other items emitted by the current {@code Observable}, no items will be emitted and the * sequence is terminated with a {@link ClassCastException}. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code List} to * be emitted. Sources that are infinite and never complete will never emit anything through this @@ -15415,7 +15415,7 @@ public final Observable unsubscribeOn(@NonNull Scheduler scheduler) { * {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the current window and * propagates the notification from the current {@code Observable}. *

- * + * *

*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
@@ -15440,7 +15440,7 @@ public final Observable> window(long count) { * the current {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the current window * and propagates the notification from the current {@code Observable}. *

- * + * *

*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
@@ -15468,7 +15468,7 @@ public final Observable> window(long count, long skip) { * the current {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the current window * and propagates the notification from the current {@code Observable}. *

- * + * *

*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
@@ -15502,7 +15502,7 @@ public final Observable> window(long count, long skip, int bufferS * {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the * current window and propagates the notification from the current {@code Observable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15538,7 +15538,7 @@ public final Observable> window(long timespan, long timeskip, @Non * {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the * current window and propagates the notification from the current {@code Observable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15576,7 +15576,7 @@ public final Observable> window(long timespan, long timeskip, @Non * {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the * current window and propagates the notification from the current {@code Observable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15620,7 +15620,7 @@ public final Observable> window(long timespan, long timeskip, @Non * {@code timespan} argument. When the current {@code Observable} completes or encounters an error, the resulting * {@code Observable} emits the current window and propagates the notification from the current {@code Observable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15654,7 +15654,7 @@ public final Observable> window(long timespan, @NonNull TimeUnit u * reached first). When the current {@code Observable} completes or encounters an error, the resulting {@code Observable} * emits the current window and propagates the notification from the current {@code Observable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15692,7 +15692,7 @@ public final Observable> window(long timespan, @NonNull TimeUnit u * reached first). When the current {@code Observable} completes or encounters an error, the resulting {@code Observable} * emits the current window and propagates the notification from the current {@code Observable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15731,7 +15731,7 @@ public final Observable> window(long timespan, @NonNull TimeUnit u * {@code timespan} argument. When the current {@code Observable} completes or encounters an error, the resulting * {@code Observable} emits the current window and propagates the notification from the current {@code Observable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15768,7 +15768,7 @@ public final Observable> window(long timespan, @NonNull TimeUnit u * first). When the current {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the * current window and propagates the notification from the current {@code Observable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15808,7 +15808,7 @@ public final Observable> window(long timespan, @NonNull TimeUnit u * first). When the current {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the * current window and propagates the notification from the current {@code Observable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15850,7 +15850,7 @@ public final Observable> window(long timespan, @NonNull TimeUnit u * first). When the current {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the * current window and propagates the notification from the current {@code Observable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15897,7 +15897,7 @@ public final Observable> window( * where the boundary of each window is determined by the items emitted from a specified boundary-governing * {@link ObservableSource}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15928,7 +15928,7 @@ public final Observable> window(@NonNull ObservableSource b * where the boundary of each window is determined by the items emitted from a specified boundary-governing * {@link ObservableSource}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -15965,7 +15965,7 @@ public final Observable> window(@NonNull ObservableSource b * the {@code openingIndicator} {@link ObservableSource} emits an item and when the {@code ObservableSource} returned by * {@code closingIndicator} emits an item. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -16002,7 +16002,7 @@ public final Observable> window( * the {@code openingIndicator} {@link ObservableSource} emits an item and when the {@code ObservableSource} returned by * {@code closingIndicator} emits an item. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent @@ -16043,7 +16043,7 @@ public final Observable> window( * Merges the specified {@link ObservableSource} into the current {@code Observable} sequence by using the {@code resultSelector} * function only when the current {@code Observable} emits an item. *

- * + * * *

*
Scheduler:
@@ -16081,7 +16081,7 @@ public final Observable withLatestFrom(@NonNull ObservableSource - * + * *
*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
@@ -16119,7 +16119,7 @@ public final Observable withLatestFrom( * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. *

- * + * *

*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
@@ -16161,7 +16161,7 @@ public final Observable withLatestFrom( * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. *

- * + * *

*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
@@ -16207,7 +16207,7 @@ public final Observable withLatestFrom( * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. *

- * + * *

*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
@@ -16238,7 +16238,7 @@ public final Observable withLatestFrom(@NonNull ObservableSource[] oth * not when any of the other sources emit, unlike {@code combineLatest}). * If a source doesn't produce any value and just completes, the sequence is completed immediately. *

- * + * *

*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
@@ -16264,7 +16264,7 @@ public final Observable withLatestFrom(@NonNull Iterable<@NonNull ? exten * Returns an {@code Observable} that emits items that are the result of applying a specified function to pairs of * values, one each from the current {@code Observable} and a specified {@link Iterable} sequence. *

- * + * *

* Note that the {@code other} {@code Iterable} is evaluated as items are observed from the current {@code Observable}; it is * not pre-consumed. This allows you to zip infinite streams on either side. @@ -16299,7 +16299,7 @@ public final Observable withLatestFrom(@NonNull Iterable<@NonNull ? exten * Returns an {@code Observable} that emits items that are the result of applying a specified function to pairs of * values, one each from the current {@code Observable} and another specified {@link ObservableSource}. *

- * + * *

* The operator subscribes to its sources in order they are specified and completes eagerly if * one of the sources is shorter than the rest while disposing the other sources. Therefore, it @@ -16343,7 +16343,7 @@ public final Observable zipWith(@NonNull ObservableSource * Returns an {@code Observable} that emits items that are the result of applying a specified function to pairs of * values, one each from the current {@code Observable} and another specified {@link ObservableSource}. *

- * + * *

* The operator subscribes to its sources in order they are specified and completes eagerly if * one of the sources is shorter than the rest while disposing the other sources. Therefore, it @@ -16389,7 +16389,7 @@ public final Observable zipWith(@NonNull ObservableSource * Returns an {@code Observable} that emits items that are the result of applying a specified function to pairs of * values, one each from the current {@code Observable} and another specified {@link ObservableSource}. *

- * + * *

* The operator subscribes to its sources in order they are specified and completes eagerly if * one of the sources is shorter than the rest while disposing the other sources. Therefore, it diff --git a/src/main/java/io/reactivex/rxjava3/core/Single.java b/src/main/java/io/reactivex/rxjava3/core/Single.java index e830589e23..b10706a214 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Single.java +++ b/src/main/java/io/reactivex/rxjava3/core/Single.java @@ -65,7 +65,7 @@ *

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

- * + * *

* See {@link Flowable} or {@code Observable} for the * implementation of the Reactive Pattern for a stream or vector of values. @@ -119,7 +119,7 @@ public abstract class Single<@NonNull T> implements SingleSource { * Runs multiple {@link SingleSource}s and signals the events of the first one that signals (disposing * the rest). *

- * + * *

*
Scheduler:
*
{@code amb} does not operate by default on a particular {@link Scheduler}.
@@ -143,7 +143,7 @@ public static Single amb(@NonNull Iterable<@NonNull ? extends SingleSourc * Runs multiple {@link SingleSource}s and signals the events of the first one that signals (disposing * the rest). *

- * + * *

*
Scheduler:
*
{@code ambArray} does not operate by default on a particular {@link Scheduler}.
@@ -176,7 +176,7 @@ public static Single ambArray(@NonNull SingleSource... sourc * Concatenate the single values, in a non-overlapping fashion, of the {@link SingleSource}s provided by * an {@link Iterable} sequence. *

- * + * *

*
Backpressure:
*
The returned {@link Flowable} honors the backpressure of the downstream consumer.
@@ -201,7 +201,7 @@ public static Flowable concat(@NonNull Iterable<@NonNull ? extends Single * Concatenate the single values, in a non-overlapping fashion, of the {@link SingleSource}s provided by * an {@link ObservableSource} sequence. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -224,7 +224,7 @@ public static Observable concat(@NonNull ObservableSource - * + * *
*
Backpressure:
*
The returned {@link Flowable} honors the backpressure of the downstream consumer @@ -250,7 +250,7 @@ public static Flowable concat(@NonNull Publisher<@NonNull ? extends Singl * Concatenate the single values, in a non-overlapping fashion, of the {@link SingleSource}s provided by * a {@link Publisher} sequence and prefetched by the specified amount. *

- * + * *

*
Backpressure:
*
The returned {@link Flowable} honors the backpressure of the downstream consumer @@ -279,7 +279,7 @@ public static Flowable concat(@NonNull Publisher<@NonNull ? extends Singl /** * Returns a {@link Flowable} that emits the items emitted by two {@link SingleSource}s, one after the other. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -311,7 +311,7 @@ public static Flowable concat( /** * Returns a {@link Flowable} that emits the items emitted by three {@link SingleSource}s, one after the other. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -347,7 +347,7 @@ public static Flowable concat( /** * Returns a {@link Flowable} that emits the items emitted by four {@link SingleSource}s, one after the other. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -387,7 +387,7 @@ public static Flowable concat( * Concatenate the single values, in a non-overlapping fashion, of the {@link SingleSource}s provided in * an array. *

- * + * *

*
Backpressure:
*
The returned {@link Flowable} honors the backpressure of the downstream consumer.
@@ -581,7 +581,7 @@ public static Flowable concatDelayError(@NonNull Publisher<@NonNull ? ext /** * Concatenates an {@link Iterable} sequence of {@link SingleSource}s eagerly into a single stream of values. *

- * + * *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the * source {@code SingleSource}s. The operator buffers the values emitted by these {@code SingleSource}s and then drains them @@ -640,7 +640,7 @@ public static Flowable concatEager(@NonNull Iterable<@NonNull ? extends S /** * Concatenates a {@link Publisher} sequence of {@link SingleSource}s eagerly into a single stream of values. *

- * + * *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the * emitted source {@code SingleSource}s as they are observed. The operator buffers the values emitted by these @@ -829,7 +829,7 @@ public static Flowable concatEagerDelayError(@NonNull Publisher<@NonNull /** * Provides an API (via a cold {@code Single}) that bridges the reactive world with the callback-style world. *

- * + * *

* Example: *


@@ -881,7 +881,7 @@ public static  Flowable concatEagerDelayError(@NonNull Publisher<@NonNull
      * Calls a {@link Supplier} for each individual {@link SingleObserver} to return the actual {@link SingleSource} to
      * be subscribed to.
      * 

- * + * *

*
Scheduler:
*
{@code defer} does not operate by default on a particular {@link Scheduler}.
@@ -903,7 +903,7 @@ public static Single defer(@NonNull Supplier - * + * *
*
Scheduler:
*
{@code error} does not operate by default on a particular {@link Scheduler}.
@@ -926,7 +926,7 @@ public static Single error(@NonNull Supplier supplie * Returns a {@code Single} that invokes a subscriber's {@link SingleObserver#onError onError} method when the * subscriber subscribes to it. *

- * + * *

*
Scheduler:
*
{@code error} does not operate by default on a particular {@link Scheduler}.
@@ -957,7 +957,7 @@ public static Single error(@NonNull Throwable throwable) { * It makes passed function "lazy". * Result of the function invocation will be emitted by the {@link Single}. *

- * + * *

*
Scheduler:
*
{@code fromCallable} does not operate by default on a particular {@link Scheduler}.
@@ -990,7 +990,7 @@ public static Single error(@NonNull Throwable throwable) { /** * Converts a {@link Future} into a {@code Single} and awaits its outcome in a blocking fashion. *

- * + * *

* The operator calls {@link Future#get()}, which is a blocking method, on the subscription thread. * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a @@ -1026,7 +1026,7 @@ public static Single error(@NonNull Throwable throwable) { /** * Converts a {@link Future} into a {@code Single} and awaits its outcome, or timeout, in a blocking fashion. *

- * + * *

* The operator calls {@link Future#get(long, TimeUnit)}, which is a blocking method, on the subscription thread. * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a @@ -1114,7 +1114,7 @@ public static Single fromMaybe(@NonNull MaybeSource maybe, @NonNull T /** * Wraps a specific {@link Publisher} into a {@code Single} and signals its single element or error. *

- * + * *

* If the source {@code Publisher} is empty, a {@link NoSuchElementException} is signaled. If * the source has more than one element, an {@link IndexOutOfBoundsException} is signaled. @@ -1154,7 +1154,7 @@ public static Single fromPublisher(@NonNull Publisher<@NonNull ? extends /** * Wraps a specific {@link ObservableSource} into a {@code Single} and signals its single element or error. *

- * + * *

* If the {@code ObservableSource} is empty, a {@link NoSuchElementException} is signaled. * If the source has more than one element, an {@link IndexOutOfBoundsException} is signaled. @@ -1185,7 +1185,7 @@ public static Single fromObservable(@NonNull ObservableSource - * + * *

*
Scheduler:
*
{@code fromSupplier} does not operate by default on a particular {@link Scheduler}.
@@ -1219,7 +1219,7 @@ public static Single fromObservable(@NonNull ObservableSource - * + * *

* To convert any object into a {@code Single} that emits that object, pass that object into the * {@code just} method. @@ -1248,7 +1248,7 @@ public static Single fromObservable(@NonNull ObservableSource - * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -1287,7 +1287,7 @@ public static Flowable merge(@NonNull Iterable<@NonNull ? extends SingleS * Merges a sequence of {@link SingleSource} instances emitted by a {@link Publisher} into a single {@link Flowable} sequence, * running all {@code SingleSource}s at once. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -1327,7 +1327,7 @@ public static Flowable merge(@NonNull Publisher<@NonNull ? extends Single * Flattens a {@link SingleSource} that emits a {@code SingleSingle} into a single {@code Single} that emits the item * emitted by the nested {@code SingleSource}, without any transformation. *

- * + * *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
@@ -1357,7 +1357,7 @@ public static Single merge(@NonNull SingleSource - * + * *

* You can combine items emitted by multiple {@code SingleSource}s so that they appear as a single {@code Flowable}, by * using the {@code merge} method. @@ -1406,7 +1406,7 @@ public static Flowable merge( /** * Flattens three {@link SingleSource}s into one {@link Flowable} sequence, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code SingleSource}s so that they appear as a single {@code Flowable}, by * the {@code merge} method. @@ -1459,7 +1459,7 @@ public static Flowable merge( /** * Flattens four {@link SingleSource}s into one {@link Flowable} sequence, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code SingleSource}s so that they appear as a single {@code Flowable}, by * the {@code merge} method. @@ -1770,7 +1770,7 @@ public static Flowable mergeDelayError( /** * Returns a singleton instance of a never-signaling {@code Single} (only calls {@code onSubscribe}). *

- * + * *

*
Scheduler:
*
{@code never} does not operate by default on a particular {@link Scheduler}.
@@ -1790,7 +1790,7 @@ public static Single never() { /** * Signals success with 0L value after the given delay when a {@link SingleObserver} subscribes. *

- * + * *

*
Scheduler:
*
{@code timer} operates by default on the {@code computation} {@link Scheduler}.
@@ -1812,7 +1812,7 @@ public static Single timer(long delay, @NonNull TimeUnit unit) { * Signals success with 0L value on the specified {@link Scheduler} after the given * delay when a {@link SingleObserver} subscribes. *

- * + * *

*
Scheduler:
*
you specify the {@code Scheduler} to signal on.
@@ -1838,7 +1838,7 @@ public static Single timer(long delay, @NonNull TimeUnit unit, @NonNull Sc /** * Compares two {@link SingleSource}s and emits {@code true} if they emit the same value (compared via {@link Object#equals(Object)}). *

- * + * *

*
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
@@ -1961,7 +1961,7 @@ public static Single unsafeCreate(@NonNull SingleSource onSubscribe) { * Allows using and disposing a resource while running a {@link SingleSource} instance generated from * that resource (similar to a try-with-resources). *

- * + * *

*
Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
@@ -2570,7 +2570,7 @@ public static Single zipArray(@NonNull Function - * + * *
*
Scheduler:
*
{@code ambWith} does not operate by default on a particular {@link Scheduler}.
@@ -2593,7 +2593,7 @@ public final Single ambWith(@NonNull SingleSource other) { * Hides the identity of the current {@code Single}, including the {@link Disposable} that is sent * to the downstream via {@code onSubscribe()}. *

- * + * *

*
Scheduler:
*
{@code hide} does not operate by default on a particular {@link Scheduler}.
@@ -2611,7 +2611,7 @@ public final Single hide() { /** * Transform a {@code Single} by applying a particular {@link SingleTransformer} function to it. *

- * + * *

* This method operates on the {@code Single} itself whereas {@link #lift} operates on {@link SingleObserver}s. *

@@ -2767,7 +2767,7 @@ public final Maybe concatMapMaybe(@NonNull Function - * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -2794,7 +2794,7 @@ public final Flowable concatWith(@NonNull SingleSource other) { * Delays the emission of the success signal from the current {@code Single} by the specified amount. * An error signal will not be delayed. *

- * + * *

*
Scheduler:
*
{@code delay} operates by default on the {@code computation} {@link Scheduler}.
@@ -2817,7 +2817,7 @@ public final Single delay(long time, @NonNull TimeUnit unit) { /** * Delays the emission of the success or error signal from the current {@code Single} by the specified amount. *

- * + * *

*
Scheduler:
*
{@code delay} operates by default on the {@code computation} {@link Scheduler}.
@@ -2841,7 +2841,7 @@ public final Single delay(long time, @NonNull TimeUnit unit, boolean delayErr * Delays the emission of the success signal from the current {@code Single} by the specified amount. * An error signal will not be delayed. *

- * + * *

*
Scheduler:
*
you specify the {@link Scheduler} where the non-blocking wait and emission happens
@@ -2867,7 +2867,7 @@ public final Single delay(long time, @NonNull TimeUnit unit, @NonNull Schedul /** * Delays the emission of the success or error signal from the current {@code Single} by the specified amount. *

- * + * *

*
Scheduler:
*
you specify the {@link Scheduler} where the non-blocking wait and emission happens
@@ -3089,7 +3089,7 @@ public final Single delaySubscription(long time, @NonNull TimeUnit unit, @Non /** * Calls the specified consumer with the success item after this item has been emitted to the downstream. *

- * + * *

* Note that the {@code doAfterSuccess} action is shared between subscriptions and as such * should be thread-safe. @@ -3114,7 +3114,7 @@ public final Single doAfterSuccess(@NonNull Consumer onAfterSucces /** * Registers an {@link Action} to be called after this {@code Single} invokes either {@code onSuccess} or {@code onError}. *

- * + * *

* Note that the {@code doAfterTerminate} action is shared between subscriptions and as such * should be thread-safe.

@@ -3149,7 +3149,7 @@ public final Single doAfterTerminate(@NonNull Action onAfterTerminate) { *

Note that the {@code onFinally} action is shared between subscriptions and as such * should be thread-safe. *

- * + * *

*
*
Scheduler:
@@ -3201,7 +3201,7 @@ public final Single doOnLifecycle(@NonNull Consumer onSub * Calls the shared consumer with the {@link Disposable} sent through the {@code onSubscribe} for each * {@link SingleObserver} that subscribes to the current {@code Single}. *

- * + * *

*
*
Scheduler:
@@ -3224,7 +3224,7 @@ public final Single doOnSubscribe(@NonNull Consumer onSub * Returns a {@code Single} instance that calls the given {@code onTerminate} callback * just before this {@code Single} completes normally or with an exception. *

- * + * *

* This differs from {@code doAfterTerminate} in that this happens before the {@code onSuccess} or * {@code onError} notification. @@ -3252,7 +3252,7 @@ public final Single doOnTerminate(@NonNull Action onTerminate) { * Calls the shared consumer with the success value sent via {@code onSuccess} for each * {@link SingleObserver} that subscribes to the current {@code Single}. *

- * + * *

*
*
Scheduler:
@@ -3297,7 +3297,7 @@ public final Single doOnEvent(@NonNull BiConsumer<@Nullable ? super T, @Nulla * Calls the shared consumer with the error sent via {@code onError} for each * {@link SingleObserver} that subscribes to the current {@code Single}. *

- * + * *

*
*
Scheduler:
@@ -3320,7 +3320,7 @@ public final Single doOnError(@NonNull Consumer onError) { * Calls the shared {@link Action} if a {@link SingleObserver} subscribed to the current {@code Single} * disposes the common {@link Disposable} it received via {@code onSubscribe}. *

- * + * *

*
*
Scheduler:
@@ -3343,7 +3343,7 @@ public final Single doOnDispose(@NonNull Action onDispose) { * Filters the success item of the {@code Single} via a predicate function and emitting it if the predicate * returns {@code true}, completing otherwise. *

- * + * *

*
Scheduler:
*
{@code filter} does not operate by default on a particular {@link Scheduler}.
@@ -3369,7 +3369,7 @@ public final Maybe filter(@NonNull Predicate predicate) { * Returns a {@code Single} that is based on applying a specified function to the item emitted by the current {@code Single}, * where that function returns a {@link SingleSource}. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -3485,7 +3485,7 @@ public final Maybe flatMapMaybe(@NonNull Function - * + * *
*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer @@ -3574,7 +3574,7 @@ public final Flowable flattenAsFlowable(@NonNull Function - * + * *
*
Scheduler:
*
{@code flatMapObservable} does not operate by default on a particular {@link Scheduler}.
@@ -3901,7 +3901,7 @@ public final Single lift(@NonNull SingleOperator * Returns a {@code Single} that applies a specified function to the item emitted by the current {@code Single} and * emits the result of this function application. *

- * + * *

*
Scheduler:
*
{@code map} does not operate by default on a particular {@link Scheduler}.
@@ -3926,7 +3926,7 @@ public final Single lift(@NonNull SingleOperator * Maps the signal types of this {@code Single} into a {@link Notification} of the same kind * and emits it as a single success value to downstream. *

- * + * *

*
Scheduler:
*
{@code materialize} does not operate by default on a particular {@link Scheduler}.
@@ -3994,7 +3994,7 @@ public final Single contains(@NonNull Object item, @NonNull BiPredicate /** * Flattens this {@code Single} and another {@link SingleSource} into one {@link Flowable}, without any transformation. *

- * + * *

* You can combine items emitted by multiple {@code SingleSource}s so that they appear as one {@code Flowable}, by using * the {@code mergeWith} method. @@ -4048,7 +4048,7 @@ public final Maybe ofType(@NonNull Class clazz) { * Signals the success item or the terminal signals of the current {@code Single} on the specified {@link Scheduler}, * asynchronously. *

- * + * *

*
Scheduler:
*
you specify which {@code Scheduler} this operator will use.
@@ -4074,7 +4074,7 @@ public final Single observeOn(@NonNull Scheduler scheduler) { * Ends the flow with a success item returned by a function for the {@link Throwable} error signaled by the current * {@code Single} instead of signaling the error via {@code onError}. *

- * + * *

* By default, when a {@code Single} encounters an error that prevents it from emitting the expected item to its * subscriber, the {@code Single} invokes its subscriber's {@link SingleObserver#onError} method, and then quits @@ -4108,7 +4108,7 @@ public final Single onErrorReturn(@NonNull Function i /** * Signals the specified value as success in case the current {@code Single} signals an error. *

- * + * *

*
Scheduler:
*
{@code onErrorReturnItem} does not operate by default on a particular {@link Scheduler}.
@@ -4209,7 +4209,7 @@ public final Maybe onErrorComplete(@NonNull Predicate pred * Resumes the flow with a {@link SingleSource} returned for the failure {@link Throwable} of the current {@code Single} by a * function instead of signaling the error via {@code onError}. *

- * + * *

* By default, when a {@code Single} encounters an error that prevents it from emitting the expected item to * its {@link SingleObserver}, the {@code Single} invokes its {@code SingleObserver}'s {@code onError} method, and then quits @@ -4267,7 +4267,7 @@ public final Single onTerminateDetach() { /** * Repeatedly re-subscribes to the current {@code Single} and emits each success value as a {@link Flowable} sequence. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -4288,7 +4288,7 @@ public final Flowable repeat() { /** * Re-subscribes to the current {@code Single} at most the given number of times and emits each success value as a {@link Flowable} sequence. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -4313,7 +4313,7 @@ public final Flowable repeat(long times) { * the {@link Publisher} returned by the handler function signals a value in response to a * value signaled through the {@link Flowable} the handler receives. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer. @@ -4341,7 +4341,7 @@ public final Flowable repeatWhen(@NonNull Function, * Re-subscribes to the current {@code Single} until the given {@link BooleanSupplier} returns {@code true} * and emits the success items as a {@link Flowable} sequence. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -4867,7 +4867,7 @@ public final void subscribe(@NonNull SingleObserver observer) { /** * Asynchronously subscribes {@link SingleObserver}s to this {@code Single} on the specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
You specify which {@code Scheduler} this operator will use.
@@ -5308,7 +5308,7 @@ private Single timeout0(final long timeout, final TimeUnit unit, final Schedu /** * Calls the specified converter function during assembly time and returns its resulting value. *

- * + * *

* This allows fluent conversion to any other type. *

@@ -5351,7 +5351,7 @@ public final Completable ignoreElement() { /** * Converts this {@code Single} into a {@link Flowable}. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -5376,7 +5376,7 @@ public final Flowable toFlowable() { /** * Returns a {@link Future} representing the single value emitted by this {@code Single}. *

- * + * *

* Cancelling the {@code Future} will cancel the subscription to the current {@code Single}. *

@@ -5397,7 +5397,7 @@ public final Future toFuture() { /** * Converts this {@code Single} into a {@link Maybe}. *

- * + * *

*
Scheduler:
*
{@code toMaybe} does not operate by default on a particular {@link Scheduler}.
@@ -5418,7 +5418,7 @@ public final Maybe toMaybe() { /** * Converts this {@code Single} into an {@link Observable}. *

- * + * *

*
Scheduler:
*
{@code toObservable} does not operate by default on a particular {@link Scheduler}.
diff --git a/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java b/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java index 3e80c68ced..041fc3bd1b 100644 --- a/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java @@ -34,7 +34,7 @@ * can wait for all intended {@link Subscriber}s to {@link Flowable#subscribe} to the {@code Flowable} * before the {@code Flowable} begins emitting items. *

- * + * *

* When the upstream terminates, the {@code ConnectableFlowable} remains in this terminated state and, * depending on the actual underlying implementation, relays cached events to late {@link Subscriber}s. diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecent.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecent.java index bfd206eea9..d679dd937f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecent.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecent.java @@ -23,7 +23,7 @@ * Returns an Iterable that always returns the item most recently emitted by an Observable, or a * seed value if no item has yet been emitted. *

- * + * * * @param the value type */ diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java index 6972bcc777..b8e79eda54 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java @@ -27,7 +27,7 @@ /** * Returns an Iterable that blocks until the Observable emits another item, then returns that item. *

- * + * * * @param the value type */ diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecent.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecent.java index 1332351cac..75e8966ca9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecent.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecent.java @@ -23,7 +23,7 @@ * Returns an Iterable that always returns the item most recently emitted by an Observable, or a * seed value if no item has yet been emitted. *

- * + * * * @param the value type */ diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNext.java index 53aed40999..3ebeb0e1f0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNext.java @@ -25,7 +25,7 @@ /** * Returns an Iterable that blocks until the Observable emits another item, then returns that item. *

- * + * * * @param the value type */ diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapPublisher.java index c89913791d..e453f28b68 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapPublisher.java @@ -28,7 +28,7 @@ * A Flowable that emits items based on applying a specified function to the item emitted by the * source Single, where that function returns a Publisher. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer diff --git a/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java b/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java index 2221ac62b0..107971ca88 100644 --- a/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java +++ b/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java @@ -32,7 +32,7 @@ * can wait for all intended {@link Observer}s to {@link Observable#subscribe} to the {@code Observable} * before the {@code Observable} begins emitting items. *

- * + * *

* When the upstream terminates, the {@code ConnectableObservable} remains in this terminated state and, * depending on the actual underlying implementation, relays cached events to late {@link Observer}s. diff --git a/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java b/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java index 61cf92cba3..ce89f7b856 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java @@ -28,7 +28,7 @@ * Subject that emits the most recent item it has observed and all subsequent observed items to each subscribed * {@link Observer}. *

- * + * *

* This subject does not have a public constructor by design; a new empty instance of this * {@code BehaviorSubject} can be created via the {@link #create()} method and From c4780977c766f301d38b99e304edf492e66c7631 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Sat, 4 Apr 2020 11:29:22 +0200 Subject: [PATCH 018/521] 3.x: Fix Observable.flatMap with maxConcurrency hangs (#6946) * 3.x: Fix Observable.flatMap with maxConcurrency hangs * Verify Flowable --- .../observable/ObservableFlatMap.java | 39 +++++++++++++------ .../flowable/FlowableFlatMapTest.java | 24 ++++++++++++ .../observable/ObservableFlatMapTest.java | 24 ++++++++++++ 3 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java index d2ca19c31a..6265a5d557 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java @@ -318,6 +318,7 @@ void drainLoop() { if (checkTerminate()) { return; } + int innerCompleted = 0; SimplePlainQueue svq = queue; if (svq != null) { @@ -333,9 +334,18 @@ void drainLoop() { } child.onNext(o); + innerCompleted++; } } + if (innerCompleted != 0) { + if (maxConcurrency != Integer.MAX_VALUE) { + subscribeMore(innerCompleted); + innerCompleted = 0; + } + continue; + } + boolean d = done; svq = queue; InnerObserver[] inner = observers.get(); @@ -353,7 +363,6 @@ void drainLoop() { return; } - int innerCompleted = 0; if (n != 0) { int j = Math.min(n - 1, lastIndex); @@ -415,20 +424,12 @@ void drainLoop() { if (innerCompleted != 0) { if (maxConcurrency != Integer.MAX_VALUE) { - while (innerCompleted-- != 0) { - ObservableSource p; - synchronized (this) { - p = sources.poll(); - if (p == null) { - wip--; - continue; - } - } - subscribeInner(p); - } + subscribeMore(innerCompleted); + innerCompleted = 0; } continue; } + missed = addAndGet(-missed); if (missed == 0) { break; @@ -436,6 +437,20 @@ void drainLoop() { } } + void subscribeMore(int innerCompleted) { + while (innerCompleted-- != 0) { + ObservableSource p; + synchronized (this) { + p = sources.poll(); + if (p == null) { + wip--; + continue; + } + } + subscribeInner(p); + } + } + boolean checkTerminate() { if (disposed) { return true; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java index 1687c2dd79..935182fd7a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java @@ -1478,4 +1478,28 @@ public void innerCompletesAfterOnNextInDrainThenCancels() { .requestMore(1) .assertValuesOnly(1); } + + @Test(timeout = 5000) + public void mixedScalarAsync() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + Flowable + .range(0, 20) + .flatMap( + integer -> { + if (integer % 5 != 0) { + return Flowable + .just(integer); + } + + return Flowable + .just(-integer) + .observeOn(Schedulers.computation()); + }, + false, + 1 + ) + .ignoreElements() + .blockingAwait(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java index 165aa9dabf..1e083d217c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java @@ -1240,4 +1240,28 @@ public void fusedInnerCrash2() { to.assertFailure(TestException.class, 1, 2); } + + @Test(timeout = 5000) + public void mixedScalarAsync() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + Observable + .range(0, 20) + .flatMap( + integer -> { + if (integer % 5 != 0) { + return Observable + .just(integer); + } + + return Observable + .just(-integer) + .observeOn(Schedulers.computation()); + }, + false, + 1 + ) + .ignoreElements() + .blockingAwait(); + } + } } From c13ba601b1a64075537dbdb6458499b0390f3cd5 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Tue, 7 Apr 2020 12:20:53 +0200 Subject: [PATCH 019/521] 3.x: Workaround flakyness of multiThreadedWithNPE* tests (#6953) * 3.x: Workaround flakyness of multiThreadedWithNPE* tests * Do not run the original test --- .../flowable/FlowableSerializeTest.java | 34 +++++++++++++++++-- .../observable/ObservableSerializeTest.java | 34 +++++++++++++++++-- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerializeTest.java index cd848e683d..9407df90cf 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerializeTest.java @@ -79,7 +79,22 @@ public void multiThreadedBasic() { } @Test - public void multiThreadedWithNPE() { + public void multiThreadedWithNPEFlaky() throws InterruptedException { + int max = 9; + for (int i = 0; i <= max; i++) { + try { + multiThreadedWithNPE(); + return; + } catch (AssertionError ex) { + if (i == max) { + throw ex; + } + } + Thread.sleep((long)(1000 * Math.random() + 100)); + } + } + + void multiThreadedWithNPE() { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null); Flowable w = Flowable.unsafeCreate(onSubscribe); @@ -108,7 +123,22 @@ public void multiThreadedWithNPE() { } @Test - public void multiThreadedWithNPEinMiddle() { + public void multiThreadedWithNPEinMiddleFlaky() throws InterruptedException { + int max = 9; + for (int i = 0; i <= max; i++) { + try { + multiThreadedWithNPEinMiddle(); + return; + } catch (AssertionError ex) { + if (i == max) { + throw ex; + } + } + Thread.sleep((long)(1000 * Math.random() + 100)); + } + } + + void multiThreadedWithNPEinMiddle() { boolean lessThan9 = false; for (int i = 0; i < 3; i++) { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerializeTest.java index 2a2b7dff0d..2457a9a312 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerializeTest.java @@ -78,7 +78,22 @@ public void multiThreadedBasic() { } @Test - public void multiThreadedWithNPE() { + public void multiThreadedWithNPEFlaky() throws InterruptedException { + int max = 9; + for (int i = 0; i <= max; i++) { + try { + multiThreadedWithNPE(); + return; + } catch (AssertionError ex) { + if (i == max) { + throw ex; + } + } + Thread.sleep((long)(1000 * Math.random() + 100)); + } + } + + void multiThreadedWithNPE() { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null); Observable w = Observable.unsafeCreate(onSubscribe); @@ -107,7 +122,22 @@ public void multiThreadedWithNPE() { } @Test - public void multiThreadedWithNPEinMiddle() { + public void multiThreadedWithNPEinMiddleFlaky() throws InterruptedException { + int max = 9; + for (int i = 0; i <= max; i++) { + try { + multiThreadedWithNPEinMiddle(); + return; + } catch (AssertionError ex) { + if (i == max) { + throw ex; + } + } + Thread.sleep((long)(1000 * Math.random() + 100)); + } + } + + void multiThreadedWithNPEinMiddle() { boolean lessThan9 = false; for (int i = 0; i < 3; i++) { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); From 51de02143146e38f82a3ee857f72dced73f29020 Mon Sep 17 00:00:00 2001 From: Zachary Trant Date: Tue, 7 Apr 2020 09:44:23 -0500 Subject: [PATCH 020/521] docs: Quick Javadoc fixes. (#6943) * docs: Quick Javadoc fixes. Added @code tag wherever < or > were used, also removed a few self-closing

tags. Both of these issues cause errors with the latest version of Javadoc. Signed-off-by: Zachary Trant * Update FuseToFlowable.java * Update FuseToMaybe.java * Update FuseToObservable.java * Update SchedulerWhen.java * Update BlockingFlowableNextTest.java * Update BlockingObservableNextTest.java * Update SerializedObserverTest.java * Update SerializedObserverTest.java * Update SerializedObserverTest.java * Update SerializedSubscriberTest.java * Update BaseTck.java * Update SchedulerWhen.java * Update SerializedObserverTest.java --- .../rxjava3/internal/fuseable/FuseToFlowable.java | 6 ++++-- .../reactivex/rxjava3/internal/fuseable/FuseToMaybe.java | 6 ++++-- .../rxjava3/internal/fuseable/FuseToObservable.java | 6 ++++-- .../rxjava3/internal/schedulers/SchedulerWhen.java | 6 ++++++ .../operators/flowable/BlockingFlowableNextTest.java | 4 ++-- .../operators/observable/BlockingObservableNextTest.java | 4 ++-- .../rxjava3/observers/SerializedObserverTest.java | 7 +++---- .../rxjava3/subscribers/SerializedSubscriberTest.java | 4 ++-- src/test/java/io/reactivex/rxjava3/tck/BaseTck.java | 2 +- 9 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java index 6ad462c706..a1572b0e53 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java @@ -21,8 +21,10 @@ * the operator goes from Flowable to some other reactive type and then the sequence calls * for toFlowable again: *

- * Single<Integer> single = Flowable.range(1, 10).reduce((a, b) -> a + b);
- * Flowable<Integer> flowable = single.toFlowable();
+ * {@code 
+ * Single single = Flowable.range(1, 10).reduce((a, b) -> a + b);
+ * Flowable flowable = single.toFlowable();
+ * }
  * 
* * The {@code Single.toFlowable()} will check for this interface and call the {@link #fuseToFlowable()} diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java index 572255a7bc..3fa2334be6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java @@ -21,8 +21,10 @@ * the operator goes from Maybe to some other reactive type and then the sequence calls * for toMaybe again: *
- * Single<Integer> single = Maybe.just(1).isEmpty();
- * Maybe<Integer> maybe = single.toMaybe();
+ * {@code 
+ * Single single = Maybe.just(1).isEmpty();
+ * Maybe maybe = single.toMaybe();
+ * }
  * 
* * The {@code Single.toMaybe()} will check for this interface and call the {@link #fuseToMaybe()} diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToObservable.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToObservable.java index 567d3c9a7c..c9371ae538 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToObservable.java @@ -21,8 +21,10 @@ * the operator goes from Observable to some other reactive type and then the sequence calls * for toObservable again: *
- * Single<Integer> single = Observable.range(1, 10).reduce((a, b) -> a + b);
- * Observable<Integer> observable = single.toObservable();
+ * {@code
+ * Single single = Observable.range(1, 10).reduce((a, b) -> a + b);
+ * Observable observable = single.toObservable();
+ * }
  * 
* * The {@code Single.toObservable()} will check for this interface and call the {@link #fuseToObservable()} diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java index 57033f4563..fe3c624786 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java @@ -53,11 +53,13 @@ * thread pool: * *
+ * {@code 
  * Scheduler limitScheduler = Schedulers.computation().when(workers -> {
  *  // use merge max concurrent to limit the number of concurrent
  *  // callbacks two at a time
  *  return Completable.merge(Observable.merge(workers), 2);
  * });
+ * }
  * 
*

* This is a slightly different way to limit the concurrency but it has some @@ -71,11 +73,13 @@ * to the second. * *

+ * {@code 
  * Scheduler limitScheduler = Schedulers.computation().when(workers -> {
  *  // use merge max concurrent to limit the number of concurrent
  *  // Observables two at a time
  *  return Completable.merge(Observable.merge(workers, 2));
  * });
+ * }
  * 
* * Slowing down the rate to no more than than 1 a second. This suffers from the @@ -84,6 +88,7 @@ * algorithm). * *
+ * {@code 
  * Scheduler slowScheduler = Schedulers.computation().when(workers -> {
  *  // use concatenate to make each worker happen one at a time.
  *  return Completable.concat(workers.map(actions -> {
@@ -91,6 +96,7 @@
  *      return Completable.merge(actions.delaySubscription(1, TimeUnit.SECONDS));
  *  }));
  * });
+ * }
  * 
*

History 2.0.1 - experimental * @since 2.1 diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNextTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNextTest.java index 35c1501ae0..714bde2e21 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNextTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNextTest.java @@ -223,8 +223,8 @@ public void nextWithCallingHasNextMultipleTimes() { /** * 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 + *

+ * This results in output such as {@code => a: 1 b: 2 c: 89} * * @throws Throwable some method call is declared throws */ diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNextTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNextTest.java index 357d512e9b..82397f45dd 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNextTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNextTest.java @@ -227,8 +227,8 @@ public void nextWithCallingHasNextMultipleTimes() { /** * 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 + *

+ * This results in output such as {@code => a: 1 b: 2 c: 89} * * @throws Throwable some method call is declared throws */ diff --git a/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java index 48bd28aaf8..4c8819937e 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java @@ -340,11 +340,11 @@ public void onNext(String t) { * * When using SynchronizedSubscriber we get this output: * - * p1: 18 p2: 68 => should be close to each other unless we have thread starvation + * {@code p1: 18 p2: 68 =>} should be close to each other unless we have thread starvation * * When using SerializedObserver we get: * - * p1: 1 p2: 2445261 => should be close to each other unless we have thread starvation + * {@code p1: 1 p2: 2445261 =>} should be close to each other unless we have thread starvation * * This demonstrates how SynchronizedSubscriber balances back and forth better, and blocks emission. * The real issue in this example is the async buffer-bloat, so we need backpressure. @@ -1042,8 +1042,7 @@ public void run() { } }; - TestHelper.race(r1, r2); - + TestHelper.race(r1, r2); to.awaitDone(5, TimeUnit.SECONDS) .assertError(ex) .assertNotComplete(); diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java index ee842fcf02..7391b212ae 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java @@ -341,11 +341,11 @@ public void onNext(String t) { * * When using SynchronizedSubscriber we get this output: * - * p1: 18 p2: 68 => should be close to each other unless we have thread starvation + * {@code p1: 18 p2: 68 =>} should be close to each other unless we have thread starvation * * When using SerializedSubscriber we get: * - * p1: 1 p2: 2445261 => should be close to each other unless we have thread starvation + * {@code p1: 1 p2: 2445261 =>} should be close to each other unless we have thread starvation * * This demonstrates how SynchronizedSubscriber balances back and forth better, and blocks emission. * The real issue in this example is the async buffer-bloat, so we need backpressure. diff --git a/src/test/java/io/reactivex/rxjava3/tck/BaseTck.java b/src/test/java/io/reactivex/rxjava3/tck/BaseTck.java index 82c25e9591..4de830b877 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/BaseTck.java +++ b/src/test/java/io/reactivex/rxjava3/tck/BaseTck.java @@ -51,7 +51,7 @@ public long maxElementsFromPublisher() { /** * Creates an Iterable with the specified number of elements or an infinite one if - * elements > {@link Integer#MAX_VALUE}. + * {@code elements >} {@link Integer#MAX_VALUE}. * @param elements the number of elements to return, {@link Integer#MAX_VALUE} means an infinite sequence * @return the Iterable */ From b47783e3186f83be6a87257a2d13c9619bccaa42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Karnok?= Date: Tue, 7 Apr 2020 16:46:27 +0200 Subject: [PATCH 021/521] Fix trailing spaces. --- .../reactivex/rxjava3/internal/fuseable/FuseToFlowable.java | 2 +- .../io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java | 2 +- .../rxjava3/internal/schedulers/SchedulerWhen.java | 6 +++--- .../reactivex/rxjava3/observers/SerializedObserverTest.java | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java index a1572b0e53..c0359640f1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java @@ -21,7 +21,7 @@ * the operator goes from Flowable to some other reactive type and then the sequence calls * for toFlowable again: *

- * {@code 
+ * {@code
  * Single single = Flowable.range(1, 10).reduce((a, b) -> a + b);
  * Flowable flowable = single.toFlowable();
  * }
diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java
index 3fa2334be6..d7655bba86 100644
--- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java
+++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java
@@ -21,7 +21,7 @@
  * the operator goes from Maybe to some other reactive type and then the sequence calls
  * for toMaybe again:
  * 
- * {@code 
+ * {@code
  * Single single = Maybe.just(1).isEmpty();
  * Maybe maybe = single.toMaybe();
  * }
diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java
index fe3c624786..4da5b161b8 100644
--- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java
+++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java
@@ -53,7 +53,7 @@
  * thread pool:
  *
  * 
- * {@code 
+ * {@code
  * Scheduler limitScheduler = Schedulers.computation().when(workers -> {
  *  // use merge max concurrent to limit the number of concurrent
  *  // callbacks two at a time
@@ -73,7 +73,7 @@
  * to the second.
  *
  * 
- * {@code 
+ * {@code
  * Scheduler limitScheduler = Schedulers.computation().when(workers -> {
  *  // use merge max concurrent to limit the number of concurrent
  *  // Observables two at a time
@@ -88,7 +88,7 @@
  * algorithm).
  *
  * 
- * {@code 
+ * {@code
  * Scheduler slowScheduler = Schedulers.computation().when(workers -> {
  *  // use concatenate to make each worker happen one at a time.
  *  return Completable.concat(workers.map(actions -> {
diff --git a/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java
index 4c8819937e..874670bc5f 100644
--- a/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java
+++ b/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java
@@ -1042,7 +1042,8 @@ public void run() {
                 }
             };
 
-            TestHelper.race(r1, r2);       
+            TestHelper.race(r1, r2);
+
             to.awaitDone(5, TimeUnit.SECONDS)
             .assertError(ex)
             .assertNotComplete();

From e40916e5fa772af416d0174c0e3aaa49de2f56a9 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sat, 11 Apr 2020 16:33:33 +0200
Subject: [PATCH 022/521] Bump gradle-bintray-plugin from 1.8.4 to 1.8.5
 (#6951)

Bumps gradle-bintray-plugin from 1.8.4 to 1.8.5.

Signed-off-by: dependabot-preview[bot] 

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index f51d004ee4..69a4276574 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ buildscript {
   ext.jacocoVersion = "0.8.4"
   ext.animalSnifferVersion = "1.5.0"
   ext.licenseVersion = "0.15.0"
-  ext.bintrayVersion = "1.8.4"
+  ext.bintrayVersion = "1.8.5"
   ext.jfrogExtractorVersion = "4.15.1"
   ext.bndVersion = "5.0.1"
   ext.checkstyleVersion = "8.26"

From 81c3ee742233236f5a3a53e3e903e655c8119e30 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Tue, 14 Apr 2020 09:31:45 +0200
Subject: [PATCH 023/521] Bump guava from 28.2-jre to 29.0-jre (#6959)

Bumps [guava](https://github.com/google/guava) from 28.2-jre to 29.0-jre.
- [Release notes](https://github.com/google/guava/releases)
- [Commits](https://github.com/google/guava/commits)

Signed-off-by: dependabot-preview[bot] 

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index 69a4276574..b96ba76139 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,7 +9,7 @@ buildscript {
   ext.mockitoVersion = "3.3.3"
   ext.jmhLibVersion = "1.21"
   ext.jmhGradleVersion = "0.5.0"
-  ext.guavaVersion = "28.2-jre"
+  ext.guavaVersion = "29.0-jre"
   ext.jacocoVersion = "0.8.4"
   ext.animalSnifferVersion = "1.5.0"
   ext.licenseVersion = "0.15.0"

From b6a994f7c5956c69ee267b721806beebd81886e1 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Fri, 24 Apr 2020 14:33:57 +0200
Subject: [PATCH 024/521] Bump build-info-extractor-gradle from 4.15.1 to
 4.15.2 (#6967)

Bumps build-info-extractor-gradle from 4.15.1 to 4.15.2.

Signed-off-by: dependabot-preview[bot] 

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index b96ba76139..c35b696d20 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,7 +14,7 @@ buildscript {
   ext.animalSnifferVersion = "1.5.0"
   ext.licenseVersion = "0.15.0"
   ext.bintrayVersion = "1.8.5"
-  ext.jfrogExtractorVersion = "4.15.1"
+  ext.jfrogExtractorVersion = "4.15.2"
   ext.bndVersion = "5.0.1"
   ext.checkstyleVersion = "8.26"
 

From fbee37a052c8c4052614db021cd242de6f710221 Mon Sep 17 00:00:00 2001
From: David Karnok 
Date: Mon, 27 Apr 2020 08:46:51 +0200
Subject: [PATCH 025/521] 3.x: Fix scheduled tasks' fatal exception behavior
 (#6956)

* 3.x: Fix scheduled tasks' fatal exception behavior

* Fix direct periodic tasks not stopping upon crash.

* Fix the mistake introduced in the previous commit.

* Ensure task exception is rethrown so that the parent FutureTask can end

* Update the abstract Scheduler's tasks too

* Adjust some test expectation with DisposeTask
---
 .../io/reactivex/rxjava3/core/Scheduler.java  |  17 ++-
 .../schedulers/ExecutorScheduler.java         |  28 +++-
 .../schedulers/InstantPeriodicTask.java       |   6 +-
 .../ScheduledDirectPeriodicTask.java          |   6 +-
 .../schedulers/ScheduledDirectTask.java       |  16 ++-
 .../schedulers/ScheduledRunnable.java         |   1 +
 .../rxjava3/core/DisposeTaskTest.java         |  50 +++++++
 .../rxjava3/core/PeriodicDirectTaskTest.java  |  57 ++++++++
 .../schedulers/BooleanRunnableTest.java       |  50 +++++++
 .../schedulers/InstantPeriodicTaskTest.java   |   7 +-
 .../schedulers/InterruptibleRunnableTest.java |  50 +++++++
 .../ScheduledDirectPeriodicTaskTest.java      |   9 +-
 .../schedulers/ScheduledRunnableTest.java     |   7 +-
 .../schedulers/ComputationSchedulerTests.java | 127 +++++++++++++++++-
 .../rxjava3/schedulers/SchedulerTest.java     |  30 +++--
 15 files changed, 425 insertions(+), 36 deletions(-)
 create mode 100644 src/test/java/io/reactivex/rxjava3/core/DisposeTaskTest.java
 create mode 100644 src/test/java/io/reactivex/rxjava3/core/PeriodicDirectTaskTest.java
 create mode 100644 src/test/java/io/reactivex/rxjava3/internal/schedulers/BooleanRunnableTest.java
 create mode 100644 src/test/java/io/reactivex/rxjava3/internal/schedulers/InterruptibleRunnableTest.java

diff --git a/src/main/java/io/reactivex/rxjava3/core/Scheduler.java b/src/main/java/io/reactivex/rxjava3/core/Scheduler.java
index 7b96792bbe..069c3d5bfb 100644
--- a/src/main/java/io/reactivex/rxjava3/core/Scheduler.java
+++ b/src/main/java/io/reactivex/rxjava3/core/Scheduler.java
@@ -18,11 +18,9 @@
 
 import io.reactivex.rxjava3.annotations.*;
 import io.reactivex.rxjava3.disposables.Disposable;
-import io.reactivex.rxjava3.exceptions.Exceptions;
 import io.reactivex.rxjava3.functions.Function;
 import io.reactivex.rxjava3.internal.disposables.*;
 import io.reactivex.rxjava3.internal.schedulers.*;
-import io.reactivex.rxjava3.internal.util.ExceptionHelper;
 import io.reactivex.rxjava3.plugins.RxJavaPlugins;
 import io.reactivex.rxjava3.schedulers.SchedulerRunnableIntrospection;
 
@@ -542,9 +540,10 @@ public void run() {
                 try {
                     run.run();
                 } catch (Throwable ex) {
-                    Exceptions.throwIfFatal(ex);
-                    worker.dispose();
-                    throw ExceptionHelper.wrapOrThrow(ex);
+                    // Exceptions.throwIfFatal(ex); nowhere to go
+                    dispose();
+                    RxJavaPlugins.onError(ex);
+                    throw ex;
                 }
             }
         }
@@ -586,7 +585,13 @@ static final class DisposeTask implements Disposable, Runnable, SchedulerRunnabl
         public void run() {
             runner = Thread.currentThread();
             try {
-                decoratedRun.run();
+                try {
+                    decoratedRun.run();
+                } catch (Throwable ex) {
+                    // Exceptions.throwIfFatal(e); nowhere to go
+                    RxJavaPlugins.onError(ex);
+                    throw ex;
+                }
             } finally {
                 dispose();
                 runner = null;
diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java
index 6387069548..2e76702b1b 100644
--- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java
+++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java
@@ -320,6 +320,10 @@ public void run() {
                 }
                 try {
                     actual.run();
+                } catch (Throwable ex) {
+                    // Exceptions.throwIfFatal(ex); nowhere to go
+                    RxJavaPlugins.onError(ex);
+                    throw ex;
                 } finally {
                     lazySet(true);
                 }
@@ -386,7 +390,13 @@ public void run() {
                     thread = Thread.currentThread();
                     if (compareAndSet(READY, RUNNING)) {
                         try {
-                            run.run();
+                            try {
+                                run.run();
+                            } catch (Throwable ex) {
+                                // Exceptions.throwIfFatal(ex); nowhere to go
+                                RxJavaPlugins.onError(ex);
+                                throw ex;
+                            }
                         } finally {
                             thread = null;
                             if (compareAndSet(RUNNING, FINISHED)) {
@@ -463,11 +473,17 @@ public void run() {
             Runnable r = get();
             if (r != null) {
                 try {
-                    r.run();
-                } finally {
-                    lazySet(null);
-                    timed.lazySet(DisposableHelper.DISPOSED);
-                    direct.lazySet(DisposableHelper.DISPOSED);
+                    try {
+                        r.run();
+                    } finally {
+                        lazySet(null);
+                        timed.lazySet(DisposableHelper.DISPOSED);
+                        direct.lazySet(DisposableHelper.DISPOSED);
+                    }
+                } catch (Throwable ex) {
+                    // Exceptions.throwIfFatal(ex); nowhere to go
+                    RxJavaPlugins.onError(ex);
+                    throw ex;
                 }
             }
         }
diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTask.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTask.java
index a855b44683..ad85765ed6 100644
--- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTask.java
+++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTask.java
@@ -20,7 +20,6 @@
 import java.util.concurrent.atomic.AtomicReference;
 
 import io.reactivex.rxjava3.disposables.Disposable;
-import io.reactivex.rxjava3.exceptions.Exceptions;
 import io.reactivex.rxjava3.internal.functions.Functions;
 import io.reactivex.rxjava3.plugins.RxJavaPlugins;
 
@@ -54,12 +53,13 @@ public Void call() {
         runner = Thread.currentThread();
         try {
             task.run();
-            setRest(executor.submit(this));
             runner = null;
+            setRest(executor.submit(this));
         } catch (Throwable ex) {
-            Exceptions.throwIfFatal(ex);
+            // Exceptions.throwIfFatal(ex); nowhere to go
             runner = null;
             RxJavaPlugins.onError(ex);
+            throw ex;
         }
         return null;
     }
diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java
index ac30532e68..4be6e4b558 100644
--- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java
+++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java
@@ -16,7 +16,6 @@
 
 package io.reactivex.rxjava3.internal.schedulers;
 
-import io.reactivex.rxjava3.exceptions.Exceptions;
 import io.reactivex.rxjava3.plugins.RxJavaPlugins;
 
 /**
@@ -39,10 +38,11 @@ public void run() {
             runnable.run();
             runner = null;
         } catch (Throwable ex) {
-            Exceptions.throwIfFatal(ex);
+            // Exceptions.throwIfFatal(ex); nowhere to go
             runner = null;
-            lazySet(FINISHED);
+            dispose();
             RxJavaPlugins.onError(ex);
+            throw ex;
         }
     }
 }
diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectTask.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectTask.java
index d48ccf8ab6..4959342d06 100644
--- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectTask.java
+++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectTask.java
@@ -18,6 +18,8 @@
 
 import java.util.concurrent.Callable;
 
+import io.reactivex.rxjava3.plugins.RxJavaPlugins;
+
 /**
  * A Callable to be submitted to an ExecutorService that runs a Runnable
  * action and manages completion/cancellation.
@@ -35,10 +37,16 @@ public ScheduledDirectTask(Runnable runnable) {
     public Void call() {
         runner = Thread.currentThread();
         try {
-            runnable.run();
-        } finally {
-            lazySet(FINISHED);
-            runner = null;
+            try {
+                runnable.run();
+            } finally {
+                lazySet(FINISHED);
+                runner = null;
+            }
+        } catch (Throwable ex) {
+            // Exceptions.throwIfFatal(e); nowhere to go
+            RxJavaPlugins.onError(ex);
+            throw ex;
         }
         return null;
     }
diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java
index 45072a2abd..d437985890 100644
--- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java
+++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java
@@ -66,6 +66,7 @@ public void run() {
             } catch (Throwable e) {
                 // Exceptions.throwIfFatal(e); nowhere to go
                 RxJavaPlugins.onError(e);
+                throw e;
             }
         } finally {
             lazySet(THREAD_INDEX, null);
diff --git a/src/test/java/io/reactivex/rxjava3/core/DisposeTaskTest.java b/src/test/java/io/reactivex/rxjava3/core/DisposeTaskTest.java
new file mode 100644
index 0000000000..64a13fdb6d
--- /dev/null
+++ b/src/test/java/io/reactivex/rxjava3/core/DisposeTaskTest.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2016-present, RxJava Contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
+ * the License for the specific language governing permissions and limitations under the License.
+ */
+
+package io.reactivex.rxjava3.core;
+
+import static org.junit.Assert.fail;
+import static org.testng.Assert.assertTrue;
+
+import org.junit.Test;
+
+import io.reactivex.rxjava3.core.Scheduler.DisposeTask;
+import io.reactivex.rxjava3.exceptions.TestException;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+import io.reactivex.rxjava3.testsupport.TestHelper;
+
+public class DisposeTaskTest extends RxJavaTest {
+
+    @Test
+    public void runnableThrows() throws Throwable {
+        TestHelper.withErrorTracking(errors -> {
+
+            Scheduler.Worker worker = Schedulers.single().createWorker();
+
+            DisposeTask task = new DisposeTask(() -> {
+                throw new TestException();
+            }, worker);
+
+            try {
+                task.run();
+                fail("Should have thrown!");
+            } catch (TestException expected) {
+                // expected
+            }
+
+            TestHelper.assertUndeliverable(errors, 0, TestException.class);
+
+            assertTrue(worker.isDisposed());
+        });
+    }
+}
diff --git a/src/test/java/io/reactivex/rxjava3/core/PeriodicDirectTaskTest.java b/src/test/java/io/reactivex/rxjava3/core/PeriodicDirectTaskTest.java
new file mode 100644
index 0000000000..cd8469ae07
--- /dev/null
+++ b/src/test/java/io/reactivex/rxjava3/core/PeriodicDirectTaskTest.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2016-present, RxJava Contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
+ * the License for the specific language governing permissions and limitations under the License.
+ */
+
+package io.reactivex.rxjava3.core;
+
+import static org.junit.Assert.fail;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.reactivex.rxjava3.core.Scheduler.PeriodicDirectTask;
+import io.reactivex.rxjava3.exceptions.TestException;
+import io.reactivex.rxjava3.plugins.RxJavaPlugins;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+import io.reactivex.rxjava3.testsupport.TestHelper;
+
+public class PeriodicDirectTaskTest extends RxJavaTest {
+
+    @Test
+    public void runnableThrows() {
+        List errors = TestHelper.trackPluginErrors();
+        try {
+            Scheduler.Worker worker = Schedulers.single().createWorker();
+
+            PeriodicDirectTask task = new PeriodicDirectTask(() -> {
+                throw new TestException();
+            }, worker);
+
+            try {
+                task.run();
+                fail("Should have thrown!");
+            } catch (TestException expected) {
+                // expected
+            }
+
+            TestHelper.assertUndeliverable(errors, 0, TestException.class);
+
+            assertTrue(worker.isDisposed());
+
+            task.run();
+        } finally {
+            RxJavaPlugins.reset();
+        }
+    }
+}
diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/BooleanRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/BooleanRunnableTest.java
new file mode 100644
index 0000000000..424d108e8a
--- /dev/null
+++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/BooleanRunnableTest.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2016-present, RxJava Contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
+ * the License for the specific language governing permissions and limitations under the License.
+ */
+
+package io.reactivex.rxjava3.internal.schedulers;
+
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.reactivex.rxjava3.core.RxJavaTest;
+import io.reactivex.rxjava3.exceptions.TestException;
+import io.reactivex.rxjava3.internal.schedulers.ExecutorScheduler.ExecutorWorker.BooleanRunnable;
+import io.reactivex.rxjava3.plugins.RxJavaPlugins;
+import io.reactivex.rxjava3.testsupport.TestHelper;
+
+public class BooleanRunnableTest extends RxJavaTest {
+
+    @Test
+    public void runnableThrows() {
+        List errors = TestHelper.trackPluginErrors();
+        try {
+            BooleanRunnable task = new BooleanRunnable(() -> {
+                throw new TestException();
+            });
+
+            try {
+                task.run();
+                fail("Should have thrown!");
+            } catch (TestException expected) {
+                // expected
+            }
+
+            TestHelper.assertUndeliverable(errors, 0, TestException.class);
+        } finally {
+            RxJavaPlugins.reset();
+        }
+    }
+}
diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTaskTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTaskTest.java
index 7b7299f309..2f530f2afa 100644
--- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTaskTest.java
+++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTaskTest.java
@@ -44,7 +44,12 @@ public void run() {
                 }
             }, exec);
 
-            assertNull(task.call());
+            try {
+                task.call();
+                fail("Should have thrown!");
+            } catch (TestException excepted) {
+                // excepted
+            }
 
             TestHelper.assertUndeliverable(errors, 0, TestException.class);
         } finally {
diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/InterruptibleRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/InterruptibleRunnableTest.java
new file mode 100644
index 0000000000..9abc100862
--- /dev/null
+++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/InterruptibleRunnableTest.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2016-present, RxJava Contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
+ * the License for the specific language governing permissions and limitations under the License.
+ */
+
+package io.reactivex.rxjava3.internal.schedulers;
+
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.reactivex.rxjava3.core.RxJavaTest;
+import io.reactivex.rxjava3.exceptions.TestException;
+import io.reactivex.rxjava3.internal.schedulers.ExecutorScheduler.ExecutorWorker.InterruptibleRunnable;
+import io.reactivex.rxjava3.plugins.RxJavaPlugins;
+import io.reactivex.rxjava3.testsupport.TestHelper;
+
+public class InterruptibleRunnableTest extends RxJavaTest {
+
+    @Test
+    public void runnableThrows() {
+        List errors = TestHelper.trackPluginErrors();
+        try {
+            InterruptibleRunnable task = new InterruptibleRunnable(() -> {
+                throw new TestException();
+            }, null);
+
+            try {
+                task.run();
+                fail("Should have thrown!");
+            } catch (TestException expected) {
+                // expected
+            }
+
+            TestHelper.assertUndeliverable(errors, 0, TestException.class);
+        } finally {
+            RxJavaPlugins.reset();
+        }
+    }
+}
diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTaskTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTaskTest.java
index 23afeca2ce..7736d5e40b 100644
--- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTaskTest.java
+++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTaskTest.java
@@ -13,6 +13,8 @@
 
 package io.reactivex.rxjava3.internal.schedulers;
 
+import static org.junit.Assert.fail;
+
 import java.util.List;
 
 import org.junit.Test;
@@ -35,7 +37,12 @@ public void run() {
                 }
             });
 
-            task.run();
+            try {
+                task.run();
+                fail("Should have thrown!");
+            } catch (TestException expected) {
+                // expected
+            }
 
             TestHelper.assertUndeliverable(errors, 0, TestException.class);
         } finally {
diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java
index a8920fbd93..f0577e41ca 100644
--- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java
+++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java
@@ -208,7 +208,12 @@ public void run() {
             }, set);
             set.add(run);
 
-            run.run();
+            try {
+                run.run();
+                fail("Should have thrown!");
+            } catch (TestException expected) {
+                // expected
+            }
 
             assertTrue(run.isDisposed());
 
diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java b/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java
index 0e6248cc89..582a419b13 100644
--- a/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java
+++ b/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java
@@ -17,14 +17,16 @@
 
 import java.util.HashMap;
 import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
 
-import io.reactivex.rxjava3.disposables.Disposable;
 import org.junit.Test;
 
 import io.reactivex.rxjava3.core.*;
 import io.reactivex.rxjava3.core.Scheduler.Worker;
+import io.reactivex.rxjava3.disposables.Disposable;
 import io.reactivex.rxjava3.functions.*;
 import io.reactivex.rxjava3.internal.schedulers.ComputationScheduler;
+import io.reactivex.rxjava3.plugins.RxJavaPlugins;
 
 public class ComputationSchedulerTests extends AbstractSchedulerConcurrencyTests {
 
@@ -192,4 +194,127 @@ public void run() {
 
         assertEquals(0, calls[0]);
     }
+
+    @Test
+    public void exceptionFromObservableShouldNotBeSwallowed() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+
+        // #3 thread's uncaught exception handler
+        Scheduler computationScheduler = new ComputationScheduler(new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                Thread t = new Thread(r);
+                t.setUncaughtExceptionHandler((thread, throwable) -> {
+                    latch.countDown();
+                });
+                return t;
+            }
+        });
+
+        // #2 RxJava exception handler
+        RxJavaPlugins.setErrorHandler(h -> {
+            latch.countDown();
+        });
+
+        // Exceptions, fatal or not, should be handled by
+        // #1 observer's onError(), or
+        // #2 RxJava exception handler, or
+        // #3 thread's uncaught exception handler,
+        // and should not be swallowed.
+        try {
+
+            // #1 observer's onError()
+            Observable.create(s -> {
+
+                s.onNext(1);
+                throw new OutOfMemoryError();
+            })
+            .subscribeOn(computationScheduler)
+            .subscribe(v -> { },
+                e -> { latch.countDown(); }
+            );
+
+            assertTrue(latch.await(2, TimeUnit.SECONDS));
+        } finally {
+            RxJavaPlugins.reset();
+            computationScheduler.shutdown();
+        }
+    }
+
+    @Test
+    public void exceptionFromObserverShouldNotBeSwallowed() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+
+        // #3 thread's uncaught exception handler
+        Scheduler computationScheduler = new ComputationScheduler(new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                Thread t = new Thread(r);
+                t.setUncaughtExceptionHandler((thread, throwable) -> {
+                    latch.countDown();
+                });
+                return t;
+            }
+        });
+
+        // #2 RxJava exception handler
+        RxJavaPlugins.setErrorHandler(h -> {
+            latch.countDown();
+        });
+
+        // Exceptions, fatal or not, should be handled by
+        // #1 observer's onError(), or
+        // #2 RxJava exception handler, or
+        // #3 thread's uncaught exception handler,
+        // and should not be swallowed.
+        try {
+
+            // #1 observer's onError()
+            Flowable.interval(500, TimeUnit.MILLISECONDS, computationScheduler)
+                    .subscribe(v -> {
+                        throw new OutOfMemoryError();
+                    }, e -> {
+                        latch.countDown();
+                    });
+
+            assertTrue(latch.await(2, TimeUnit.SECONDS));
+        } finally {
+            RxJavaPlugins.reset();
+            computationScheduler.shutdown();
+        }
+    }
+
+    @Test
+    public void periodicTaskShouldStopOnError() throws Exception {
+        AtomicInteger repeatCount = new AtomicInteger();
+
+        Schedulers.computation().schedulePeriodicallyDirect(new Runnable() {
+            @Override
+            public void run() {
+                repeatCount.incrementAndGet();
+                throw new OutOfMemoryError();
+            }
+        }, 0, 1, TimeUnit.MILLISECONDS);
+
+        Thread.sleep(200);
+
+        assertEquals(1, repeatCount.get());
+    }
+
+    @Test
+    public void periodicTaskShouldStopOnError2() throws Exception {
+        AtomicInteger repeatCount = new AtomicInteger();
+
+        Schedulers.computation().schedulePeriodicallyDirect(new Runnable() {
+            @Override
+            public void run() {
+                repeatCount.incrementAndGet();
+                throw new OutOfMemoryError();
+            }
+        }, 0, 1, TimeUnit.NANOSECONDS);
+
+        Thread.sleep(200);
+
+        assertEquals(1, repeatCount.get());
+    }
 }
diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTest.java
index fa97b1c0e7..d55578d663 100644
--- a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTest.java
+++ b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTest.java
@@ -61,18 +61,28 @@ public void run() {
         assertEquals(2, count[0]);
     }
 
-    @Test(expected = TestException.class)
-    public void periodicDirectThrows() {
-        TestScheduler scheduler = new TestScheduler();
+    @Test
+    public void periodicDirectThrows() throws Throwable {
+        TestHelper.withErrorTracking(errors -> {
+            TestScheduler scheduler = new TestScheduler();
 
-        scheduler.schedulePeriodicallyDirect(new Runnable() {
-            @Override
-            public void run() {
-                throw new TestException();
+            try {
+                scheduler.schedulePeriodicallyDirect(new Runnable() {
+                    @Override
+                    public void run() {
+                        throw new TestException();
+                    }
+                }, 100, 100, TimeUnit.MILLISECONDS);
+
+                scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS);
+
+                fail("Should have thrown!");
+            } catch (TestException expected) {
+                // expected
             }
-        }, 100, 100, TimeUnit.MILLISECONDS);
 
-        scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS);
+            TestHelper.assertUndeliverable(errors, 0, TestException.class);
+        });
     }
 
     @Test
@@ -233,7 +243,7 @@ public void run() {
 
             Thread.sleep(250);
 
-            assertEquals(1, list.size());
+            assertTrue(list.size() >= 1);
             TestHelper.assertUndeliverable(list, 0, TestException.class, null);
 
         } finally {

From 77c2ef1605302e9405467558d6b0e97b4e9d46c7 Mon Sep 17 00:00:00 2001
From: David Karnok 
Date: Mon, 27 Apr 2020 10:46:40 +0200
Subject: [PATCH 026/521] 3.x: Allow setting the drift tolerance timeunit via
 system property (#6969)

---
 .../io/reactivex/rxjava3/core/Scheduler.java  | 43 +++++++++++++++----
 .../reactivex/rxjava3/core/SchedulerTest.java | 37 ++++++++++++++++
 2 files changed, 71 insertions(+), 9 deletions(-)
 create mode 100644 src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java

diff --git a/src/main/java/io/reactivex/rxjava3/core/Scheduler.java b/src/main/java/io/reactivex/rxjava3/core/Scheduler.java
index 069c3d5bfb..bd121c06c4 100644
--- a/src/main/java/io/reactivex/rxjava3/core/Scheduler.java
+++ b/src/main/java/io/reactivex/rxjava3/core/Scheduler.java
@@ -73,8 +73,8 @@
  * based on the relative time between it and {@link Worker#now(TimeUnit)}. However, drifts or changes in the
  * system clock could affect this calculation either by scheduling subsequent runs too frequently or too far apart.
  * Therefore, the default implementation uses the {@link #clockDriftTolerance()} value (set via
- * {@code rx3.scheduler.drift-tolerance} in minutes) to detect a drift in {@link Worker#now(TimeUnit)} and
- * re-adjust the absolute/relative time calculation accordingly.
+ * {@code rx3.scheduler.drift-tolerance} and {@code rx3.scheduler.drift-tolerance-unit}) to detect a
+ * drift in {@link Worker#now(TimeUnit)} and re-adjust the absolute/relative time calculation accordingly.
  * 

* The default implementations of {@link #start()} and {@link #shutdown()} do nothing and should be overridden if the * underlying task-execution scheme supports stopping and restarting itself. @@ -91,17 +91,42 @@ public abstract class Scheduler { /** * The tolerance for a clock drift in nanoseconds where the periodic scheduler will rebase. *

- * The associated system parameter, {@code rx3.scheduler.drift-tolerance}, expects its value in minutes. + * Associated system parameters: + *

    + *
  • {@code rx3.scheduler.drift-tolerance}, long, default {@code 15}
  • + *
  • {@code rx3.scheduler.drift-tolerance-unit}, string, default {@code minutes}, + * supports {@code seconds} and {@code milliseconds}. + *
*/ - static final long CLOCK_DRIFT_TOLERANCE_NANOSECONDS; - static { - CLOCK_DRIFT_TOLERANCE_NANOSECONDS = TimeUnit.MINUTES.toNanos( - Long.getLong("rx3.scheduler.drift-tolerance", 15)); + static final long CLOCK_DRIFT_TOLERANCE_NANOSECONDS = + computeClockDrift( + Long.getLong("rx3.scheduler.drift-tolerance", 15), + System.getProperty("rx3.scheduler.drift-tolerance-unit", "minutes") + ); + + /** + * Returns the clock drift tolerance in nanoseconds based on the input selection. + * @param time the time value + * @param timeUnit the time unit string + * @return the time amount in nanoseconds + */ + static long computeClockDrift(long time, String timeUnit) { + if ("seconds".equalsIgnoreCase(timeUnit)) { + return TimeUnit.SECONDS.toNanos(time); + } else if ("milliseconds".equalsIgnoreCase(timeUnit)) { + return TimeUnit.MILLISECONDS.toNanos(time); + } + return TimeUnit.MINUTES.toNanos(time); } /** * Returns the clock drift tolerance in nanoseconds. - *

Related system property: {@code rx3.scheduler.drift-tolerance} in minutes. + *

Related system properties: + *

    + *
  • {@code rx3.scheduler.drift-tolerance}, long, default {@code 15}
  • + *
  • {@code rx3.scheduler.drift-tolerance-unit}, string, default {@code minutes}, + * supports {@code seconds} and {@code milliseconds}. + *
* @return the tolerance in nanoseconds * @since 2.0 */ @@ -350,7 +375,7 @@ public S when(@NonNull Function * If the {@code Worker} is disposed, the {@code schedule} methods diff --git a/src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java b/src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java new file mode 100644 index 0000000000..a8e661fe75 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivex.rxjava3.core; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class SchedulerTest { + + @Test + public void clockDriftCalculation() { + assertEquals(100_000_000L, Scheduler.computeClockDrift(100, "milliseconds")); + + assertEquals(2_000_000_000L, Scheduler.computeClockDrift(2, "seconds")); + + assertEquals(180_000_000_000L, Scheduler.computeClockDrift(3, "minutes")); + + assertEquals(240_000_000_000L, Scheduler.computeClockDrift(4, "random")); + + assertEquals(300_000_000_000L, Scheduler.computeClockDrift(5, null)); + } + +} From eb58c59160ce5f3749396c7002d4c8fe162663a6 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Sun, 10 May 2020 10:52:29 +0200 Subject: [PATCH 027/521] 3.x: Fix Flowable.groupBy eviction logic double decrement and hang (#6975) --- .../operators/flowable/FlowableGroupBy.java | 7 +-- .../flowable/FlowableGroupByTest.java | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java index 9a42e927bc..9e204f1741 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java @@ -270,9 +270,10 @@ private void completeEvictions() { public void cancel(K key) { Object mapKey = key != null ? key : NULL_KEY; - groups.remove(mapKey); - if (groupCount.decrementAndGet() == 0) { - upstream.cancel(); + if (groups.remove(mapKey) != null) { + if (groupCount.decrementAndGet() == 0) { + upstream.cancel(); + } } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java index 21b7f8fb24..781a310697 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java @@ -2558,4 +2558,50 @@ public void subscribeAbandonRace() throws Throwable { ts.assertValueCount(1); } } + + @Test + public void issue6974() { + + FlowableTransformer operation = + source -> source.publish(shared -> + shared + .firstElement() + .flatMapPublisher(firstElement -> + Flowable.just(firstElement).concatWith(shared) + ) + ); + + issue6974Run(20, 500_000, 20 - 1, 20 * 2, operation, false); + + issue6974Run(20, 500_000, 20, 20 * 2, operation, false); + } + + static void issue6974Run(int groups, int iterations, int sizeCap, int flatMapConcurrency, + FlowableTransformer operation, boolean notifyOnExplicitRevoke) { + TestSubscriber test = Flowable + .range(1, groups) + .repeat(iterations / groups) + .groupBy(i -> i, i -> i, false, 128, sizeCap(sizeCap, notifyOnExplicitRevoke)) + .flatMap(gf -> gf.compose(operation), flatMapConcurrency) + .test(); + test.awaitDone(5, TimeUnit.SECONDS); + test.assertValueCount(iterations); + } + + static Function, Map> sizeCap(int maxCapacity, boolean notifyOnExplicit) { + return itemEvictConsumer -> + CacheBuilder + .newBuilder() + .maximumSize(maxCapacity) + .removalListener(notification -> { + if (notification.getCause() != RemovalCause.EXPLICIT || notifyOnExplicit) { + try { + itemEvictConsumer.accept(notification.getValue()); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + }) + .build().asMap(); + } } From 5c26064196c41437c7410b1687ace4386d19856a Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 13 May 2020 09:52:23 +0200 Subject: [PATCH 028/521] 3.x: Fix Flowable.groupBy cancellation/cleanup/eviction race hangs (#6979) --- .../operators/flowable/FlowableGroupBy.java | 102 +++++++++++------- .../flowable/FlowableGroupByTest.java | 89 +++++++++++++++ 2 files changed, 155 insertions(+), 36 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java index 9e204f1741..7ad3d1eed8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java @@ -259,8 +259,9 @@ private void completeEvictions() { int count = 0; GroupedUnicast evictedGroup; while ((evictedGroup = evictedGroups.poll()) != null) { - evictedGroup.onComplete(); - count++; + if (evictedGroup.state.tryComplete()) { + count++; + } } if (count != 0) { groupCount.addAndGet(-count); @@ -383,6 +384,8 @@ static final class State extends BasicIntQueueSubscription implements P static final int ABANDONED = 2; static final int ABANDONED_HAS_SUBSCRIBER = ABANDONED | HAS_SUBSCRIBER; + final AtomicBoolean evictOnce = new AtomicBoolean(); + State(int bufferSize, GroupBySubscriber parent, K key, boolean delayError) { this.queue = new SpscLinkedArrayQueue<>(bufferSize); this.parent = parent; @@ -444,9 +447,18 @@ public void onComplete() { drain(); } + boolean tryComplete() { + boolean canEvict = evictOnce.compareAndSet(false, true); + done = true; + drain(); + return canEvict; + } + void cancelParent() { if ((once.get() & ABANDONED) == 0) { - parent.cancel(key); + if (evictOnce.compareAndSet(false, true)) { + parent.cancel(key); + } } } @@ -518,37 +530,44 @@ void drainNormal() { final SpscLinkedArrayQueue q = queue; final boolean delayError = this.delayError; Subscriber a = actual.get(); + final AtomicBoolean cancelled = this.cancelled; + + outer: for (;;) { - if (a != null) { - long r = requested.get(); - long e = 0; + if (cancelled.get()) { + cleanupQueue(0, false); + } else { + if (a != null) { + long r = requested.get(); + long e = 0; - while (e != r) { - boolean d = done; - T v = q.poll(); - boolean empty = v == null; + while (e != r) { + boolean d = done; + T v = q.poll(); + boolean empty = v == null; - if (checkTerminated(d, empty, a, delayError, e)) { - return; - } + if (checkTerminated(d, empty, a, delayError, e, !empty)) { + continue outer; + } - if (empty) { - break; - } + if (empty) { + break; + } - a.onNext(v); + a.onNext(v); - e++; - } + e++; + } - if (e == r && checkTerminated(done, q.isEmpty(), a, delayError, e)) { - return; - } + if (e == r && checkTerminated(done, q.isEmpty(), a, delayError, e, false)) { + continue outer; + } - if (e != 0L) { - BackpressureHelper.produced(requested, e); - // replenish based on this batch run - requestParent(e); + if (e != 0L) { + BackpressureHelper.produced(requested, e); + // replenish based on this batch run + requestParent(e); + } } } @@ -568,23 +587,32 @@ void requestParent(long e) { } } - boolean checkTerminated(boolean d, boolean empty, Subscriber a, boolean delayError, long emitted) { + void cleanupQueue(long emitted, boolean polled) { + // if this group is canceled, all accumulated emissions and + // remaining items in the queue should be requested + // so that other groups can proceed + while (queue.poll() != null) { + emitted++; + } + if (polled) { + emitted++; + } + if (emitted != 0L) { + requestParent(emitted); + } + } + + boolean checkTerminated(boolean d, boolean empty, Subscriber a, + boolean delayError, long emitted, boolean polled) { if (cancelled.get()) { - // if this group is canceled, all accumulated emissions and - // remaining items in the queue should be requested - // so that other groups can proceed - while (queue.poll() != null) { - emitted++; - } - if (emitted != 0L) { - requestParent(emitted); - } + cleanupQueue(emitted, polled); return true; } if (d) { if (delayError) { if (empty) { + cancelled.lazySet(true); Throwable e = error; if (e != null) { a.onError(e); @@ -597,10 +625,12 @@ boolean checkTerminated(boolean d, boolean empty, Subscriber a, boole Throwable e = error; if (e != null) { queue.clear(); + cancelled.lazySet(true); a.onError(e); return true; } else if (empty) { + cancelled.lazySet(true); a.onComplete(); return true; } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java index 781a310697..3b85d583b9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java @@ -2604,4 +2604,93 @@ static Function, Map> sizeCap(int maxCapacity, b }) .build().asMap(); } + + static void issue6974RunPart2(int groupByBufferSize, int flatMapMaxConcurrency, int groups, + boolean notifyOnExplicitEviction) { + TestSubscriber ts = Flowable + .range(1, 500_000) + .map(i -> i % groups) + .groupBy(i -> i, i -> i, false, groupByBufferSize, + // set cap too high + sizeCap(groups * 100, notifyOnExplicitEviction)) + .flatMap(gf -> gf + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .test(); + + ts + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void issue6974Part2Case1() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + issue6974RunPart2(groupByBufferSize, flatMapMaxConcurrency, groups, notifyOnExplicitEviction); + } + + @Test + public void issue6974Part2Case2() { + final int groups = 20; + + // Timeout... explicit eviction notification makes difference + int groupByBufferSize = groups * 30; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = true; + issue6974RunPart2(groupByBufferSize, flatMapMaxConcurrency, groups, notifyOnExplicitEviction); + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6974Part2Case2Loop() { + for (int i = 0; i < 1000; i++) { + issue6974Part2Case2(); + } + } + */ + + static void issue6974RunPart2NoEvict(int groupByBufferSize, int flatMapMaxConcurrency, int groups, + boolean notifyOnExplicitEviction) { + TestSubscriber ts = Flowable + .range(1, 500_000) + .map(i -> i % groups) + .groupBy(i -> i) + .flatMap(gf -> gf + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .test(); + + ts + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void issue6974Part2Case1NoEvict() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + issue6974RunPart2NoEvict(groupByBufferSize, flatMapMaxConcurrency, groups, notifyOnExplicitEviction); + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6974Part2Case1NoEvictLoop() { + for (int i = 0; i < 1000; i++) { + issue6974Part2Case1NoEvict(); + } + } + */ } From 125bd057b1ce20e2c848cdbc5a2f7c42585fb827 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 15 May 2020 12:33:27 +0200 Subject: [PATCH 029/521] 3.x: Disable fusion on the groups of Flowable.groupBy (#6983) --- .../operators/flowable/FlowableGroupBy.java | 3 + .../flowable/FlowableGroupByTest.java | 187 +++++++++++++++++- 2 files changed, 188 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java index 7ad3d1eed8..8dc325fbde 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java @@ -642,10 +642,13 @@ boolean checkTerminated(boolean d, boolean empty, Subscriber a, @Override public int requestFusion(int mode) { + // FIXME fusion mode causes hangs + /* if ((mode & ASYNC) != 0) { outputFused = true; return ASYNC; } + */ return NONE; } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java index 3b85d583b9..e606d821f9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java @@ -22,7 +22,7 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.*; -import org.junit.Test; +import org.junit.*; import org.mockito.Mockito; import org.reactivestreams.*; @@ -1671,7 +1671,9 @@ public void accept(GroupedFlowable g) { .subscribe(ts2); ts1 - .assertFusionMode(QueueFuseable.ASYNC) + // FIXME fusion mode causes hangs + //.assertFusionMode(QueueFuseable.ASYNC) + .assertFusionMode(QueueFuseable.NONE) .assertValues(2, 3, 4, 5, 6, 7, 8, 9, 10, 11) .assertNoErrors() .assertComplete(); @@ -2693,4 +2695,185 @@ public void issue6974Part2Case1NoEvictLoop() { } } */ + + @Test + public void issue6974Part2Case1ObserveOn() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnCancel(() -> { + System.out.println("Cancelling upstream"); + }) + .groupBy(i -> i, i -> i, false, groupByBufferSize, + sizeCap(groups * 2, notifyOnExplicitEviction)) + .flatMap(gf -> gf + .observeOn(Schedulers.computation()) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void issue6974Part2Case1ObserveOnHide() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnCancel(() -> System.out.println("Cancelling upstream")) + .groupBy(i -> i, i -> i, false, groupByBufferSize, + sizeCap(groups * 2, notifyOnExplicitEviction)) + .flatMap(gf -> gf + .hide() + .observeOn(Schedulers.computation()) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void issue6974Part2Case1ObserveOnNoCap() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int flatMapMaxConcurrency = 1_000_000; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnRequest(v -> { + System.out.println("Source: " + v); + }) + .groupBy(i -> i) + .flatMap(gf -> gf + .observeOn(Schedulers.computation()) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void issue6974Part2Case1ObserveOnNoCapHide() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int flatMapMaxConcurrency = 1_000_000; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnRequest(v -> { + System.out.println("Source: " + v); + }) + .groupBy(i -> i) + .flatMap(gf -> gf + .hide() + .observeOn(Schedulers.computation()) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6974Part2Case1ObserveOnNoCapHideLoop() { + for (int i = 0; i < 100; i++) { + issue6974Part2Case1ObserveOnNoCapHide(); + } + } + */ + + @Test + public void issue6974Part2Case1ObserveOnConditional() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnCancel(() -> System.out.println("Cancelling upstream")) + .groupBy(i -> i, i -> i, false, groupByBufferSize, + sizeCap(groups * 2, notifyOnExplicitEviction)) + .flatMap(gf -> gf + .observeOn(Schedulers.computation()) + .filter(v -> true) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void issue6974Part2Case1ObserveOnConditionalHide() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnCancel(() -> System.out.println("Cancelling upstream")) + .groupBy(i -> i, i -> i, false, groupByBufferSize, + sizeCap(groups * 2, notifyOnExplicitEviction)) + .flatMap(gf -> gf + .hide() + .observeOn(Schedulers.computation()) + .filter(v -> true) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6974Part2Case1ObserveOnHideLoop() { + for (int i = 0; i < 100; i++) { + issue6974Part2Case1ObserveOnHide(); + } + } + */ } From ca222c2909bd8e1958c1241df4412bea1fbc543d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brais=20Gab=C3=ADn?= Date: Tue, 19 May 2020 21:28:57 +0200 Subject: [PATCH 030/521] Add missing `@Test` annotation (#6990) --- .../java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java index eb06a1a0ea..106f85bfc5 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java @@ -797,6 +797,7 @@ public void getValuesUnbounded() { } + @Test public void createInvalidCapacity() { try { ReplaySubject.create(-99); From e2b1d2fc6c2ff7ad5551f6d5a14aafb37c1bb5bc Mon Sep 17 00:00:00 2001 From: Derar Bakr Date: Wed, 20 May 2020 21:55:39 +0300 Subject: [PATCH 031/521] Removed unnecessary upstream.cancel() call for casually finished upstream sequences. (#6992) * no upstream.cancel() in FlowablePublishMulticast when the sequence is finished normally via onComplete/onError from upstream; minor code cleanup - unnecessary Disposable implementation to avoid method name clash * cleanup in FlowablePublishFunctionTest: refactored anonymous classes to lambdas * reduced visibility for dispose() and isDisposed() in the inner MulticastProcessor Co-authored-by: derar --- .../flowable/FlowablePublishMulticast.java | 21 +- .../flowable/FlowablePublishFunctionTest.java | 254 ++++++------------ 2 files changed, 93 insertions(+), 182 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticast.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticast.java index 05e0e7a907..26dbaa7f4b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticast.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticast.java @@ -22,7 +22,6 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.fuseable.*; @@ -124,7 +123,7 @@ public void cancel() { } } - static final class MulticastProcessor extends Flowable implements FlowableSubscriber, Disposable { + static final class MulticastProcessor extends Flowable implements FlowableSubscriber { @SuppressWarnings("rawtypes") static final MulticastSubscription[] EMPTY = new MulticastSubscription[0]; @@ -192,19 +191,19 @@ public void onSubscribe(Subscription s) { } } - @Override - public void dispose() { - SubscriptionHelper.cancel(upstream); - if (wip.getAndIncrement() == 0) { - SimpleQueue q = queue; - if (q != null) { - q.clear(); + void dispose() { + if (!done) { + SubscriptionHelper.cancel(upstream); + if (wip.getAndIncrement() == 0) { + SimpleQueue q = queue; + if (q != null) { + q.clear(); + } } } } - @Override - public boolean isDisposed() { + boolean isDisposed() { return upstream.get() == SubscriptionHelper.CANCELLED; } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishFunctionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishFunctionTest.java index 69ed3a4aed..62998eb543 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishFunctionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishFunctionTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.*; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.junit.*; @@ -26,7 +27,6 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.processors.PublishProcessor; @@ -39,12 +39,9 @@ public class FlowablePublishFunctionTest extends RxJavaTest { public void concatTakeFirstLastCompletes() { TestSubscriber ts = new TestSubscriber<>(); - Flowable.range(1, 3).publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return Flowable.concat(f.take(5), f.takeLast(5)); - } - }).subscribe(ts); + Flowable.range(1, 3) + .publish(f -> Flowable.concat(f.take(5), f.takeLast(5))) + .subscribe(ts); ts.assertValues(1, 2, 3); ts.assertNoErrors(); @@ -55,12 +52,9 @@ public Flowable apply(Flowable f) { public void concatTakeFirstLastBackpressureCompletes() { TestSubscriber ts = TestSubscriber.create(0L); - Flowable.range(1, 6).publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return Flowable.concat(f.take(5), f.takeLast(5)); - } - }).subscribe(ts); + Flowable.range(1, 6) + .publish(f -> Flowable.concat(f.take(5), f.takeLast(5))) + .subscribe(ts); ts.assertNoValues(); ts.assertNoErrors(); @@ -86,12 +80,7 @@ public void canBeCancelled() { PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return Flowable.concat(f.take(5), f.takeLast(5)); - } - }).subscribe(ts); + pp.publish(f -> Flowable.concat(f.take(5), f.takeLast(5))).subscribe(ts); pp.onNext(1); pp.onNext(2); @@ -108,8 +97,7 @@ public Flowable apply(Flowable f) { @Test public void invalidPrefetch() { try { - Flowable.never().publish( - Functions.>identity(), -99); + Flowable.never().publish(Functions.identity(), -99); fail("Didn't throw IllegalArgumentException"); } catch (IllegalArgumentException ex) { Assert.assertEquals("prefetch > 0 required but it was -99", ex.getMessage()); @@ -122,12 +110,7 @@ public void takeCompletes() { PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f.take(1); - } - }).subscribe(ts); + pp.publish(f -> f.take(1)).subscribe(ts); pp.onNext(1); @@ -153,12 +136,7 @@ public void onStart() { PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f.take(1); - } - }).subscribe(ts); + pp.publish(f -> f.take(1)).subscribe(ts); Assert.assertEquals(1, startCount.get()); } @@ -169,12 +147,7 @@ public void takeCompletesUnsafe() { PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f.take(1); - } - }).subscribe(ts); + pp.publish(f -> f.take(1)).subscribe(ts); pp.onNext(1); @@ -191,12 +164,7 @@ public void directCompletesUnsafe() { PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f; - } - }).subscribe(ts); + pp.publish(Functions.identity()).subscribe(ts); pp.onNext(1); pp.onComplete(); @@ -214,12 +182,7 @@ public void overflowMissingBackpressureException() { PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f; - } - }).subscribe(ts); + pp.publish(Functions.identity()).subscribe(ts); for (int i = 0; i < Flowable.bufferSize() * 2; i++) { pp.onNext(i); @@ -240,12 +203,7 @@ public void overflowMissingBackpressureExceptionDelayed() { PublishProcessor pp = PublishProcessor.create(); - new FlowablePublishMulticast<>(pp, new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f; - } - }, Flowable.bufferSize(), true).subscribe(ts); + new FlowablePublishMulticast<>(pp, Functions.identity(), Flowable.bufferSize(), true).subscribe(ts); for (int i = 0; i < Flowable.bufferSize() * 2; i++) { pp.onNext(i); @@ -264,22 +222,16 @@ public Flowable apply(Flowable f) { @Test public void emptyIdentityMapped() { Flowable.empty() - .publish(Functions.>identity()) + .publish(Functions.identity()) .test() - .assertResult() - ; + .assertResult(); } @Test public void independentlyMapped() { PublishProcessor pp = PublishProcessor.create(); - TestSubscriber ts = pp.publish(new Function, Publisher>() { - @Override - public Publisher apply(Flowable v) throws Exception { - return Flowable.range(1, 5); - } - }).test(0); + TestSubscriber ts = pp.publish(v -> Flowable.range(1, 5)).test(0); assertTrue("pp has no Subscribers?!", pp.hasSubscribers()); @@ -296,12 +248,7 @@ public Publisher apply(Flowable v) throws Exception { @Test public void badSource() { - TestHelper.checkBadSourceFlowable(new Function, Object>() { - @Override - public Object apply(Flowable f) throws Exception { - return f.publish(Functions.>identity()); - } - }, false, 1, 1, 1); + TestHelper.checkBadSourceFlowable(f -> f.publish(Functions.identity()), false, 1, 1, 1); } @Test @@ -315,7 +262,7 @@ protected void subscribeActual(Subscriber s) { } } } - .publish(Functions.>identity(), 8) + .publish(Functions.identity(), 8) .test(0) .assertFailure(MissingBackpressureException.class); } @@ -323,12 +270,7 @@ protected void subscribeActual(Subscriber s) { @Test public void errorResubscribe() { Flowable.error(new TestException()) - .publish(new Function, Publisher>() { - @Override - public Publisher apply(Flowable f) throws Exception { - return f.onErrorResumeWith(f); - } - }) + .publish(f -> f.onErrorResumeWith(f)) .test() .assertFailure(TestException.class); } @@ -336,21 +278,18 @@ public Publisher apply(Flowable f) throws Exception { @Test public void fusedInputCrash() { Flowable.just(1) - .map(new Function() { - @Override - public Integer apply(Integer v) throws Exception { - throw new TestException(); - } + .map(v -> { + throw new TestException(); }) - .publish(Functions.>identity()) + .publish(Functions.identity()) .test() .assertFailure(TestException.class); } @Test public void error() { - new FlowablePublishMulticast<>(Flowable.just(1).concatWith(Flowable.error(new TestException())), - Functions.>identity(), 16, true) + new FlowablePublishMulticast<>(Flowable.just(1).concatWith(Flowable.error(new TestException())), + Functions.identity(), 16, true) .test() .assertFailure(TestException.class, 1); } @@ -358,7 +297,7 @@ public void error() { @Test public void backpressuredEmpty() { Flowable.empty() - .publish(Functions.>identity()) + .publish(Functions.identity()) .test(0L) .assertResult(); } @@ -366,7 +305,7 @@ public void backpressuredEmpty() { @Test public void oneByOne() { Flowable.range(1, 10) - .publish(Functions.>identity()) + .publish(Functions.identity()) .rebatchRequests(1) .test() .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); @@ -387,7 +326,7 @@ public void onNext(Integer t) { } }; - pp.publish(Functions.>identity()).subscribe(ts); + pp.publish(Functions.identity()).subscribe(ts); pp.onNext(1); @@ -399,12 +338,7 @@ public void onNext(Integer t) { @Test public void inputOutputSubscribeRace() { Flowable source = Flowable.just(1) - .publish(new Function, Publisher>() { - @Override - public Publisher apply(Flowable f) throws Exception { - return f.subscribeOn(Schedulers.single()); - } - }); + .publish(f -> f.subscribeOn(Schedulers.single())); for (int i = 0; i < 500; i++) { source.test() @@ -416,7 +350,7 @@ public Publisher apply(Flowable f) throws Exception { @Test public void inputOutputSubscribeRace2() { Flowable source = Flowable.just(1).subscribeOn(Schedulers.single()) - .publish(Functions.>identity()); + .publish(Functions.identity()); for (int i = 0; i < 500; i++) { source.test() @@ -431,30 +365,19 @@ public void sourceSubscriptionDelayed() { final TestSubscriber ts1 = new TestSubscriber<>(0L); Flowable.just(1) - .publish(new Function, Publisher>() { - @Override - public Publisher apply(final Flowable f) throws Exception { - Runnable r1 = new Runnable() { - @Override - public void run() { - f.subscribe(ts1); - } - }; - - Runnable r2 = new Runnable() { - @Override - public void run() { + .publish(f -> { + Runnable r1 = () -> f.subscribe(ts1); + + Runnable r2 = () -> { for (int j = 0; j < 100; j++) { ts1.request(1); } - } - }; + }; - TestHelper.race(r1, r2); - return f; - } - }).test() - .assertResult(1); + TestHelper.race(r1, r2); + return f; + }).test() + .assertResult(1); ts1.assertResult(1); } @@ -463,24 +386,9 @@ public void run() { @Test public void longFlow() { Flowable.range(1, 1000000) - .publish(new Function, Publisher>() { - @Override - public Publisher apply(Flowable v) throws Exception { - return Flowable.mergeArray( - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 == 0; - } - }), - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 != 0; - } - })); - } - }) + .publish(v -> Flowable.mergeArray( + v.filter(w -> w % 2 == 0), + v.filter(w -> w % 2 != 0))) .takeLast(1) .test() .assertResult(1000000); @@ -489,24 +397,9 @@ public boolean test(Integer w) throws Exception { @Test public void longFlow2() { Flowable.range(1, 100000) - .publish(new Function, Publisher>() { - @Override - public Publisher apply(Flowable v) throws Exception { - return Flowable.mergeArray( - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 == 0; - } - }), - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 != 0; - } - })); - } - }) + .publish(v -> Flowable.mergeArray( + v.filter(w -> w % 2 == 0), + v.filter(w -> w % 2 != 0))) .test() .assertValueCount(100000) .assertNoErrors() @@ -516,26 +409,45 @@ public boolean test(Integer w) throws Exception { @Test public void longFlowHidden() { Flowable.range(1, 1000000).hide() - .publish(new Function, Publisher>() { - @Override - public Publisher apply(Flowable v) throws Exception { - return Flowable.mergeArray( - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 == 0; - } - }), - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 != 0; - } - })); - } - }) + .publish(v -> Flowable.mergeArray( + v.filter(w -> w % 2 == 0), + v.filter(w -> w % 2 != 0))) .takeLast(1) .test() .assertResult(1000000); } + + @Test + public void noUpstreamCancelOnCasualChainClose() { + AtomicBoolean parentUpstreamCancelled = new AtomicBoolean(false); + Flowable.range(1, 10) + .doOnCancel(() -> parentUpstreamCancelled.set(true)) + .publish(Functions.identity()) + .test() + .awaitDone(1, TimeUnit.SECONDS); + assertFalse("Unnecessary upstream .cancel() call in FlowablePublishMulticast", parentUpstreamCancelled.get()); + } + + @Test + public void noUpstreamCancelOnCasualChainCloseWithInnerCancels() { + AtomicBoolean parentUpstreamCancelled = new AtomicBoolean(false); + Flowable.range(1, 10) + .doOnCancel(() -> parentUpstreamCancelled.set(true)) + .publish(v -> Flowable.concat(v.take(1), v.skip(5))) + .test() + .awaitDone(1, TimeUnit.SECONDS); + assertFalse("Unnecessary upstream .cancel() call in FlowablePublishMulticast", parentUpstreamCancelled.get()); + } + + @Test + public void upstreamCancelOnDownstreamCancel() { + AtomicBoolean parentUpstreamCancelled = new AtomicBoolean(false); + Flowable.range(1, 10) + .doOnCancel(() -> parentUpstreamCancelled.set(true)) + .publish(Functions.identity()) + .take(1) + .test() + .awaitDone(1, TimeUnit.SECONDS); + assertTrue("Upstream .cancel() not called in FlowablePublishMulticast", parentUpstreamCancelled.get()); + } } From c693edbb3cd773252185fa5f339a4c7049a3493c Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 20 May 2020 21:34:06 +0200 Subject: [PATCH 032/521] 3.x: Fix Flowable.groupBy eviction-completion-replenishment problems (#6988) --- .../operators/flowable/FlowableGroupBy.java | 22 +++-- .../flowable/FlowableGroupByTest.java | 81 ++++++++++++++++++- 2 files changed, 96 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java index 8dc325fbde..f4df324110 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java @@ -214,9 +214,7 @@ public void onError(Throwable t) { g.onError(t); } groups.clear(); - if (evictedGroups != null) { - evictedGroups.clear(); - } + completeEvictions(); downstream.onError(t); } @@ -226,10 +224,10 @@ public void onComplete() { for (GroupedUnicast g : groups.values()) { g.onComplete(); } + groups.clear(); - if (evictedGroups != null) { - evictedGroups.clear(); - } + completeEvictions(); + done = true; downstream.onComplete(); } @@ -594,6 +592,11 @@ void cleanupQueue(long emitted, boolean polled) { while (queue.poll() != null) { emitted++; } + + replenishParent(emitted, polled); + } + + void replenishParent(long emitted, boolean polled) { if (polled) { emitted++; } @@ -618,6 +621,9 @@ boolean checkTerminated(boolean d, boolean empty, Subscriber a, a.onError(e); } else { a.onComplete(); + // completion doesn't mean the parent has completed + // because of evicted groups + replenishParent(emitted, polled); } return true; } @@ -632,6 +638,10 @@ boolean checkTerminated(boolean d, boolean empty, Subscriber a, if (empty) { cancelled.lazySet(true); a.onComplete(); + + // completion doesn't mean the parent has completed + // because of evicted groups + replenishParent(emitted, polled); return true; } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java index e606d821f9..bfb467f424 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java @@ -18,11 +18,12 @@ import static org.mockito.Mockito.*; import java.io.IOException; +import java.time.Duration; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; -import org.junit.*; +import org.junit.Test; import org.mockito.Mockito; import org.reactivestreams.*; @@ -2876,4 +2877,82 @@ public void issue6974Part2Case1ObserveOnHideLoop() { } } */ + + static Function, ConcurrentMap> ttlCapGuava(Duration ttl) { + return itemEvictConsumer -> + CacheBuilder + .newBuilder() + .expireAfterWrite(ttl) + .removalListener(n -> { + if (n.getCause() != com.google.common.cache.RemovalCause.EXPLICIT) { + try { + itemEvictConsumer.accept(n.getValue()); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + }).build().asMap(); + } + + @Test + public void issue6982Case1() { + final int groups = 20; + + int groupByBufferSize = 2; + int flatMapMaxConcurrency = 200 * groups; + + // ~50% of executions - Not completed (latch = 1, values = 500000, errors = 0, completions = 0, timeout!, + // disposed!) + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .groupBy(i -> i, i -> i, false, groupByBufferSize, ttlCapGuava(Duration.ofMillis(10))) + .flatMap(gf -> gf.observeOn(Schedulers.computation()), flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6982Case1Loop() { + for (int i = 0; i < 200; i++) { + System.out.println("issue6982Case1Loop " + i); + issue6982Case1(); + } + } + */ + + @Test + public void issue6982Case2() { + final int groups = 20; + + int groupByBufferSize = groups * 30; + int flatMapMaxConcurrency = groups * 500; + // Always : Not completed (latch = 1, values = 14100, errors = 0, completions = 0, timeout!, disposed!) + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .groupBy(i -> i, i -> i, false, groupByBufferSize, ttlCapGuava(Duration.ofMillis(10))) + .flatMap(gf -> gf.observeOn(Schedulers.computation()), flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6982Case2Loop() { + for (int i = 0; i < 200; i++) { + System.out.println("issue6982Case2Loop " + i); + issue6982Case2(); + } + } + */ } From 3f386be351eb7c62379d124a2f65894a9961e604 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Thu, 21 May 2020 10:11:47 +0200 Subject: [PATCH 033/521] 3.x: Fix recent groupBy tests sometimes failing with MBE (#6994) --- .../flowable/FlowableGroupByTest.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java index bfb467f424..947a911446 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java @@ -2719,10 +2719,9 @@ public void issue6974Part2Case1ObserveOn() { // .take(10) .take(10, TimeUnit.MILLISECONDS) , flatMapMaxConcurrency) - .test() + .subscribeWith(new TestSubscriberEx<>()) .awaitDone(5, TimeUnit.SECONDS) - .assertNoErrors() - .assertComplete(); + .assertTerminated(); // MBE is possible if the async group closing is slow } @Test @@ -2746,10 +2745,9 @@ public void issue6974Part2Case1ObserveOnHide() { // .take(10) .take(10, TimeUnit.MILLISECONDS) , flatMapMaxConcurrency) - .test() + .subscribeWith(new TestSubscriberEx<>()) .awaitDone(5, TimeUnit.SECONDS) - .assertNoErrors() - .assertComplete(); + .assertTerminated(); // MBE is possible if the async group closing is slow } @Test @@ -2834,10 +2832,9 @@ public void issue6974Part2Case1ObserveOnConditional() { // .take(10) .take(10, TimeUnit.MILLISECONDS) , flatMapMaxConcurrency) - .test() + .subscribeWith(new TestSubscriberEx<>()) .awaitDone(5, TimeUnit.SECONDS) - .assertNoErrors() - .assertComplete(); + .assertTerminated(); // MBE is possible if the async group closing is slow } @Test @@ -2862,10 +2859,9 @@ public void issue6974Part2Case1ObserveOnConditionalHide() { // .take(10) .take(10, TimeUnit.MILLISECONDS) , flatMapMaxConcurrency) - .test() + .subscribeWith(new TestSubscriberEx<>()) .awaitDone(5, TimeUnit.SECONDS) - .assertNoErrors() - .assertComplete(); + .assertTerminated(); // MBE is possible if the async group closing is slow } /* From 597d2493f9b807580f46277f8102bd874833fba7 Mon Sep 17 00:00:00 2001 From: akluball <35855437+akluball@users.noreply.github.com> Date: Sun, 24 May 2020 03:37:31 -0400 Subject: [PATCH 034/521] Suppress UndeliverableException handling in tests (#6987) (#6996) * Suppress UndeliverableException handling in tests (#6987) Use a JUnit TestRule to suppress UndeliverableException handling in test methods annotated with SuppressUndeliverable. * Write requested changes (#6996) * remove changes to RxJavaPlugins * move @Rule field into RxJavaTest * use lambda SuppressUndeliverableRule --- .../io/reactivex/rxjava3/core/RxJavaTest.java | 4 ++ .../internal/jdk8/ParallelCollectorTest.java | 1 + .../CompletableFromCallableTest.java | 4 +- .../CompletableFromSupplierTest.java | 3 +- .../flowable/FlowableGroupByTest.java | 6 ++ .../FlowableWindowWithFlowableTest.java | 1 + ...lowableWindowWithStartEndFlowableTest.java | 3 +- .../flowable/FlowableWindowWithTimeTest.java | 6 +- .../operators/maybe/MaybeUsingTest.java | 1 + .../observable/ObservableCreateTest.java | 7 +++ .../observable/ObservableGroupByTest.java | 5 ++ .../observable/ObservableGroupJoinTest.java | 1 + .../observable/ObservableLiftTest.java | 2 + .../observable/ObservableRefCountTest.java | 4 ++ .../ObservableRetryWithPredicateTest.java | 3 + .../observable/ObservableTakeTest.java | 3 +- .../observable/ObservableTakeWhileTest.java | 1 + .../ObservableTimeoutWithSelectorTest.java | 2 + .../ObservableWindowWithObservableTest.java | 1 + ...vableWindowWithStartEndObservableTest.java | 3 +- .../ObservableWindowWithTimeTest.java | 11 +++- .../operators/single/SingleCreateTest.java | 5 +- .../operators/single/SingleUsingTest.java | 1 + .../ExecutorSchedulerDelayedRunnableTest.java | 2 + .../schedulers/SingleSchedulerTest.java | 3 +- .../TrampolineSchedulerInternalTest.java | 4 +- .../subscribers/BoundedSubscriberTest.java | 3 +- .../subscribers/FutureSubscriberTest.java | 8 ++- .../subscribers/LambdaSubscriberTest.java | 3 +- .../util/HalfSerializerObserverTest.java | 2 + .../util/HalfSerializerSubscriberTest.java | 2 + .../rxjava3/observers/SafeObserverTest.java | 1 + .../observers/SerializedObserverTest.java | 1 + .../rxjava3/parallel/ParallelPeekTest.java | 4 +- .../processors/AsyncProcessorTest.java | 3 + .../processors/PublishProcessorTest.java | 5 +- .../processors/ReplayProcessorTest.java | 4 ++ .../schedulers/CachedThreadSchedulerTest.java | 2 + .../schedulers/ComputationSchedulerTests.java | 4 ++ .../schedulers/NewThreadSchedulerTest.java | 3 + .../rxjava3/subjects/AsyncSubjectTest.java | 3 + .../rxjava3/subjects/PublishSubjectTest.java | 5 +- .../rxjava3/subjects/ReplaySubjectTest.java | 4 ++ .../subscribers/SafeSubscriberTest.java | 2 + .../subscribers/SerializedSubscriberTest.java | 1 + .../testsupport/SuppressUndeliverable.java | 23 ++++++++ .../SuppressUndeliverableRule.java | 57 +++++++++++++++++++ 47 files changed, 210 insertions(+), 17 deletions(-) create mode 100644 src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverable.java create mode 100644 src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java diff --git a/src/test/java/io/reactivex/rxjava3/core/RxJavaTest.java b/src/test/java/io/reactivex/rxjava3/core/RxJavaTest.java index d903a87f0c..8eaf638916 100644 --- a/src/test/java/io/reactivex/rxjava3/core/RxJavaTest.java +++ b/src/test/java/io/reactivex/rxjava3/core/RxJavaTest.java @@ -20,9 +20,13 @@ import org.junit.*; import org.junit.rules.Timeout; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverableRule; + public abstract class RxJavaTest { @Rule public Timeout globalTimeout = new Timeout(5, TimeUnit.MINUTES); + @Rule + public final SuppressUndeliverableRule suppressUndeliverableRule = new SuppressUndeliverableRule(); /** * Announce creates a log print preventing Travis CI from killing the build. diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelCollectorTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelCollectorTest.java index 8e558a0048..42f7748a03 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelCollectorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelCollectorTest.java @@ -148,6 +148,7 @@ public Set characteristics() { } @Test + @SuppressUndeliverable public void collectorCombinerCrash() { Flowable.range(1, 5) .parallel() diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallableTest.java index 7bd5011ca7..57c11b8a7b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallableTest.java @@ -29,9 +29,10 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class CompletableFromCallableTest extends RxJavaTest { + @Test public void fromCallable() { final AtomicInteger atomicInteger = new AtomicInteger(); @@ -161,6 +162,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { } @Test + @SuppressUndeliverable public void fromActionErrorsDisposed() { final AtomicInteger calls = new AtomicInteger(); Completable.fromCallable(new Callable() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplierTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplierTest.java index bcad666785..5aca1f7847 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplierTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplierTest.java @@ -30,7 +30,7 @@ import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class CompletableFromSupplierTest extends RxJavaTest { @@ -163,6 +163,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { } @Test + @SuppressUndeliverable public void fromActionErrorsDisposed() { final AtomicInteger calls = new AtomicInteger(); Completable.fromSupplier(new Supplier() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java index 947a911446..c86e9f260d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java @@ -112,6 +112,7 @@ public void empty() { } @Test + @SuppressUndeliverable public void error() { Flowable sourceStrings = Flowable.just("one", "two", "three", "four", "five", "six"); Flowable errorSource = Flowable.error(new TestException("forced failure")); @@ -1196,6 +1197,7 @@ public void keySelectorThrows() { } @Test + @SuppressUndeliverable public void valueSelectorThrows() { Flowable source = Flowable.just(0, 1, 2, 3, 4, 5, 6); @@ -1251,6 +1253,7 @@ public void accept(GroupedFlowable t1) { } @Test + @SuppressUndeliverable public void error2() { Flowable source = Flowable.concat(Flowable.just(0), Flowable. error(new TestException("Forced failure"))); @@ -1686,6 +1689,7 @@ public void accept(GroupedFlowable g) { } @Test + @SuppressUndeliverable public void keySelectorAndDelayError() { Flowable.just(1).concatWith(Flowable.error(new TestException())) .groupBy(Functions.identity(), true) @@ -1700,6 +1704,7 @@ public Flowable apply(GroupedFlowable g) throws Excep } @Test + @SuppressUndeliverable public void keyAndValueSelectorAndDelayError() { Flowable.just(1).concatWith(Flowable.error(new TestException())) .groupBy(Functions.identity(), Functions.identity(), true) @@ -1853,6 +1858,7 @@ public Publisher apply(GroupedFlowable g) throws Excep } @Test + @SuppressUndeliverable public void groupError() { Flowable.just(1).concatWith(Flowable.error(new TestException())) .groupBy(Functions.justFunction(1), true) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithFlowableTest.java index 66273656f9..0d9cd5dd1b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithFlowableTest.java @@ -623,6 +623,7 @@ public void run() { } @Test + @SuppressUndeliverable public void disposeMainBoundaryErrorRace() { final TestException ex = new TestException(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java index a9a5a7e821..e6ffaed5bd 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java @@ -32,7 +32,7 @@ import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.TestScheduler; import io.reactivex.rxjava3.subscribers.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class FlowableWindowWithStartEndFlowableTest extends RxJavaTest { @@ -324,6 +324,7 @@ public Flowable apply(Integer v) throws Exception { } @Test + @SuppressUndeliverable public void endError() { PublishProcessor source = PublishProcessor.create(); PublishProcessor start = PublishProcessor.create(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java index bf6a271b84..190d10f0af 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java @@ -31,7 +31,7 @@ import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subscribers.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class FlowableWindowWithTimeTest extends RxJavaTest { @@ -556,6 +556,7 @@ public void restartTimer() { } @Test + @SuppressUndeliverable public void exactBoundaryError() { Flowable.error(new TestException()) .window(1, TimeUnit.DAYS, Schedulers.single(), 2, true) @@ -973,6 +974,7 @@ public void accept(Flowable v) throws Exception { } @Test + @SuppressUndeliverable public void exactTimeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); @@ -1053,6 +1055,7 @@ public void accept(Flowable v) throws Exception { } @Test + @SuppressUndeliverable public void exactTimeAndSizeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); @@ -1133,6 +1136,7 @@ public void accept(Flowable v) throws Exception { } @Test + @SuppressUndeliverable public void skipTimeAndSizeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsingTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsingTest.java index c9ff65e09a..e9621a2f64 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsingTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsingTest.java @@ -481,6 +481,7 @@ public void run() { } @Test + @SuppressUndeliverable public void errorDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java index 9f25879a60..2998f0a5a8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java @@ -33,6 +33,7 @@ public class ObservableCreateTest extends RxJavaTest { @Test + @SuppressUndeliverable public void basic() { final Disposable d = Disposable.empty(); @@ -58,6 +59,7 @@ public void subscribe(ObservableEmitter e) throws Exception { } @Test + @SuppressUndeliverable public void basicWithCancellable() { final Disposable d1 = Disposable.empty(); final Disposable d2 = Disposable.empty(); @@ -91,6 +93,7 @@ public void cancel() throws Exception { } @Test + @SuppressUndeliverable public void basicWithError() { final Disposable d = Disposable.empty(); @@ -115,6 +118,7 @@ public void subscribe(ObservableEmitter e) throws Exception { } @Test + @SuppressUndeliverable public void basicSerialized() { final Disposable d = Disposable.empty(); @@ -142,6 +146,7 @@ public void subscribe(ObservableEmitter e) throws Exception { } @Test + @SuppressUndeliverable public void basicWithErrorSerialized() { final Disposable d = Disposable.empty(); @@ -209,6 +214,7 @@ public void unsafeWithObservable() { } @Test + @SuppressUndeliverable public void createNullValue() { final Throwable[] error = { null }; @@ -232,6 +238,7 @@ public void subscribe(ObservableEmitter e) throws Exception { } @Test + @SuppressUndeliverable public void createNullValueSerialized() { final Throwable[] error = { null }; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java index 22a56a3356..b9d37ef118 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java @@ -96,6 +96,7 @@ public void empty() { } @Test + @SuppressUndeliverable public void error() { Observable sourceStrings = Observable.just("one", "two", "three", "four", "five", "six"); Observable errorSource = Observable.error(new RuntimeException("forced failure")); @@ -1178,6 +1179,7 @@ public void keySelectorThrows() { } @Test + @SuppressUndeliverable public void valueSelectorThrows() { Observable source = Observable.just(0, 1, 2, 3, 4, 5, 6); @@ -1233,6 +1235,7 @@ public void accept(GroupedObservable t1) { } @Test + @SuppressUndeliverable public void error2() { Observable source = Observable.concat(Observable.just(0), Observable. error(new TestException("Forced failure"))); @@ -1446,6 +1449,7 @@ public Integer apply(Integer i) { } @Test + @SuppressUndeliverable public void keySelectorAndDelayError() { Observable.just(1).concatWith(Observable.error(new TestException())) .groupBy(Functions.identity(), true) @@ -1460,6 +1464,7 @@ public ObservableSource apply(GroupedObservable g) th } @Test + @SuppressUndeliverable public void keyAndValueSelectorAndDelayError() { Observable.just(1).concatWith(Observable.error(new TestException())) .groupBy(Functions.identity(), Functions.identity(), true) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java index 3f01c05cb4..2e5af8b72e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java @@ -479,6 +479,7 @@ public Observable apply(Integer r, Observable l) throws Except } @Test + @SuppressUndeliverable public void innerErrorRight() { Observable.just(1) .groupJoin( diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLiftTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLiftTest.java index 325fc504ae..64a95db947 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLiftTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLiftTest.java @@ -19,10 +19,12 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverable; public class ObservableLiftTest extends RxJavaTest { @Test + @SuppressUndeliverable public void callbackCrash() { try { Observable.just(1) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java index 09b0bf0cfe..442f326e37 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java @@ -855,6 +855,7 @@ protected void subscribeActual(Observer observer) { } @Test + @SuppressUndeliverable public void badSourceSubscribe() { BadObservableSubscribe bo = new BadObservableSubscribe(); @@ -881,6 +882,7 @@ public void badSourceDispose() { } @Test + @SuppressUndeliverable public void badSourceConnect() { BadObservableConnect bo = new BadObservableConnect(); @@ -922,6 +924,7 @@ protected void subscribeActual(Observer observer) { } @Test + @SuppressUndeliverable public void badSourceSubscribe2() { BadObservableSubscribe2 bo = new BadObservableSubscribe2(); @@ -959,6 +962,7 @@ protected void subscribeActual(Observer observer) { } @Test + @SuppressUndeliverable public void badSourceCompleteDisconnect() { BadObservableConnect2 bo = new BadObservableConnect2(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWithPredicateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWithPredicateTest.java index 3b57f6c2e1..5bb4fba7d8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWithPredicateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWithPredicateTest.java @@ -37,6 +37,7 @@ import io.reactivex.rxjava3.testsupport.*; public class ObservableRetryWithPredicateTest extends RxJavaTest { + BiPredicate retryTwice = new BiPredicate() { @Override public boolean test(Integer t1, Throwable t2) { @@ -390,6 +391,7 @@ public void dontRetry() { } @Test + @SuppressUndeliverable public void retryDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final PublishSubject ps = PublishSubject.create(); @@ -438,6 +440,7 @@ public boolean test(Integer n, Throwable e) throws Exception { } @Test + @SuppressUndeliverable public void retryBiPredicateDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final PublishSubject ps = PublishSubject.create(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTest.java index f83fb8595d..6fb3143410 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTest.java @@ -34,7 +34,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.PublishSubject; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class ObservableTakeTest extends RxJavaTest { @@ -111,6 +111,7 @@ public Integer apply(Integer t1) { } @Test + @SuppressUndeliverable public void takeDoesntLeakErrors() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhileTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhileTest.java index 134e1f93f6..1b35f4144f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhileTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhileTest.java @@ -100,6 +100,7 @@ public boolean test(String input) { } @Test + @SuppressUndeliverable public void takeWhileDoesntLeakErrors() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutWithSelectorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutWithSelectorTest.java index 60cb5a8b92..44712626c3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutWithSelectorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutWithSelectorTest.java @@ -40,6 +40,7 @@ import io.reactivex.rxjava3.testsupport.*; public class ObservableTimeoutWithSelectorTest extends RxJavaTest { + @Test public void timeoutSelectorNormal1() { PublishSubject source = PublishSubject.create(); @@ -493,6 +494,7 @@ public void withOtherMainError() { } @Test + @SuppressUndeliverable public void badSourceTimeout() { new Observable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithObservableTest.java index bfb972626b..a623e3ded3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithObservableTest.java @@ -586,6 +586,7 @@ public void run() { } @Test + @SuppressUndeliverable public void disposeMainBoundaryErrorRace() { final TestException ex = new TestException(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java index ee0c112a25..941ce58f79 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java @@ -33,7 +33,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.TestScheduler; import io.reactivex.rxjava3.subjects.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class ObservableWindowWithStartEndObservableTest extends RxJavaTest { @@ -290,6 +290,7 @@ public ObservableSource apply(Integer v) throws Exception { } @Test + @SuppressUndeliverable public void endError() { PublishSubject source = PublishSubject.create(); PublishSubject start = PublishSubject.create(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java index 56ee06ff24..527ebc004b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java @@ -19,19 +19,19 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.*; -import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.*; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subjects.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class ObservableWindowWithTimeTest extends RxJavaTest { @@ -368,6 +368,7 @@ public void timeskipOverlapping() { } @Test + @SuppressUndeliverable public void exactOnError() { TestScheduler scheduler = new TestScheduler(); @@ -383,6 +384,7 @@ public void exactOnError() { } @Test + @SuppressUndeliverable public void overlappingOnError() { TestScheduler scheduler = new TestScheduler(); @@ -398,6 +400,7 @@ public void overlappingOnError() { } @Test + @SuppressUndeliverable public void skipOnError() { TestScheduler scheduler = new TestScheduler(); @@ -434,6 +437,7 @@ public void restartTimer() { } @Test + @SuppressUndeliverable public void exactBoundaryError() { Observable.error(new TestException()) .window(1, TimeUnit.DAYS, Schedulers.single(), 2, true) @@ -751,6 +755,7 @@ public void accept(Observable v) throws Exception { } @Test + @SuppressUndeliverable public void exactTimeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); @@ -831,6 +836,7 @@ public void accept(Observable v) throws Exception { } @Test + @SuppressUndeliverable public void exactTimeAndSizeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); @@ -911,6 +917,7 @@ public void accept(Observable v) throws Exception { } @Test + @SuppressUndeliverable public void skipTimeAndSizeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCreateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCreateTest.java index 79cb58c63b..8f8519c961 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCreateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCreateTest.java @@ -25,11 +25,12 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Cancellable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class SingleCreateTest extends RxJavaTest { @Test + @SuppressUndeliverable public void basic() { final Disposable d = Disposable.empty(); @@ -51,6 +52,7 @@ public void subscribe(SingleEmitter e) throws Exception { } @Test + @SuppressUndeliverable public void basicWithCancellable() { final Disposable d1 = Disposable.empty(); final Disposable d2 = Disposable.empty(); @@ -80,6 +82,7 @@ public void cancel() throws Exception { } @Test + @SuppressUndeliverable public void basicWithError() { final Disposable d = Disposable.empty(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUsingTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUsingTest.java index 28f268f423..d11c89ec8d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUsingTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUsingTest.java @@ -293,6 +293,7 @@ protected void subscribeActual(SingleObserver observer) { } @Test + @SuppressUndeliverable public void errorDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final PublishProcessor pp = PublishProcessor.create(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ExecutorSchedulerDelayedRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ExecutorSchedulerDelayedRunnableTest.java index 08e06b189e..3dd9c59344 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ExecutorSchedulerDelayedRunnableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ExecutorSchedulerDelayedRunnableTest.java @@ -22,10 +22,12 @@ import io.reactivex.rxjava3.core.RxJavaTest; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.schedulers.ExecutorScheduler.DelayedRunnable; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverable; public class ExecutorSchedulerDelayedRunnableTest extends RxJavaTest { @Test(expected = TestException.class) + @SuppressUndeliverable public void delayedRunnableCrash() { DelayedRunnable dl = new DelayedRunnable(new Runnable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java index ad568225e6..9ab44b2ff1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java @@ -27,11 +27,12 @@ import io.reactivex.rxjava3.internal.schedulers.SingleScheduler.ScheduledWorker; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class SingleSchedulerTest extends AbstractSchedulerTests { @Test + @SuppressUndeliverable public void shutdownRejects() { final int[] calls = { 0 }; diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java index f114623bc4..912d07b215 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java @@ -31,11 +31,12 @@ import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.TrampolineScheduler.*; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class TrampolineSchedulerInternalTest extends RxJavaTest { @Test + @SuppressUndeliverable public void scheduleDirectInterrupt() { Thread.currentThread().interrupt(); @@ -147,6 +148,7 @@ public void run() { } @Test + @SuppressUndeliverable public void reentrantScheduleInterrupt() { final Worker w = Schedulers.trampoline().createWorker(); try { diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java index 19bb7104ab..38abf77e87 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java @@ -27,7 +27,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class BoundedSubscriberTest extends RxJavaTest { @@ -315,6 +315,7 @@ public void accept(Subscription s) throws Exception { } @Test + @SuppressUndeliverable public void badSourceEmitAfterDone() { Flowable source = Flowable.fromPublisher(new Publisher() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java index 5c923949e8..e8cf5c3a47 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java @@ -26,7 +26,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class FutureSubscriberTest extends RxJavaTest { @@ -155,6 +155,7 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final FutureSubscriber fs = new FutureSubscriber<>(); @@ -180,6 +181,7 @@ public void run() { } @Test + @SuppressUndeliverable public void onCompleteCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final FutureSubscriber fs = new FutureSubscriber<>(); @@ -211,6 +213,7 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorOnComplete() throws Exception { fs.onError(new TestException("One")); fs.onComplete(); @@ -224,6 +227,7 @@ public void onErrorOnComplete() throws Exception { } @Test + @SuppressUndeliverable public void onCompleteOnError() throws Exception { fs.onComplete(); fs.onError(new TestException("One")); @@ -236,6 +240,7 @@ public void onCompleteOnError() throws Exception { } @Test + @SuppressUndeliverable public void cancelOnError() throws Exception { fs.cancel(true); fs.onError(new TestException("One")); @@ -249,6 +254,7 @@ public void cancelOnError() throws Exception { } @Test + @SuppressUndeliverable public void cancelOnComplete() throws Exception { fs.cancel(true); fs.onComplete(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriberTest.java index 17e4fb2fc9..a415835a4b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriberTest.java @@ -28,7 +28,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class LambdaSubscriberTest extends RxJavaTest { @@ -246,6 +246,7 @@ public void accept(Subscription s) throws Exception { } @Test + @SuppressUndeliverable public void badSourceEmitAfterDone() { Flowable source = Flowable.fromPublisher(new Publisher() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerObserverTest.java index cbb66e6425..b948188173 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerObserverTest.java @@ -160,6 +160,7 @@ public void onComplete() { } @Test + @SuppressUndeliverable @SuppressWarnings({ "rawtypes", "unchecked" }) public void reentrantErrorOnError() { final AtomicInteger wip = new AtomicInteger(); @@ -234,6 +235,7 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorOnCompleteRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerSubscriberTest.java index 44eea4907c..65835788bb 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerSubscriberTest.java @@ -166,6 +166,7 @@ public void onComplete() { } @Test + @SuppressUndeliverable @SuppressWarnings({ "rawtypes", "unchecked" }) public void reentrantErrorOnError() { final AtomicInteger wip = new AtomicInteger(); @@ -240,6 +241,7 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorOnCompleteRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { diff --git a/src/test/java/io/reactivex/rxjava3/observers/SafeObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/SafeObserverTest.java index 3afced2d40..adcb092aea 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/SafeObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/SafeObserverTest.java @@ -221,6 +221,7 @@ public void dispose() { } @Test + @SuppressUndeliverable public void onNextAfterComplete() { TestObserver to = new TestObserver<>(); diff --git a/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java index 874670bc5f..c631b7b114 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java @@ -1144,6 +1144,7 @@ public void nullOnNext() { } @Test + @SuppressUndeliverable public void onErrorQueuedUp() { AtomicReference> soRef = new AtomicReference<>(); TestObserverEx to = new TestObserverEx() { diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java index ebdd2fd8cc..b35f0d8a1f 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java @@ -26,7 +26,7 @@ import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class ParallelPeekTest extends RxJavaTest { @@ -37,6 +37,7 @@ public void subscriberCount() { } @Test + @SuppressUndeliverable public void onSubscribeCrash() { Flowable.range(1, 5) .parallel() @@ -125,6 +126,7 @@ public void run() throws Exception { } @Test + @SuppressUndeliverable public void onCompleteCrash() { Flowable.just(1) .parallel() diff --git a/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java index 3fa8554a7d..778cf78496 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java @@ -112,6 +112,7 @@ public void subscribeAfterError() { } @Test + @SuppressUndeliverable public void error() { AsyncProcessor processor = AsyncProcessor.create(); @@ -424,6 +425,7 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -481,6 +483,7 @@ public void onNext(Object t) { } @Test + @SuppressUndeliverable public void onErrorCrossCancel() { AsyncProcessor p = AsyncProcessor.create(); diff --git a/src/test/java/io/reactivex/rxjava3/processors/PublishProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/PublishProcessorTest.java index 7c413d9dc0..9741796049 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/PublishProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/PublishProcessorTest.java @@ -30,7 +30,7 @@ import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subscribers.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class PublishProcessorTest extends FlowableProcessorTest { @@ -40,6 +40,7 @@ protected FlowableProcessor create() { } @Test + @SuppressUndeliverable public void completed() { PublishProcessor processor = PublishProcessor.create(); @@ -113,6 +114,7 @@ private void assertCompletedSubscriber(Subscriber subscriber) { } @Test + @SuppressUndeliverable public void error() { PublishProcessor processor = PublishProcessor.create(); @@ -434,6 +436,7 @@ public void onNext(Integer t) { } @Test + @SuppressUndeliverable public void crossCancelOnError() { final TestSubscriber ts1 = new TestSubscriber<>(); TestSubscriber ts2 = new TestSubscriber() { diff --git a/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java index 74b8fac206..2febfb0629 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java @@ -47,6 +47,7 @@ protected FlowableProcessor create() { } @Test + @SuppressUndeliverable public void completed() { ReplayProcessor processor = ReplayProcessor.create(); @@ -71,6 +72,7 @@ public void completed() { } @Test + @SuppressUndeliverable public void completedStopsEmittingData() { ReplayProcessor channel = ReplayProcessor.create(); Subscriber observerA = TestHelper.mockSubscriber(); @@ -140,6 +142,7 @@ public void completedStopsEmittingData() { } @Test + @SuppressUndeliverable public void completedAfterError() { ReplayProcessor processor = ReplayProcessor.create(); @@ -170,6 +173,7 @@ private void assertCompletedSubscriber(Subscriber subscriber) { } @Test + @SuppressUndeliverable public void error() { ReplayProcessor processor = ReplayProcessor.create(); diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/CachedThreadSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/CachedThreadSchedulerTest.java index 8782142035..26cec9b53b 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/CachedThreadSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/CachedThreadSchedulerTest.java @@ -24,6 +24,7 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.schedulers.IoScheduler; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverable; public class CachedThreadSchedulerTest extends AbstractSchedulerConcurrencyTests { @@ -91,6 +92,7 @@ public void workerDisposed() { } @Test + @SuppressUndeliverable public void shutdownRejects() { final int[] calls = { 0 }; diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java b/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java index 582a419b13..5f5df8d410 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java @@ -27,6 +27,7 @@ import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.schedulers.ComputationScheduler; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverable; public class ComputationSchedulerTests extends AbstractSchedulerConcurrencyTests { @@ -161,6 +162,7 @@ public void cancelledTaskRetention() throws InterruptedException { } @Test + @SuppressUndeliverable public void shutdownRejects() { final int[] calls = { 0 }; @@ -285,6 +287,7 @@ public Thread newThread(Runnable r) { } @Test + @SuppressUndeliverable public void periodicTaskShouldStopOnError() throws Exception { AtomicInteger repeatCount = new AtomicInteger(); @@ -302,6 +305,7 @@ public void run() { } @Test + @SuppressUndeliverable public void periodicTaskShouldStopOnError2() throws Exception { AtomicInteger repeatCount = new AtomicInteger(); diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/NewThreadSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/NewThreadSchedulerTest.java index 352e13333c..1476db8b92 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/NewThreadSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/NewThreadSchedulerTest.java @@ -23,6 +23,7 @@ import io.reactivex.rxjava3.core.Scheduler.Worker; import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.internal.schedulers.NewThreadWorker; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverable; public class NewThreadSchedulerTest extends AbstractSchedulerConcurrencyTests { @@ -37,6 +38,7 @@ public final void handledErrorIsNotDeliveredToThreadHandler() throws Interrupted } @Test + @SuppressUndeliverable public void shutdownRejects() { final int[] calls = { 0 }; @@ -75,6 +77,7 @@ public void run() { * @throws Exception on error */ @Test + @SuppressUndeliverable public void npeRegression() throws Exception { Scheduler s = getScheduler(); NewThreadWorker w = (NewThreadWorker) s.createWorker(); diff --git a/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java index 96d63b3ab2..17d3a94a62 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java @@ -112,6 +112,7 @@ public void subscribeAfterError() { } @Test + @SuppressUndeliverable public void error() { AsyncSubject subject = AsyncSubject.create(); @@ -418,6 +419,7 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -475,6 +477,7 @@ public void onNext(Object t) { } @Test + @SuppressUndeliverable public void onErrorCrossCancel() { AsyncSubject p = AsyncSubject.create(); diff --git a/src/test/java/io/reactivex/rxjava3/subjects/PublishSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/PublishSubjectTest.java index 7c2c333a2b..9479951609 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/PublishSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/PublishSubjectTest.java @@ -29,7 +29,7 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.observers.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class PublishSubjectTest extends SubjectTest { @@ -39,6 +39,7 @@ protected Subject create() { } @Test + @SuppressUndeliverable public void completed() { PublishSubject subject = PublishSubject.create(); @@ -112,6 +113,7 @@ private void assertCompletedSubscriber(Observer observer) { } @Test + @SuppressUndeliverable public void error() { PublishSubject subject = PublishSubject.create(); @@ -414,6 +416,7 @@ public void onNext(Integer t) { } @Test + @SuppressUndeliverable public void crossCancelOnError() { final TestObserver to1 = new TestObserver<>(); TestObserver to2 = new TestObserver() { diff --git a/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java index 106f85bfc5..3444431824 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java @@ -44,6 +44,7 @@ protected Subject create() { } @Test + @SuppressUndeliverable public void completed() { ReplaySubject subject = ReplaySubject.create(); @@ -68,6 +69,7 @@ public void completed() { } @Test + @SuppressUndeliverable public void completedStopsEmittingData() { ReplaySubject channel = ReplaySubject.create(); Observer observerA = TestHelper.mockObserver(); @@ -137,6 +139,7 @@ public void completedStopsEmittingData() { } @Test + @SuppressUndeliverable public void completedAfterError() { ReplaySubject subject = ReplaySubject.create(); @@ -167,6 +170,7 @@ private void assertCompletedSubscriber(Observer observer) { } @Test + @SuppressUndeliverable public void error() { ReplaySubject subject = ReplaySubject.create(); diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/SafeSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/SafeSubscriberTest.java index e2bc1b1bbd..ca61c8565a 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/SafeSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/SafeSubscriberTest.java @@ -98,6 +98,7 @@ public void onNextAfterOnCompleted() { * Ensure onError can not be called after onComplete. */ @Test + @SuppressUndeliverable public void onErrorAfterOnCompleted() { TestObservable t = new TestObservable(); Flowable st = Flowable.unsafeCreate(t); @@ -364,6 +365,7 @@ public void dispose() { } @Test + @SuppressUndeliverable public void onNextAfterComplete() { TestSubscriber ts = new TestSubscriber<>(); diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java index 7391b212ae..e4292216d6 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java @@ -1136,6 +1136,7 @@ public void nullOnNext() { } @Test + @SuppressUndeliverable public void onErrorQueuedUp() { AtomicReference> ssRef = new AtomicReference<>(); TestSubscriberEx ts = new TestSubscriberEx() { diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverable.java b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverable.java new file mode 100644 index 0000000000..448b896ce0 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverable.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivex.rxjava3.testsupport; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface SuppressUndeliverable { +} diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java new file mode 100644 index 0000000000..392fbdba33 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivex.rxjava3.testsupport; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import io.reactivex.rxjava3.exceptions.UndeliverableException; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * A rule for suppressing UndeliverableException handling. + * + *

Test classes that use this rule can suppress UndeliverableException + * handling by annotating the test method with SuppressUndeliverable. + */ +public class SuppressUndeliverableRule implements TestRule { + + @Override + public Statement apply(Statement base, Description description) { + if (description.getAnnotation(SuppressUndeliverable.class) != null) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + RxJavaPlugins.setErrorHandler(throwable -> { + if (!(throwable instanceof UndeliverableException)) { + throwable.printStackTrace(); + Thread currentThread = Thread.currentThread(); + currentThread.getUncaughtExceptionHandler().uncaughtException(currentThread, throwable); + } + }); + base.evaluate(); + } finally { + RxJavaPlugins.setErrorHandler(null); + } + } + }; + } else { + return base; + } + } +} From 98acac218cdb04d279b5ac49bb1afc65bc6ec4fe Mon Sep 17 00:00:00 2001 From: mrkulli Date: Tue, 26 May 2020 12:31:33 -0500 Subject: [PATCH 035/521] Fixed image link and added java examples for Connectable Observable operators (#6997) * Fixed image link and added java examples * 3.x:Update Getting started docs --- docs/Connectable-Observable-Operators.md | 47 +++++++++++------------- docs/Getting-Started.md | 24 ++++++------ 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/docs/Connectable-Observable-Operators.md b/docs/Connectable-Observable-Operators.md index 582f0fac12..157ded821b 100644 --- a/docs/Connectable-Observable-Operators.md +++ b/docs/Connectable-Observable-Operators.md @@ -7,25 +7,22 @@ This section explains the [`ConnectableObservable`](http://reactivex.io/RxJava/j A Connectable Observable resembles an ordinary Observable, except that it does not begin emitting items when it is subscribed to, but only when its `connect()` method is called. In this way you can wait for all intended Subscribers to subscribe to the Observable before the Observable begins emitting items. - + The following example code shows two Subscribers subscribing to the same Observable. In the first case, they subscribe to an ordinary Observable; in the second case, they subscribe to a Connectable Observable that only connects after both Subscribers subscribe. Note the difference in the output: **Example #1:** -```groovy -def firstMillion = Observable.range( 1, 1000000 ).sample(7, java.util.concurrent.TimeUnit.MILLISECONDS); +```java +Observable firstMillion = Observable.range(1, 1000000).sample(7, java.util.concurrent.TimeUnit.MILLISECONDS); -firstMillion.subscribe( - { println("Subscriber #1:" + it); }, // onNext - { println("Error: " + it.getMessage()); }, // onError - { println("Sequence #1 complete"); } // onCompleted -); - -firstMillion.subscribe( - { println("Subscriber #2:" + it); }, // onNext - { println("Error: " + it.getMessage()); }, // onError - { println("Sequence #2 complete"); } // onCompleted -); +firstMillion.subscribe(next -> System.out.println("Subscriber #1: " + next), // onNext + throwable -> System.out.println("Error: " + throwable), // onError + () -> System.out.println("Sequence #1 complete") // onComplete + ); +firstMillion.subscribe(next -> System.out.println("Subscriber #2: " + next), // onNext + throwable -> System.out.println("Error: " + throwable), // onError + () -> System.out.println("Sequence #2 complete") // onComplete + ); ``` ``` Subscriber #1:211128 @@ -40,20 +37,18 @@ Subscriber #2:826996 Sequence #2 complete ``` **Example #2:** -```groovy -def firstMillion = Observable.range( 1, 1000000 ).sample(7, java.util.concurrent.TimeUnit.MILLISECONDS).publish(); +```java +ConnectableObservable firstMillion = Observable.range(1, 1000000).sample(7, java.util.concurrent.TimeUnit.MILLISECONDS).publish(); -firstMillion.subscribe( - { println("Subscriber #1:" + it); }, // onNext - { println("Error: " + it.getMessage()); }, // onError - { println("Sequence #1 complete"); } // onCompleted -); +firstMillion.subscribe(next -> System.out.println("Subscriber #1: " + next), // onNext + throwable -> System.out.println("Error: " + throwable), // onError + () -> System.out.println("Sequence #1 complete") // onComplete + ); -firstMillion.subscribe( - { println("Subscriber #2:" + it); }, // onNext - { println("Error: " + it.getMessage()); }, // onError - { println("Sequence #2 complete"); } // onCompleted -); +firstMillion.subscribe(next -> System.out.println("Subscriber #2: " + next), // onNext + throwable -> System.out.println("Error: " + throwable), // onError + () -> System.out.println("Sequence #2 complete") // onComplete + ); firstMillion.connect(); ``` diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md index fb9baa47dd..e3453fbc28 100644 --- a/docs/Getting-Started.md +++ b/docs/Getting-Started.md @@ -1,20 +1,20 @@ ## Getting Binaries -You can find binaries and dependency information for Maven, Ivy, Gradle, SBT, and others at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Cg%3A"io.reactivex.rxjava2"%20AND%20"rxjava2"). +You can find binaries and dependency information for Maven, Ivy, Gradle, SBT, and others at [http://search.maven.org](https://search.maven.org/search?q=g:io.reactivex.rxjava3%20AND%20rxjava). Example for Maven: ```xml - io.reactivex.rxjava2 + io.reactivex.rxjava3 rxjava - 2.2.0 + 3.0.4 ``` and for Ivy: ```xml - + ``` and for SBT: @@ -22,12 +22,12 @@ and for SBT: ```scala libraryDependencies += "io.reactivex" %% "rxscala" % "0.26.5" -libraryDependencies += "io.reactivex.rxjava2" % "rxjava" % "2.2.0" +libraryDependencies += "io.reactivex.rxjava3" % "rxjava" % "3.0.4" ``` and for Gradle: ```groovy -compile 'io.reactivex.rxjava2:rxjava:2.2.0' +compile 'io.reactivex.rxjava3:rxjava:3.0.4' ``` If you need to download the jars instead of using a build system, create a Maven `pom` file like this with the desired version: @@ -38,17 +38,17 @@ If you need to download the jars instead of using a build system, create a Maven xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0%20http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - io.reactivex.rxjava2 + io.reactivex.rxjava3 rxjava - 2.2.0 + 3.0.4 RxJava Reactive Extensions for Java https://github.com/ReactiveX/RxJava - io.reactivex.rxjava2 + io.reactivex.rxjava3 rxjava - 2.2.0 + 3.0.4 @@ -66,7 +66,7 @@ You need Java 6 or later. ### Snapshots -Snapshots are available via [JFrog](https://oss.jfrog.org/libs-snapshot/io/reactivex/rxjava2/rxjava/): +Snapshots are available via [JFrog](https://oss.jfrog.org/libs-snapshot/io/reactivex/rxjava3/rxjava/): ```groovy repositories { @@ -74,7 +74,7 @@ repositories { } dependencies { - compile 'io.reactivex.rxjava2:rxjava:2.2.0-SNAPSHOT' + compile 'io.reactivex.rxjava3:rxjava:3.0.4' } ``` From d3dd133c3fe30610c3f4fa878de8285bf95177de Mon Sep 17 00:00:00 2001 From: Kranthi kulli Date: Sun, 31 May 2020 08:59:35 -0500 Subject: [PATCH 036/521] 3.x update Conditional-and-Boolean-Operators.md (#7000) * Fixed image link and added java examples * 3.x:Update Getting started docs * Conditional-and-Boolean-Operators documentation update --- docs/Conditional-and-Boolean-Operators.md | 180 ++++++++++++++++++++-- 1 file changed, 164 insertions(+), 16 deletions(-) diff --git a/docs/Conditional-and-Boolean-Operators.md b/docs/Conditional-and-Boolean-Operators.md index f7ed64ce34..e6d1358e31 100644 --- a/docs/Conditional-and-Boolean-Operators.md +++ b/docs/Conditional-and-Boolean-Operators.md @@ -1,21 +1,169 @@ This section explains operators with which you conditionally emit or transform Observables, or can do boolean evaluations of them: ### Conditional Operators -* [**`amb( )`**](http://reactivex.io/documentation/operators/amb.html) — given two or more source Observables, emits all of the items from the first of these Observables to emit an item -* [**`defaultIfEmpty( )`**](http://reactivex.io/documentation/operators/defaultifempty.html) — emit items from the source Observable, or emit a default item if the source Observable completes after emitting no items -* (`rxjava-computation-expressions`) [**`doWhile( )`**](http://reactivex.io/documentation/operators/repeat.html) — emit the source Observable's sequence, and then repeat the sequence as long as a condition remains true -* (`rxjava-computation-expressions`) [**`ifThen( )`**](http://reactivex.io/documentation/operators/defer.html) — only emit the source Observable's sequence if a condition is true, otherwise emit an empty or default sequence -* [**`skipUntil( )`**](http://reactivex.io/documentation/operators/skipuntil.html) — discard items emitted by a source Observable until a second Observable emits an item, then emit the remainder of the source Observable's items -* [**`skipWhile( )`**](http://reactivex.io/documentation/operators/skipwhile.html) — discard items emitted by an Observable until a specified condition is false, then emit the remainder -* (`rxjava-computation-expressions`) [**`switchCase( )`**](http://reactivex.io/documentation/operators/defer.html) — emit the sequence from a particular Observable based on the results of an evaluation -* [**`takeUntil( )`**](http://reactivex.io/documentation/operators/takeuntil.html) — emits the items from the source Observable until a second Observable emits an item or issues a notification -* [**`takeWhile( )` and `takeWhileWithIndex( )`**](http://reactivex.io/documentation/operators/takewhile.html) — emit items emitted by an Observable as long as a specified condition is true, then skip the remainder -* (`rxjava-computation-expressions`) [**`whileDo( )`**](http://reactivex.io/documentation/operators/repeat.html) — if a condition is true, emit the source Observable's sequence and then repeat the sequence as long as the condition remains true - -> (`rxjava-computation-expressions`) — indicates that this operator is currently part of the optional `rxjava-computation-expressions` package under `rxjava-contrib` and is not included with the standard RxJava set of operators + +### Outline + +- [`amb`](#all) +- [`defaultIfEmpty`](#defaultIfEmpty) +- [`skipUntil`](#skipUntil) +- [`skipWhile`](#skipWhile) +- [`takeUntil`](#takeUntil) +- [`takeWhile`](#takeUntil) + +## amb + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/amb.html](http://reactivex.io/documentation/operators/amb.html) + +given two or more source Observables, emits all of the items from the first of these Observables to emit an item + +```java + Observable source1 = Observable.range(1, 5); + Observable source2 = Observable.range(6, 5); + Observable.amb(new ArrayList(Arrays.asList(source1, source2))) + .subscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s\n", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` +## defaultIfEmpty + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/defaultifempty.html](http://reactivex.io/documentation/operators/defaultifempty.html) + +emit items from the source Observable, or emit a default item if the source Observable completes after emitting no items + +```java + Observable.empty().defaultIfEmpty(1).blockingSubscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` + +## skipUntil + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/skipuntil.html](http://reactivex.io/documentation/operators/skipuntil.html) + +discard items emitted by a source Observable until a second Observable emits an item, then emit the remainder of the source Observable's items + +```java +Observable observable1 = Observable.range(1, 10).doOnNext(next -> Thread.sleep(1000)); + +observable1.skipUntil(Observable.timer(3, TimeUnit.SECONDS)) + .subscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` +## skipWhile + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/skipwhile.html](http://reactivex.io/documentation/operators/skipwhile.html) + +discard items emitted by an Observable until a specified condition is false, then emit the remainder + +```java +Observable.range(1, 10).skipWhile(next -> next < 5) + .subscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` + +## takeUntil + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/takeuntil.html](http://reactivex.io/documentation/operators/takeuntil.html) + +emits the items from the source Observable until a second Observable emits an item or issues a notification + +```java +Observable.range(1, 10).takeUntil(value -> value >= 5) + .subscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` + +## takeWhile + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/takewhile.html](http://reactivex.io/documentation/operators/takewhile.html) + +emit items emitted by an Observable as long as a specified condition is true, then skip the remainder + +```java + Observable.range(1, 10).takeWhile(value -> value <= 5) + .subscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` ### Boolean Operators -* [**`all( )`**](http://reactivex.io/documentation/operators/all.html) — determine whether all items emitted by an Observable meet some criteria -* [**`contains( )`**](http://reactivex.io/documentation/operators/contains.html) — determine whether an Observable emits a particular item or not -* [**`exists( )` and `isEmpty( )`**](http://reactivex.io/documentation/operators/contains.html) — determine whether an Observable emits any items or not -* [**`sequenceEqual( )`**](http://reactivex.io/documentation/operators/sequenceequal.html) — test the equality of the sequences emitted by two Observables + +### Outline + +- [`all`](#all) +- [`contains`](#contains) +- [`isEmpty`](#isEmpty) +- [`sequenceEqual`](#sequenceEqual) + +## all +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/all.html](http://reactivex.io/documentation/operators/all.html) + +determine whether all items emitted by an Observable meet some criteria + +```java +Flowable.range(0,10).doOnNext(next -> System.out.println(next)).all(integer -> integer<10). + blockingSubscribe(success->System.out.println("Success: "+success)); +``` + +## contains +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/contains.html](http://reactivex.io/documentation/operators/contains.html) + +determine whether an Observable emits a particular item or not + +```java +Flowable.range(1,10).doOnNext(next->System.out.println(next)) + .contains(4).blockingSubscribe(contains->System.out.println("contains: "+contains)); +``` + +## isEmpty +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/contains.html](http://reactivex.io/documentation/operators/contains.html) + +determine whether the source Publisher is empty + +```java +Flowable.empty().isEmpty().subscribe(isEmpty -> System.out.printf("isEmpty: %s", isEmpty)); +``` + +## sequenceEqual +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/sequenceequal.html](http://reactivex.io/documentation/operators/sequenceequal.html) + +test the equality of the sequences emitted by two Observables + +```java +Flowable flowable1 = Flowable.range(1,3).doOnNext(next-> System.out.print("flowable1: "+next + " ")); + +Flowable flowable2 = Flowable.range(1,3).doOnNext(next-> System.out.println("flowable2: "+next)); + +Flowable.sequenceEqual(Flowable.fromPublisher(flowable1),Flowable.fromPublisher(flowable2)) + .blockingSubscribe(sequenceEqual->System.out.println("sequenceEqual: "+sequenceEqual)); +``` From da498c5c9b91cceaa37ca8abdaa4f914b3ec6d9c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2020 09:39:39 +0200 Subject: [PATCH 037/521] Bump biz.aQute.bnd.gradle from 5.0.1 to 5.1.0 (#7003) Bumps [biz.aQute.bnd.gradle](https://github.com/bndtools/bnd) from 5.0.1 to 5.1.0. - [Release notes](https://github.com/bndtools/bnd/releases) - [Changelog](https://github.com/bndtools/bnd/blob/master/docs/ADDING_RELEASE_DOCS.md) - [Commits](https://github.com/bndtools/bnd/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c35b696d20..0754e86f3a 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" ext.jfrogExtractorVersion = "4.15.2" - ext.bndVersion = "5.0.1" + ext.bndVersion = "5.1.0" ext.checkstyleVersion = "8.26" // -------------------------------------- From 043dbaabd86f9b54d08a55e3976dffb66d2ee54c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2020 09:59:43 +0200 Subject: [PATCH 038/521] Bump gradle-animalsniffer-plugin from 1.5.0 to 1.5.1 (#7007) Bumps gradle-animalsniffer-plugin from 1.5.0 to 1.5.1. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0754e86f3a..afb45c7cd1 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { ext.jmhGradleVersion = "0.5.0" ext.guavaVersion = "29.0-jre" ext.jacocoVersion = "0.8.4" - ext.animalSnifferVersion = "1.5.0" + ext.animalSnifferVersion = "1.5.1" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" ext.jfrogExtractorVersion = "4.15.2" From 3245db767d99f62af51a86e4c509b16820030611 Mon Sep 17 00:00:00 2001 From: Abhishek <6554853+sh-abhi@users.noreply.github.com> Date: Mon, 8 Jun 2020 08:21:22 +0000 Subject: [PATCH 039/521] Updating SuppressUndeliverableRule to have a named inner class instead of an anonymous inner class. (#7006) --- .../SuppressUndeliverableRule.java | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java index 392fbdba33..ea013daac5 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java @@ -30,26 +30,35 @@ */ public class SuppressUndeliverableRule implements TestRule { + private static class SuppressUndeliverableRuleStatement extends Statement { + private Statement base; + + private SuppressUndeliverableRuleStatement(){} + public SuppressUndeliverableRuleStatement(Statement base) { + this.base = base; + } + + @Override + public void evaluate() throws Throwable { + try { + RxJavaPlugins.setErrorHandler(throwable -> { + if (!(throwable instanceof UndeliverableException)) { + throwable.printStackTrace(); + Thread currentThread = Thread.currentThread(); + currentThread.getUncaughtExceptionHandler().uncaughtException(currentThread, throwable); + } + }); + base.evaluate(); + } finally { + RxJavaPlugins.setErrorHandler(null); + } + } + } + @Override public Statement apply(Statement base, Description description) { - if (description.getAnnotation(SuppressUndeliverable.class) != null) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - try { - RxJavaPlugins.setErrorHandler(throwable -> { - if (!(throwable instanceof UndeliverableException)) { - throwable.printStackTrace(); - Thread currentThread = Thread.currentThread(); - currentThread.getUncaughtExceptionHandler().uncaughtException(currentThread, throwable); - } - }); - base.evaluate(); - } finally { - RxJavaPlugins.setErrorHandler(null); - } - } - }; + if (description != null && description.getAnnotation(SuppressUndeliverable.class) != null) { + return new SuppressUndeliverableRuleStatement(base); } else { return base; } From 3ac8cfc14a91edc8adf66d47c58a76545e61d18d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 09:56:47 +0200 Subject: [PATCH 040/521] Bump biz.aQute.bnd.gradle from 5.1.0 to 5.1.1 (#7009) Bumps [biz.aQute.bnd.gradle](https://github.com/bndtools/bnd) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/bndtools/bnd/releases) - [Changelog](https://github.com/bndtools/bnd/blob/master/docs/ADDING_RELEASE_DOCS.md) - [Commits](https://github.com/bndtools/bnd/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index afb45c7cd1..22b5601238 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" ext.jfrogExtractorVersion = "4.15.2" - ext.bndVersion = "5.1.0" + ext.bndVersion = "5.1.1" ext.checkstyleVersion = "8.26" // -------------------------------------- From d209606e0eaaf87bb5493fd71a8413822f9992f2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 26 Jun 2020 09:22:40 +0200 Subject: [PATCH 041/521] Bump build-info-extractor-gradle from 4.15.2 to 4.16.0 (#7013) Bumps build-info-extractor-gradle from 4.15.2 to 4.16.0. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 22b5601238..6328add6c0 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.1" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" - ext.jfrogExtractorVersion = "4.15.2" + ext.jfrogExtractorVersion = "4.16.0" ext.bndVersion = "5.1.1" ext.checkstyleVersion = "8.26" From 880eed2ac0dc1e3fc234bb652f5e177ce643093c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20K=C3=B6rner?= <48256264+timkoerner@users.noreply.github.com> Date: Mon, 6 Jul 2020 17:47:35 +0200 Subject: [PATCH 042/521] Edit dependency for gradle (#7023) Since gradle 3.0.x the `compile` keyword is deprecated. [You should](https://developer.android.com/studio/build/dependencies#dependency_configurations) use 'implementation' instead. --- docs/Getting-Started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md index e3453fbc28..d2ac2e4640 100644 --- a/docs/Getting-Started.md +++ b/docs/Getting-Started.md @@ -27,7 +27,7 @@ libraryDependencies += "io.reactivex.rxjava3" % "rxjava" % "3.0.4" and for Gradle: ```groovy -compile 'io.reactivex.rxjava3:rxjava:3.0.4' +implementation 'io.reactivex.rxjava3:rxjava:3.0.4' ``` If you need to download the jars instead of using a build system, create a Maven `pom` file like this with the desired version: From 9854a83cac6e2c584e22efc231b6db30ddfabcbe Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2020 08:08:04 +0200 Subject: [PATCH 043/521] Bump build-info-extractor-gradle from 4.16.0 to 4.16.1 (#7024) Bumps build-info-extractor-gradle from 4.16.0 to 4.16.1. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6328add6c0..09753f1214 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.1" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" - ext.jfrogExtractorVersion = "4.16.0" + ext.jfrogExtractorVersion = "4.16.1" ext.bndVersion = "5.1.1" ext.checkstyleVersion = "8.26" From b345254d55368c7a129c75441e4a7d0dfbd5871f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2020 13:54:01 +0200 Subject: [PATCH 044/521] Bump mockito-core from 3.3.3 to 3.4.0 (#7027) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.3.3 to 3.4.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.3.3...v3.4.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 09753f1214..9adad92847 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.0.0" - ext.mockitoVersion = "3.3.3" + ext.mockitoVersion = "3.4.0" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.0" ext.guavaVersion = "29.0-jre" From 53cde33515ee0d2074dbcd82c6dc0174861aa06c Mon Sep 17 00:00:00 2001 From: Lugduni Desrosiers <36016544+ddunig2@users.noreply.github.com> Date: Mon, 13 Jul 2020 14:30:45 -0400 Subject: [PATCH 045/521] fixed for issue #7001 (#7028) --- .../operators/flowable/FlowableGroupByTest.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java index c86e9f260d..1e9ce41786 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java @@ -2667,19 +2667,17 @@ public void issue6974Part2Case2Loop() { static void issue6974RunPart2NoEvict(int groupByBufferSize, int flatMapMaxConcurrency, int groups, boolean notifyOnExplicitEviction) { - TestSubscriber ts = Flowable + + Flowable .range(1, 500_000) .map(i -> i % groups) .groupBy(i -> i) .flatMap(gf -> gf .take(10, TimeUnit.MILLISECONDS) , flatMapMaxConcurrency) - .test(); - - ts + .subscribeWith(new TestSubscriberEx<>()) .awaitDone(5, TimeUnit.SECONDS) - .assertNoErrors() - .assertComplete(); + .assertTerminated(); // MBE is possible if the async group closing is slow } @Test From 8e71c100e073dacac6b7a1f910830c0bf7bbaeab Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2020 10:04:52 +0200 Subject: [PATCH 046/521] Bump mockito-core from 3.4.0 to 3.4.2 (#7031) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.4.0 to 3.4.2. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.4.0...v3.4.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9adad92847..59f271636d 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.0.0" - ext.mockitoVersion = "3.4.0" + ext.mockitoVersion = "3.4.2" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.0" ext.guavaVersion = "29.0-jre" From cc48e7746b04651bb614210df83e4c83f66ef816 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2020 17:40:23 +0200 Subject: [PATCH 047/521] Bump mockito-core from 3.4.2 to 3.4.4 (#7035) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.4.2 to 3.4.4. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.4.2...v3.4.4) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 59f271636d..f73c17c287 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.0.0" - ext.mockitoVersion = "3.4.2" + ext.mockitoVersion = "3.4.4" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.0" ext.guavaVersion = "29.0-jre" From 343be6a3e6fbe38b6c2b441c5f391211caf3602d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2020 11:36:37 +0200 Subject: [PATCH 048/521] Bump biz.aQute.bnd.gradle from 5.1.1 to 5.1.2 (#7038) Bumps [biz.aQute.bnd.gradle](https://github.com/bndtools/bnd) from 5.1.1 to 5.1.2. - [Release notes](https://github.com/bndtools/bnd/releases) - [Changelog](https://github.com/bndtools/bnd/blob/master/docs/ADDING_RELEASE_DOCS.md) - [Commits](https://github.com/bndtools/bnd/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f73c17c287..59bc72c8f4 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" ext.jfrogExtractorVersion = "4.16.1" - ext.bndVersion = "5.1.1" + ext.bndVersion = "5.1.2" ext.checkstyleVersion = "8.26" // -------------------------------------- From 3e0977dedb9144663d4145fcb3a79bb379e136e4 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Sat, 25 Jul 2020 12:21:26 +0200 Subject: [PATCH 049/521] 3.x: Fix map() conditional chain causing NPE (#7040) --- .../operators/flowable/FlowableMap.java | 7 ++++++- .../jdk8/FlowableMapOptionalTest.java | 19 ++++++++++++++++++- .../operators/flowable/FlowableMapTest.java | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMap.java index 208baca0b2..6bae806972 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMap.java @@ -115,7 +115,12 @@ public void onNext(T t) { @Override public boolean tryOnNext(T t) { if (done) { - return false; + return true; + } + + if (sourceMode != NONE) { + downstream.tryOnNext(null); + return true; } U v; diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableMapOptionalTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableMapOptionalTest.java index 85fc8d75be..3e3f418790 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableMapOptionalTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableMapOptionalTest.java @@ -24,9 +24,10 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.processors.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class FlowableMapOptionalTest extends RxJavaTest { @@ -467,4 +468,20 @@ public void boundaryFusedMixedConditional() { .assertFusionMode(QueueFuseable.NONE) .assertResult(2, 4, 6, 8, 10); } + + @Test + public void conditionalFusionNoNPE() { + TestSubscriberEx ts = new TestSubscriberEx<>() + .setInitialFusionMode(QueueFuseable.ANY); + + Flowable.empty() + .observeOn(ImmediateThinScheduler.INSTANCE) + .filter(v -> true) + .mapOptional(Optional::of) + .filter(v -> true) + .subscribe(ts) + ; + + ts.assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapTest.java index 1c0e6c58a3..f7898ac5a9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapTest.java @@ -28,6 +28,7 @@ import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.*; @@ -617,4 +618,19 @@ public Object apply(Flowable f) throws Exception { }, false, 1, 1, 1); } + @Test + public void conditionalFusionNoNPE() { + TestSubscriberEx ts = new TestSubscriberEx<>() + .setInitialFusionMode(QueueFuseable.ANY); + + Flowable.empty() + .observeOn(ImmediateThinScheduler.INSTANCE) + .filter(v -> true) + .map(v -> v) + .filter(v -> true) + .subscribe(ts) + ; + + ts.assertResult(); + } } From 5fefe60eed4993c2dd28516e4861ff91ea2de54e Mon Sep 17 00:00:00 2001 From: Jakob van Kruijssen Date: Tue, 28 Jul 2020 13:05:01 +0200 Subject: [PATCH 050/521] Made the parameter of {Flowable,Observable}.collect(Collector) contravariant on T. (#7033) --- src/main/java/io/reactivex/rxjava3/core/Flowable.java | 2 +- src/main/java/io/reactivex/rxjava3/core/Observable.java | 2 +- .../rxjava3/internal/jdk8/FlowableCollectWithCollector.java | 6 +++--- .../internal/jdk8/FlowableCollectWithCollectorSingle.java | 6 +++--- .../internal/jdk8/ObservableCollectWithCollector.java | 6 +++--- .../internal/jdk8/ObservableCollectWithCollectorSingle.java | 6 +++--- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 2843a2909a..01c3c4438e 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -19802,7 +19802,7 @@ public final TestSubscriber test(long initialRequest, boolean cancel) { // No @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) @NonNull - public final <@NonNull R, A> Single collect(@NonNull Collector collector) { + public final <@NonNull R, A> Single collect(@NonNull Collector collector) { Objects.requireNonNull(collector, "collector is null"); return RxJavaPlugins.onAssembly(new FlowableCollectWithCollectorSingle<>(this, collector)); } diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index 2e0aff91ba..eabe9012ab 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -16642,7 +16642,7 @@ public final TestObserver test(boolean dispose) { // NoPMD @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @NonNull - public final <@NonNull R, A> Single collect(@NonNull Collector collector) { + public final <@NonNull R, A> Single collect(@NonNull Collector collector) { Objects.requireNonNull(collector, "collector is null"); return RxJavaPlugins.onAssembly(new ObservableCollectWithCollectorSingle<>(this, collector)); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollector.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollector.java index bc408db304..11a00aadb9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollector.java +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollector.java @@ -36,9 +36,9 @@ public final class FlowableCollectWithCollector extends Flowable { final Flowable source; - final Collector collector; + final Collector collector; - public FlowableCollectWithCollector(Flowable source, Collector collector) { + public FlowableCollectWithCollector(Flowable source, Collector collector) { this.source = source; this.collector = collector; } @@ -46,7 +46,7 @@ public FlowableCollectWithCollector(Flowable source, Collector colle @Override protected void subscribeActual(@NonNull Subscriber s) { A container; - BiConsumer accumulator; + BiConsumer accumulator; Function finisher; try { diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollectorSingle.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollectorSingle.java index 134de68415..14c1ebf4a8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollectorSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollectorSingle.java @@ -39,9 +39,9 @@ public final class FlowableCollectWithCollectorSingle extends Single final Flowable source; - final Collector collector; + final Collector collector; - public FlowableCollectWithCollectorSingle(Flowable source, Collector collector) { + public FlowableCollectWithCollectorSingle(Flowable source, Collector collector) { this.source = source; this.collector = collector; } @@ -54,7 +54,7 @@ public Flowable fuseToFlowable() { @Override protected void subscribeActual(@NonNull SingleObserver observer) { A container; - BiConsumer accumulator; + BiConsumer accumulator; Function finisher; try { diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollector.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollector.java index 34132a01b3..63452ce3e9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollector.java +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollector.java @@ -36,9 +36,9 @@ public final class ObservableCollectWithCollector extends Observable final Observable source; - final Collector collector; + final Collector collector; - public ObservableCollectWithCollector(Observable source, Collector collector) { + public ObservableCollectWithCollector(Observable source, Collector collector) { this.source = source; this.collector = collector; } @@ -46,7 +46,7 @@ public ObservableCollectWithCollector(Observable source, Collector c @Override protected void subscribeActual(@NonNull Observer observer) { A container; - BiConsumer accumulator; + BiConsumer accumulator; Function finisher; try { diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollectorSingle.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollectorSingle.java index a919b02ab9..9f990a77a5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollectorSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollectorSingle.java @@ -36,9 +36,9 @@ public final class ObservableCollectWithCollectorSingle extends Single< final Observable source; - final Collector collector; + final Collector collector; - public ObservableCollectWithCollectorSingle(Observable source, Collector collector) { + public ObservableCollectWithCollectorSingle(Observable source, Collector collector) { this.source = source; this.collector = collector; } @@ -51,7 +51,7 @@ public Observable fuseToObservable() { @Override protected void subscribeActual(@NonNull SingleObserver observer) { A container; - BiConsumer accumulator; + BiConsumer accumulator; Function finisher; try { From 2eba6f1c35c4396e8ccfa9fef5744dfd897650df Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2020 09:23:35 +0200 Subject: [PATCH 051/521] Bump mockito-core from 3.4.4 to 3.4.6 (#7043) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.4.4 to 3.4.6. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.4.4...v3.4.6) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 59bc72c8f4..ad0148f254 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.0.0" - ext.mockitoVersion = "3.4.4" + ext.mockitoVersion = "3.4.6" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.0" ext.guavaVersion = "29.0-jre" From a8fcaf0bcde19b9ec3b335bfd696cbf3b8beb81c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2020 10:04:59 +0200 Subject: [PATCH 052/521] Bump testng from 7.0.0 to 7.3.0 (#7047) Bumps [testng](https://github.com/cbeust/testng) from 7.0.0 to 7.3.0. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/cbeust/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ad0148f254..08ce0cf788 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" - ext.testNgVersion = "7.0.0" + ext.testNgVersion = "7.3.0" ext.mockitoVersion = "3.4.6" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.0" From 7ed8e1a8931f5fd730805fd8e6299d1357863a47 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Thu, 6 Aug 2020 08:09:46 +0200 Subject: [PATCH 053/521] 3.x: Fix Observable.window (size, skip, overlap) dispose behavior (#7049) --- .../observable/ObservableWindow.java | 52 ++++++----- .../flowable/FlowableWindowWithSizeTest.java | 91 +++++++++++++++++++ .../ObservableWindowWithSizeTest.java | 90 ++++++++++++++++++ 3 files changed, 208 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindow.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindow.java index 3ada9cd93c..1bd0e2f32e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindow.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindow.java @@ -51,18 +51,20 @@ static final class WindowExactObserver final long count; final int capacityHint; + final AtomicBoolean cancelled; + long size; Disposable upstream; UnicastSubject window; - volatile boolean cancelled; - WindowExactObserver(Observer> actual, long count, int capacityHint) { this.downstream = actual; this.count = count; this.capacityHint = capacityHint; + this.cancelled = new AtomicBoolean(); + this.lazySet(1); } @Override @@ -78,7 +80,9 @@ public void onSubscribe(Disposable d) { public void onNext(T t) { UnicastSubject w = window; ObservableWindowSubscribeIntercept intercept = null; - if (w == null && !cancelled) { + if (w == null && !cancelled.get()) { + getAndIncrement(); + w = UnicastSubject.create(capacityHint, this); window = w; intercept = new ObservableWindowSubscribeIntercept<>(w); @@ -92,15 +96,12 @@ public void onNext(T t) { size = 0; window = null; w.onComplete(); - if (cancelled) { - upstream.dispose(); - } } if (intercept != null && intercept.tryAbandon()) { + window = null; w.onComplete(); w = null; - window = null; } } } @@ -127,23 +128,25 @@ public void onComplete() { @Override public void dispose() { - cancelled = true; + if (cancelled.compareAndSet(false, true)) { + run(); + } } @Override public boolean isDisposed() { - return cancelled; + return cancelled.get(); } @Override public void run() { - if (cancelled) { + if (decrementAndGet() == 0) { upstream.dispose(); } } } - static final class WindowSkipObserver extends AtomicBoolean + static final class WindowSkipObserver extends AtomicInteger implements Observer, Disposable, Runnable { private static final long serialVersionUID = 3366976432059579510L; @@ -153,23 +156,23 @@ static final class WindowSkipObserver extends AtomicBoolean final int capacityHint; final ArrayDeque> windows; - long index; + final AtomicBoolean cancelled; - volatile boolean cancelled; + long index; /** Counts how many elements were emitted to the very first window in windows. */ long firstEmission; Disposable upstream; - final AtomicInteger wip = new AtomicInteger(); - WindowSkipObserver(Observer> actual, long count, long skip, int capacityHint) { this.downstream = actual; this.count = count; this.skip = skip; this.capacityHint = capacityHint; this.windows = new ArrayDeque<>(); + this.cancelled = new AtomicBoolean(); + this.lazySet(1); } @Override @@ -191,8 +194,8 @@ public void onNext(T t) { ObservableWindowSubscribeIntercept intercept = null; - if (i % s == 0 && !cancelled) { - wip.getAndIncrement(); + if (i % s == 0 && !cancelled.get()) { + getAndIncrement(); UnicastSubject w = UnicastSubject.create(capacityHint, this); intercept = new ObservableWindowSubscribeIntercept<>(w); ws.offer(w); @@ -207,8 +210,7 @@ public void onNext(T t) { if (c >= count) { ws.poll().onComplete(); - if (ws.isEmpty() && cancelled) { - this.upstream.dispose(); + if (ws.isEmpty() && cancelled.get()) { return; } firstEmission = c - s; @@ -243,20 +245,20 @@ public void onComplete() { @Override public void dispose() { - cancelled = true; + if (cancelled.compareAndSet(false, true)) { + run(); + } } @Override public boolean isDisposed() { - return cancelled; + return cancelled.get(); } @Override public void run() { - if (wip.decrementAndGet() == 0) { - if (cancelled) { - upstream.dispose(); - } + if (decrementAndGet() == 0) { + upstream.dispose(); } } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java index b8ae0b35c9..ab38a160e8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java @@ -25,6 +25,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; @@ -721,4 +722,94 @@ public void moreQueuedClean() { .test(3) .cancel(); } + + @Test + public void cancelWithoutWindowSize() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10) + .test(); + + assertTrue(pp.hasSubscribers()); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void cancelAfterAbandonmentSize() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10) + .test(); + + assertTrue(pp.hasSubscribers()); + + pp.onNext(1); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void cancelWithoutWindowSkip() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10, 15) + .test(); + + assertTrue(pp.hasSubscribers()); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void cancelAfterAbandonmentSkip() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10, 15) + .test(); + + assertTrue(pp.hasSubscribers()); + + pp.onNext(1); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void cancelWithoutWindowOverlap() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10, 5) + .test(); + + assertTrue(pp.hasSubscribers()); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void cancelAfterAbandonmentOverlap() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10, 5) + .test(); + + assertTrue(pp.hasSubscribers()); + + pp.onNext(1); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithSizeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithSizeTest.java index 04099c31cb..6c253eb52c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithSizeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithSizeTest.java @@ -535,4 +535,94 @@ public void accept(Observable v) throws Throwable { inner.get().test().assertResult(1); } + + @Test + public void cancelWithoutWindowSize() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10) + .test(); + + assertTrue(ps.hasObservers()); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void cancelAfterAbandonmentSize() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void cancelWithoutWindowSkip() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10, 15) + .test(); + + assertTrue(ps.hasObservers()); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void cancelAfterAbandonmentSkip() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10, 15) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void cancelWithoutWindowOverlap() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10, 5) + .test(); + + assertTrue(ps.hasObservers()); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void cancelAfterAbandonmentOverlap() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10, 5) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } } From 5cbb6070a2bb53ce2c7185dae4ad69b3b87bc8e8 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Thu, 6 Aug 2020 09:25:54 +0200 Subject: [PATCH 054/521] 3.x: Update marbles of onErrorResume(Next|With) + cleanup (#7051) --- src/main/java/io/reactivex/rxjava3/core/Flowable.java | 2 +- src/main/java/io/reactivex/rxjava3/core/Observable.java | 2 +- .../internal/operators/flowable/FlowableGroupByTest.java | 2 +- .../internal/operators/flowable/FlowableGroupJoinTest.java | 2 +- .../internal/operators/flowable/FlowableJoinTest.java | 2 +- .../operators/observable/ObservableGroupJoinTest.java | 2 +- .../internal/operators/observable/ObservableJoinTest.java | 2 +- .../rxjava3/testsupport/SuppressUndeliverableRule.java | 5 ++--- 8 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 01c3c4438e..f836f4ba54 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -12770,7 +12770,7 @@ public final Flowable onErrorResumeNext(@NonNull Function - * + * *

* By default, when a {@code Publisher} encounters an error that prevents it from emitting the expected item to * its {@link Subscriber}, the {@code Publisher} invokes its {@code Subscriber}'s {@code onError} method, and then quits diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index eabe9012ab..6332eaf793 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -10691,7 +10691,7 @@ public final Observable onErrorResumeNext(@NonNull Function - * + * *

* By default, when an {@code ObservableSource} encounters an error that prevents it from emitting the expected item to * its {@link Observer}, the {@code ObservableSource} invokes its {@code Observer}'s {@code onError} method, and then quits diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java index 1e9ce41786..9512cab905 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java @@ -2667,7 +2667,7 @@ public void issue6974Part2Case2Loop() { static void issue6974RunPart2NoEvict(int groupByBufferSize, int flatMapMaxConcurrency, int groups, boolean notifyOnExplicitEviction) { - + Flowable .range(1, 500_000) .map(i -> i % groups) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java index 2e7eba8d1a..40539769e9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java @@ -79,7 +79,7 @@ public Integer apply(Integer rightValue) throws Throwable { @Before public void before() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); } @Test diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java index 080be1bb59..b74fa0752f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java @@ -55,7 +55,7 @@ public Flowable apply(Integer t1) { @Before public void before() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); } @Test diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java index 2e5af8b72e..67f8eda0b7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java @@ -81,7 +81,7 @@ public Integer apply(Integer rightValue) throws Throwable { @Before public void before() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); } @Test diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java index 54c2822395..a674d0680e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java @@ -54,7 +54,7 @@ public Observable apply(Integer t1) { @Before public void before() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); } @Test diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java index ea013daac5..fa78a23410 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java @@ -30,11 +30,10 @@ */ public class SuppressUndeliverableRule implements TestRule { - private static class SuppressUndeliverableRuleStatement extends Statement { + static final class SuppressUndeliverableRuleStatement extends Statement { private Statement base; - private SuppressUndeliverableRuleStatement(){} - public SuppressUndeliverableRuleStatement(Statement base) { + SuppressUndeliverableRuleStatement(Statement base) { this.base = base; } From e1bd35ad4d51f91bf9e8eb82c72937b863eef897 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2020 09:56:46 +0200 Subject: [PATCH 055/521] Bump build-info-extractor-gradle from 4.16.1 to 4.17.0 (#7053) Bumps build-info-extractor-gradle from 4.16.1 to 4.17.0. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 08ce0cf788..8ced056461 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.1" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" - ext.jfrogExtractorVersion = "4.16.1" + ext.jfrogExtractorVersion = "4.17.0" ext.bndVersion = "5.1.2" ext.checkstyleVersion = "8.26" From 6c64e863b08d3fe0213240ae55ed239708210654 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2020 10:19:22 +0200 Subject: [PATCH 056/521] Bump mockito-core from 3.4.6 to 3.5.0 (#7057) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.4.6 to 3.5.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.4.6...v3.5.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8ced056461..c1c36f5f6c 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.4.6" + ext.mockitoVersion = "3.5.0" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.0" ext.guavaVersion = "29.0-jre" From 298a56fca684abeac86ab12895126bfbf5049260 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2020 10:25:39 +0200 Subject: [PATCH 057/521] Bump build-info-extractor-gradle from 4.17.0 to 4.17.1 (#7059) Bumps build-info-extractor-gradle from 4.17.0 to 4.17.1. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c1c36f5f6c..e70f030c7c 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.1" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" - ext.jfrogExtractorVersion = "4.17.0" + ext.jfrogExtractorVersion = "4.17.1" ext.bndVersion = "5.1.2" ext.checkstyleVersion = "8.26" From 493c71d2f83ee81895598d52a26f133132504888 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2020 10:55:25 +0200 Subject: [PATCH 058/521] Bump mockito-core from 3.5.0 to 3.5.2 (#7060) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.0 to 3.5.2. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.0...v3.5.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e70f030c7c..8d87d80c75 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.5.0" + ext.mockitoVersion = "3.5.2" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.0" ext.guavaVersion = "29.0-jre" From 12e5ee4340f08595978d1684adc91def1cfedc5b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2020 09:48:55 +0200 Subject: [PATCH 059/521] Bump mockito-core from 3.5.2 to 3.5.5 (#7062) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.2 to 3.5.5. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.2...v3.5.5) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8d87d80c75..f2c66ed513 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.5.2" + ext.mockitoVersion = "3.5.5" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.0" ext.guavaVersion = "29.0-jre" From e3b12fd97c30c08b833bd65fa0c5ed6855c8d3da Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2020 09:05:53 +0200 Subject: [PATCH 060/521] Bump mockito-core from 3.5.5 to 3.5.6 (#7063) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.5 to 3.5.6. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.5...v3.5.6) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f2c66ed513..d94627f336 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.5.5" + ext.mockitoVersion = "3.5.6" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.0" ext.guavaVersion = "29.0-jre" From c76ad8ce837810c1b3696129953aedc6edb2fc8f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2020 08:26:45 +0200 Subject: [PATCH 061/521] Bump mockito-core from 3.5.6 to 3.5.7 (#7064) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.6 to 3.5.7. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.6...v3.5.7) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d94627f336..0b1b1b420e 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.5.6" + ext.mockitoVersion = "3.5.7" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.0" ext.guavaVersion = "29.0-jre" From da56e9bb31ff8cb4df81db02046c5269243aa284 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2020 08:41:55 +0200 Subject: [PATCH 062/521] Bump jmh-gradle-plugin from 0.5.0 to 0.5.1 (#7066) Bumps jmh-gradle-plugin from 0.5.0 to 0.5.1. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0b1b1b420e..d3c0c5a982 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { ext.testNgVersion = "7.3.0" ext.mockitoVersion = "3.5.7" ext.jmhLibVersion = "1.21" - ext.jmhGradleVersion = "0.5.0" + ext.jmhGradleVersion = "0.5.1" ext.guavaVersion = "29.0-jre" ext.jacocoVersion = "0.8.4" ext.animalSnifferVersion = "1.5.1" From c15b5a535d146aa2bfe684226f52fc42eb5559f5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2020 08:36:18 +0200 Subject: [PATCH 063/521] Bump build-info-extractor-gradle from 4.17.1 to 4.17.2 (#7067) Bumps build-info-extractor-gradle from 4.17.1 to 4.17.2. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d3c0c5a982..ed8d5d84d9 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.1" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" - ext.jfrogExtractorVersion = "4.17.1" + ext.jfrogExtractorVersion = "4.17.2" ext.bndVersion = "5.1.2" ext.checkstyleVersion = "8.26" From 1ff3cd62091db049b39a2eda0443240e75d6c321 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 08:37:35 +0200 Subject: [PATCH 064/521] Bump mockito-core from 3.5.7 to 3.5.9 (#7069) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.7 to 3.5.9. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.7...v3.5.9) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ed8d5d84d9..905846f41b 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.5.7" + ext.mockitoVersion = "3.5.9" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.1" ext.guavaVersion = "29.0-jre" From 7693126377ac78618dfc8aa232670a692edd37f4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 11:08:25 +0200 Subject: [PATCH 065/521] Bump mockito-core from 3.5.9 to 3.5.10 (#7070) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.9 to 3.5.10. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.9...v3.5.10) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 905846f41b..43b8e208e5 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.5.9" + ext.mockitoVersion = "3.5.10" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.1" ext.guavaVersion = "29.0-jre" From 0f21ba82666e65a54e980638cfb8a0ffe3dfe506 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Sat, 12 Sep 2020 11:44:42 +0200 Subject: [PATCH 066/521] 3.x: fromRunnable/fromAction javadoc improvements (#7071) --- .../reactivex/rxjava3/core/Completable.java | 16 +++++++++---- .../io/reactivex/rxjava3/core/Flowable.java | 18 ++++++++++----- .../java/io/reactivex/rxjava3/core/Maybe.java | 23 +++++++++++++++---- .../io/reactivex/rxjava3/core/Observable.java | 20 ++++++++++------ 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Completable.java b/src/main/java/io/reactivex/rxjava3/core/Completable.java index f261d1a782..c411cae489 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Completable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Completable.java @@ -528,8 +528,8 @@ public static Completable error(@NonNull Throwable throwable) { } /** - * Returns a {@code Completable} instance that runs the given {@link Action} for each subscriber and - * emits either an unchecked exception or simply completes. + * Returns a {@code Completable} instance that runs the given {@link Action} for each {@link CompletableObserver} and + * emits either an exception or simply completes. *

* *

@@ -543,7 +543,7 @@ public static Completable error(@NonNull Throwable throwable) { * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. * *
- * @param action the {@code Action} to run for each subscribing {@link CompletableObserver} + * @param action the {@code Action} to run for each subscribing {@code CompletableObserver} * @return the new {@code Completable} instance * @throws NullPointerException if {@code action} is {@code null} */ @@ -636,14 +636,19 @@ public static Completable fromMaybe(@NonNull MaybeSource maybe) { /** * Returns a {@code Completable} instance that runs the given {@link Runnable} for each {@link CompletableObserver} and - * emits either its exception or simply completes. + * emits either its unchecked exception or simply completes. *

* + *

+ * If the code to be wrapped needs to throw a checked or more broader {@link Throwable} exception, that + * exception has to be converted to an unchecked exception by the wrapped code itself. Alternatively, + * use the {@link #fromAction(Action)} method which allows the wrapped code to throw any {@code Throwable} + * exception and will signal it to observers as-is. *

*
Scheduler:
*
{@code fromRunnable} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If the {@code Runnable} throws an exception, the respective {@link Throwable} is + *
If the {@code Runnable} throws an exception, the respective {@code Throwable} is * delivered to the downstream via {@link CompletableObserver#onError(Throwable)}, * except when the downstream has disposed this {@code Completable} source. * In this latter case, the {@code Throwable} is delivered to the global error handler via @@ -653,6 +658,7 @@ public static Completable fromMaybe(@NonNull MaybeSource maybe) { * @param run the {@code Runnable} to run for each {@code CompletableObserver} * @return the new {@code Completable} instance * @throws NullPointerException if {@code run} is {@code null} + * @see #fromAction(Action) */ @CheckReturnValue @NonNull diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index f836f4ba54..4da8b6fd53 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -2113,7 +2113,7 @@ public static Flowable error(@NonNull Throwable throwable) { } /** - * Returns a {@code Flowable} instance that runs the given {@link Action} for each subscriber and + * Returns a {@code Flowable} instance that runs the given {@link Action} for each {@link Subscriber} and * emits either its exception or simply completes. *

* @@ -2131,7 +2131,7 @@ public static Flowable error(@NonNull Throwable throwable) { *

*
* @param the target type - * @param action the {@code Action} to run for each subscriber + * @param action the {@code Action} to run for each {@code Subscriber} * @return the new {@code Flowable} instance * @throws NullPointerException if {@code action} is {@code null} * @since 3.0.0 @@ -2497,17 +2497,22 @@ public static Flowable fromPublisher(@NonNull Publisher<@NonNull ? extend } /** - * Returns a {@code Flowable} instance that runs the given {@link Runnable} for each subscriber and - * emits either its exception or simply completes. + * Returns a {@code Flowable} instance that runs the given {@link Runnable} for each {@link Subscriber} and + * emits either its unchecked exception or simply completes. *

* + *

+ * If the code to be wrapped needs to throw a checked or more broader {@link Throwable} exception, that + * exception has to be converted to an unchecked exception by the wrapped code itself. Alternatively, + * use the {@link #fromAction(Action)} method which allows the wrapped code to throw any {@code Throwable} + * exception and will signal it to observers as-is. *

*
Backpressure:
*
This source doesn't produce any elements and effectively ignores downstream backpressure.
*
Scheduler:
*
{@code fromRunnable} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If the {@code Runnable} throws an exception, the respective {@link Throwable} is + *
If the {@code Runnable} throws an exception, the respective {@code Throwable} is * delivered to the downstream via {@link Subscriber#onError(Throwable)}, * except when the downstream has canceled the resulting {@code Flowable} source. * In this latter case, the {@code Throwable} is delivered to the global error handler via @@ -2515,10 +2520,11 @@ public static Flowable fromPublisher(@NonNull Publisher<@NonNull ? extend *
*
* @param the target type - * @param run the {@code Runnable} to run for each subscriber + * @param run the {@code Runnable} to run for each {@code Subscriber} * @return the new {@code Flowable} instance * @throws NullPointerException if {@code run} is {@code null} * @since 3.0.0 + * @see #fromAction(Action) */ @CheckReturnValue @NonNull diff --git a/src/main/java/io/reactivex/rxjava3/core/Maybe.java b/src/main/java/io/reactivex/rxjava3/core/Maybe.java index f5ff68247a..161f7a69d1 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Maybe.java +++ b/src/main/java/io/reactivex/rxjava3/core/Maybe.java @@ -967,7 +967,7 @@ public static Maybe error(@NonNull Supplier supplier } /** - * Returns a {@code Maybe} instance that runs the given {@link Action} for each observer and + * Returns a {@code Maybe} instance that runs the given {@link Action} for each {@link MaybeObserver} and * emits either its exception or simply completes. *

* @@ -983,7 +983,7 @@ public static Maybe error(@NonNull Supplier supplier * * * @param the target type - * @param action the {@code Action} to run for each observer + * @param action the {@code Action} to run for each {@code MaybeObserver} * @return the new {@code Maybe} instance * @throws NullPointerException if {@code action} is {@code null} */ @@ -1208,18 +1208,31 @@ public static Maybe fromPublisher(@NonNull Publisher source) { } /** - * Returns a {@code Maybe} instance that runs the given {@link Runnable} for each observer and - * emits either its exception or simply completes. + * Returns a {@code Maybe} instance that runs the given {@link Runnable} for each {@link MaybeObserver} and + * emits either its unchecked exception or simply completes. *

* + *

+ * If the code to be wrapped needs to throw a checked or more broader {@link Throwable} exception, that + * exception has to be converted to an unchecked exception by the wrapped code itself. Alternatively, + * use the {@link #fromAction(Action)} method which allows the wrapped code to throw any {@code Throwable} + * exception and will signal it to observers as-is. *

*
Scheduler:
*
{@code fromRunnable} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the {@code Runnable} throws an exception, the respective {@code Throwable} is + * delivered to the downstream via {@link MaybeObserver#onError(Throwable)}, + * except when the downstream has disposed this {@code Maybe} source. + * In this latter case, the {@code Throwable} is delivered to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. + *
*
* @param the target type - * @param run the {@code Runnable} to run for each observer + * @param run the {@code Runnable} to run for each {@code MaybeObserver} * @return the new {@code Maybe} instance * @throws NullPointerException if {@code run} is {@code null} + * @see #fromAction(Action) */ @CheckReturnValue @NonNull diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index 6332eaf793..366e2bdd51 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -1855,10 +1855,10 @@ public static Observable error(@NonNull Throwable throwable) { } /** - * Returns an {@code Observable} instance that runs the given {@link Action} for each subscriber and + * Returns an {@code Observable} instance that runs the given {@link Action} for each {@link Observer} and * emits either its exception or simply completes. *

- * + * *

*
Scheduler:
*
{@code fromAction} does not operate by default on a particular {@link Scheduler}.
@@ -1871,7 +1871,7 @@ public static Observable error(@NonNull Throwable throwable) { * *
* @param the target type - * @param action the {@code Action} to run for each subscriber + * @param action the {@code Action} to run for each {@code Observer} * @return the new {@code Observable} instance * @throws NullPointerException if {@code action} is {@code null} * @since 3.0.0 @@ -2145,15 +2145,20 @@ public static Observable fromPublisher(@NonNull Publisher<@NonNull ? exte } /** - * Returns an {@code Observable} instance that runs the given {@link Runnable} for each observer and - * emits either its exception or simply completes. + * Returns an {@code Observable} instance that runs the given {@link Runnable} for each {@link Observer} and + * emits either its unchecked exception or simply completes. *

* + *

+ * If the code to be wrapped needs to throw a checked or more broader {@link Throwable} exception, that + * exception has to be converted to an unchecked exception by the wrapped code itself. Alternatively, + * use the {@link #fromAction(Action)} method which allows the wrapped code to throw any {@code Throwable} + * exception and will signal it to observers as-is. *

*
Scheduler:
*
{@code fromRunnable} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If the {@code Runnable} throws an exception, the respective {@link Throwable} is + *
If the {@code Runnable} throws an exception, the respective {@code Throwable} is * delivered to the downstream via {@link Observer#onError(Throwable)}, * except when the downstream has canceled the resulting {@code Observable} source. * In this latter case, the {@code Throwable} is delivered to the global error handler via @@ -2161,10 +2166,11 @@ public static Observable fromPublisher(@NonNull Publisher<@NonNull ? exte *
*
* @param the target type - * @param run the {@code Runnable} to run for each observer + * @param run the {@code Runnable} to run for each {@code Observer} * @return the new {@code Observable} instance * @throws NullPointerException if {@code run} is {@code null} * @since 3.0.0 + * @see #fromAction(Action) */ @CheckReturnValue @NonNull From ffe2fccfd696a64e3d085ab70cd2992e9e6046df Mon Sep 17 00:00:00 2001 From: David Karnok Date: Sat, 12 Sep 2020 12:13:57 +0200 Subject: [PATCH 067/521] 3.x: Patch out duplicate @NonNull annotation in generated javadocs (#7073) * 3.x: Patch out duplicate @NonNull annotation in generated javadocs * Replace different pattern for Javadocs 8 --- gradle/javadoc_cleanup.gradle | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/gradle/javadoc_cleanup.gradle b/gradle/javadoc_cleanup.gradle index 12216464a7..64f6437288 100644 --- a/gradle/javadoc_cleanup.gradle +++ b/gradle/javadoc_cleanup.gradle @@ -29,6 +29,31 @@ def fixJavadocFile(file) { // lots of spaces after the @Nullable annotations fileContents = fileContents.replaceAll("@Nullable\\s{4,}", "@Nullable "); + // javadoc bug: duplicates the link to @NonNull for some reason + def nonNullText1 = "@NonNull"; + + fileContents = fileContents.replace(nonNullText1 + " " + nonNullText1, nonNullText1); + fileContents = fileContents.replace(nonNullText1 + "\n " + nonNullText1, nonNullText1); + fileContents = fileContents.replace(nonNullText1 + "\r\n " + nonNullText1, nonNullText1); + + def nonNullText2 = "@NonNull"; + fileContents = fileContents.replace(nonNullText2 + " " + nonNullText2, nonNullText2); + fileContents = fileContents.replace(nonNullText2 + "\n " + nonNullText2, nonNullText2); + fileContents = fileContents.replace(nonNullText2 + "\r\n " + nonNullText2, nonNullText2); + + // javadoc bug: duplicates the link to @Nullable for some reason + def nullableText1 = "@Nullable"; + + fileContents = fileContents.replace(nullableText1 + " " + nullableText1, nullableText1); + fileContents = fileContents.replace(nullableText1 + "\n " + nullableText1, nullableText1); + fileContents = fileContents.replace(nullableText1 + "\r\n " + nullableText1, nullableText1); + + def nullableText2 = "@Nullable"; + + fileContents = fileContents.replace(nullableText2 + " " + nullableText2, nullableText2); + fileContents = fileContents.replace(nullableText2 + "\n " + nullableText2, nullableText2); + fileContents = fileContents.replace(nullableText2 + "\r\n " + nullableText2, nullableText2); + file.setText(fileContents, 'UTF-8'); } From 6b48855b1fdcefeca9ed9e0c112140c23bd42f6e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2020 08:20:52 +0200 Subject: [PATCH 068/521] Bump mockito-core from 3.5.10 to 3.5.11 (#7077) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.10 to 3.5.11. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.10...v3.5.11) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 43b8e208e5..998b5eb1c7 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.5.10" + ext.mockitoVersion = "3.5.11" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.1" ext.guavaVersion = "29.0-jre" From fe71f6a22847429163704e076e8447c674ee7571 Mon Sep 17 00:00:00 2001 From: Tasuku Nakagawa <38446259+T45K@users.noreply.github.com> Date: Mon, 21 Sep 2020 23:50:32 +0900 Subject: [PATCH 069/521] s/error/fail/ (#7080) --- .../operators/observable/ObservableZipIterable.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterable.java index d775c58515..c1881bdca3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterable.java @@ -112,7 +112,7 @@ public void onNext(T t) { u = Objects.requireNonNull(iterator.next(), "The iterator returned a null value"); } catch (Throwable e) { Exceptions.throwIfFatal(e); - error(e); + fail(e); return; } @@ -121,7 +121,7 @@ public void onNext(T t) { v = Objects.requireNonNull(zipper.apply(t, u), "The zipper function returned a null value"); } catch (Throwable e) { Exceptions.throwIfFatal(e); - error(e); + fail(e); return; } @@ -133,7 +133,7 @@ public void onNext(T t) { b = iterator.hasNext(); } catch (Throwable e) { Exceptions.throwIfFatal(e); - error(e); + fail(e); return; } @@ -144,7 +144,7 @@ public void onNext(T t) { } } - void error(Throwable e) { + void fail(Throwable e) { done = true; upstream.dispose(); downstream.onError(e); From 0690c7cc26bc08fbd54336b20dcd2e3d7b3d8fcf Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 23 Sep 2020 10:47:29 +0200 Subject: [PATCH 070/521] 3.x: Fix toFlowable(ERROR) not cancelling on MBE (#7083) --- .../flowable/FlowableOnBackpressureError.java | 1 + .../FlowableOnBackpressureErrorTest.java | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureError.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureError.java index b85e1281fb..ea0a92af52 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureError.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureError.java @@ -65,6 +65,7 @@ public void onNext(T t) { downstream.onNext(t); BackpressureHelper.produced(this, 1); } else { + upstream.cancel(); onError(new MissingBackpressureException("could not emit value due to lack of requests")); } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureErrorTest.java index 76fad7bf49..ef65662586 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureErrorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureErrorTest.java @@ -13,11 +13,16 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import static org.junit.Assert.*; + import org.junit.Test; import org.reactivestreams.Publisher; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.MissingBackpressureException; import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; public class FlowableOnBackpressureErrorTest extends RxJavaTest { @@ -51,4 +56,20 @@ public Object apply(Flowable f) throws Exception { } }, false, 1, 1, 1); } + + @Test + public void overflowCancels() { + PublishSubject ps = PublishSubject.create(); + + TestSubscriber ts = ps.toFlowable(BackpressureStrategy.ERROR) + .test(0L); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + assertFalse(ps.hasObservers()); + + ts.assertFailure(MissingBackpressureException.class); + } } From 88697a4fa769aa4a81ba5ed14b3be6dad93441ed Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2020 08:30:32 +0200 Subject: [PATCH 071/521] Bump mockito-core from 3.5.11 to 3.5.13 (#7086) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.11 to 3.5.13. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.11...v3.5.13) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 998b5eb1c7..d05812fc8d 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.5.11" + ext.mockitoVersion = "3.5.13" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.1" ext.guavaVersion = "29.0-jre" From 0668d042b47d3585f856af831bd3b1e1742c080f Mon Sep 17 00:00:00 2001 From: David Karnok Date: Mon, 5 Oct 2020 14:07:15 +0200 Subject: [PATCH 072/521] 3.x: Fix Flowable.concatMap backpressure w/ scalars (#7089) --- .../operators/flowable/FlowableConcatMap.java | 18 +++--- .../flowable/FlowableConcatMapScheduler.java | 4 +- .../FlowableConcatMapSchedulerTest.java | 50 ++++++++++++++++ .../flowable/FlowableConcatMapTest.java | 58 +++++++++++++++++-- 4 files changed, 116 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java index aea261f5aa..9409bc7a2e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java @@ -13,7 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; import java.util.Objects; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -308,7 +308,7 @@ void drain() { continue; } else { active = true; - inner.setSubscription(new WeakScalarSubscription<>(vr, inner)); + inner.setSubscription(new SimpleScalarSubscription<>(vr, inner)); } } else { @@ -325,20 +325,22 @@ void drain() { } } - static final class WeakScalarSubscription implements Subscription { + static final class SimpleScalarSubscription + extends AtomicBoolean + implements Subscription { + private static final long serialVersionUID = -7606889335172043256L; + final Subscriber downstream; final T value; - boolean once; - WeakScalarSubscription(T value, Subscriber downstream) { + SimpleScalarSubscription(T value, Subscriber downstream) { this.value = value; this.downstream = downstream; } @Override public void request(long n) { - if (n > 0 && !once) { - once = true; + if (n > 0L && compareAndSet(false, true)) { Subscriber a = downstream; a.onNext(value); a.onComplete(); @@ -507,7 +509,7 @@ void drain() { continue; } else { active = true; - inner.setSubscription(new WeakScalarSubscription<>(vr, inner)); + inner.setSubscription(new SimpleScalarSubscription<>(vr, inner)); } } else { active = true; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java index 3dfcd38b9d..fba627c7ed 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java @@ -345,7 +345,7 @@ public void run() { continue; } else { active = true; - inner.setSubscription(new WeakScalarSubscription<>(vr, inner)); + inner.setSubscription(new SimpleScalarSubscription<>(vr, inner)); } } else { @@ -528,7 +528,7 @@ public void run() { continue; } else { active = true; - inner.setSubscription(new WeakScalarSubscription<>(vr, inner)); + inner.setSubscription(new SimpleScalarSubscription<>(vr, inner)); } } else { active = true; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java index bb01b50a20..adb92ef08f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java @@ -66,6 +66,56 @@ public Publisher apply(String v) .assertResult("RxSingleScheduler"); } + @Test + public void innerScalarRequestRace() { + Flowable just = Flowable.just(1); + int n = 1000; + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor> source = PublishProcessor.create(); + + TestSubscriber ts = source + .concatMap(v -> v, n + 1, ImmediateThinScheduler.INSTANCE) + .test(1L); + + TestHelper.race(() -> { + for (int j = 0; j < n; j++) { + source.onNext(just); + } + }, () -> { + for (int j = 0; j < n; j++) { + ts.request(1); + } + }); + + ts.assertValueCount(n); + } + } + + @Test + public void innerScalarRequestRaceDelayError() { + Flowable just = Flowable.just(1); + int n = 1000; + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor> source = PublishProcessor.create(); + + TestSubscriber ts = source + .concatMapDelayError(v -> v, true, n + 1, ImmediateThinScheduler.INSTANCE) + .test(1L); + + TestHelper.race(() -> { + for (int j = 0; j < n; j++) { + source.onNext(just); + } + }, () -> { + for (int j = 0; j < n; j++) { + ts.request(1); + } + }); + + ts.assertValueCount(n); + } + } + @Test public void boundaryFusionDelayError() { Flowable.range(1, 10000) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java index 4ff32a9eb4..d195c054e1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java @@ -24,8 +24,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.operators.flowable.FlowableConcatMap.WeakScalarSubscription; -import io.reactivex.rxjava3.processors.UnicastProcessor; +import io.reactivex.rxjava3.internal.operators.flowable.FlowableConcatMap.SimpleScalarSubscription; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -33,9 +33,9 @@ public class FlowableConcatMapTest extends RxJavaTest { @Test - public void weakSubscriptionRequest() { + public void simpleSubscriptionRequest() { TestSubscriber ts = new TestSubscriber<>(0); - WeakScalarSubscription ws = new WeakScalarSubscription<>(1, ts); + SimpleScalarSubscription ws = new SimpleScalarSubscription<>(1, ts); ts.onSubscribe(ws); ws.request(0); @@ -79,6 +79,56 @@ public Publisher apply(String v) .assertResult("RxSingleScheduler"); } + @Test + public void innerScalarRequestRace() { + Flowable just = Flowable.just(1); + int n = 1000; + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor> source = PublishProcessor.create(); + + TestSubscriber ts = source + .concatMap(v -> v, n + 1) + .test(1L); + + TestHelper.race(() -> { + for (int j = 0; j < n; j++) { + source.onNext(just); + } + }, () -> { + for (int j = 0; j < n; j++) { + ts.request(1); + } + }); + + ts.assertValueCount(n); + } + } + + @Test + public void innerScalarRequestRaceDelayError() { + Flowable just = Flowable.just(1); + int n = 1000; + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor> source = PublishProcessor.create(); + + TestSubscriber ts = source + .concatMapDelayError(v -> v, true, n + 1) + .test(1L); + + TestHelper.race(() -> { + for (int j = 0; j < n; j++) { + source.onNext(just); + } + }, () -> { + for (int j = 0; j < n; j++) { + ts.request(1); + } + }); + + ts.assertValueCount(n); + } + } + @Test public void boundaryFusionDelayError() { Flowable.range(1, 10000) From 35702ec5e64e8adc2ce6f9828f9af8f5fde42bbc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2020 14:45:00 +0200 Subject: [PATCH 073/521] Bump jmh-gradle-plugin from 0.5.1 to 0.5.2 (#7082) Bumps jmh-gradle-plugin from 0.5.1 to 0.5.2. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d05812fc8d..7b31012f8e 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { ext.testNgVersion = "7.3.0" ext.mockitoVersion = "3.5.13" ext.jmhLibVersion = "1.21" - ext.jmhGradleVersion = "0.5.1" + ext.jmhGradleVersion = "0.5.2" ext.guavaVersion = "29.0-jre" ext.jacocoVersion = "0.8.4" ext.animalSnifferVersion = "1.5.1" From 82a218a5e0bb7f8788e4e6b0b54e4de9cd28e9ea Mon Sep 17 00:00:00 2001 From: David Karnok Date: Tue, 6 Oct 2020 22:58:22 +0200 Subject: [PATCH 074/521] Clarify the documentation for scan operators (#7093) * Clarify the documentation for scan operators * Fix typo Co-authored-by: David Andrews --- .../io/reactivex/rxjava3/core/Flowable.java | 21 ++++++++----------- .../io/reactivex/rxjava3/core/Observable.java | 21 ++++++++----------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 4da8b6fd53..b5c52c2583 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -14675,10 +14675,9 @@ public final Flowable sample(@NonNull Publisher sampler, boolean emitL } /** - * Returns a {@code Flowable} that applies a specified accumulator function to the first item emitted by the current - * {@code Flowable}, then feeds the result of that function along with the second item emitted by the current - * {@code Floawble} into the same function, and so on until all items have been emitted by the current {@code Flowable}, - * emitting the result of each of these iterations. + * Returns a {@code Flowable} that emits the first value emitted by the current {@code Flowable}, then emits one value + * for each subsequent value emitted by the current {@code Flowable}. Each emission after the first is the result of + * applying the specified accumulator function to the previous emission and the corresponding value from the current @{code Flowable}. *

* *

@@ -14709,10 +14708,9 @@ public final Flowable scan(@NonNull BiFunction accumulator) { } /** - * Returns a {@code Flowable} that applies a specified accumulator function to the first item emitted by the current - * {@code Flowable} and a seed value, then feeds the result of that function along with the second item emitted by - * the current {@code Flowable} into the same function, and so on until all items have been emitted by the current - * {@code Flowable}, emitting the result of each of these iterations. + * Returns a {@code Flowable} that emits the provided initial (seed) value, then emits one value for each value emitted + * by the current {@code Flowable}. Each emission after the first is the result of applying the specified accumulator + * function to the previous emission and the corresponding value from the current @{code Flowable}. *

* *

@@ -14763,10 +14761,9 @@ public final Flowable scan(@NonNull BiFunction accumulator) { } /** - * Returns a {@code Flowable} that applies a specified accumulator function to the first item emitted by the current - * {@code Flowable} and a seed value, then feeds the result of that function along with the second item emitted by - * the current {@code Flowable} into the same function, and so on until all items have been emitted by the current - * {@code Flowable}, emitting the result of each of these iterations. + * Returns a {@code Flowable} that emits the provided initial (seed) value, then emits one value for each value emitted + * by the current {@code Flowable}. Each emission after the first is the result of applying the specified accumulator + * function to the previous emission and the corresponding value from the current @{code Flowable}. *

* *

diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index 366e2bdd51..3332b624b2 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -12202,10 +12202,9 @@ public final Observable sample(@NonNull ObservableSource sampler, bool } /** - * Returns an {@code Observable} that applies a specified accumulator function to the first item emitted by the current - * {@code Observable}, then feeds the result of that function along with the second item emitted by the current - * {@code Observable} into the same function, and so on until all items have been emitted by the current {@code Observable}, - * emitting the result of each of these iterations. + * Returns an {@code Observable} that emits the first value emitted by the current {@code Observable}, then emits one value + * for each subsequent value emitted by the current {@code Observable}. Each emission after the first is the result of + * applying the specified accumulator function to the previous emission and the corresponding value from the current @{code Observable}. *

* *

@@ -12232,10 +12231,9 @@ public final Observable scan(@NonNull BiFunction accumulator) { } /** - * Returns an {@code Observable} that applies a specified accumulator function to the first item emitted by the current - * {@code Observable} and a seed value, then feeds the result of that function along with the second item emitted by - * the current {@code Observable} into the same function, and so on until all items have been emitted by the current - * {@code Observable}, emitting the result of each of these iterations. + * Returns an {@code Observable} that emits the provided initial (seed) value, then emits one value for each value emitted + * by the current {@code Observable}. Each emission after the first is the result of applying the specified accumulator + * function to the previous emission and the corresponding value from the current @{code Observable}. *

* *

@@ -12282,10 +12280,9 @@ public final Observable scan(@NonNull R initialValue, @NonNull BiFunction } /** - * Returns an {@code Observable} that applies a specified accumulator function to the first item emitted by the current - * {@code Observable} and a seed value, then feeds the result of that function along with the second item emitted by - * the current {@code Observable} into the same function, and so on until all items have been emitted by the current - * {@code Observable}, emitting the result of each of these iterations. + * Returns an {@code Observable} that emits the provided initial (seed) value, then emits one value for each value emitted + * by the current {@code Observable}. Each emission after the first is the result of applying the specified accumulator + * function to the previous emission and the corresponding value from the current @{code Observable}. *

* *

From fb45ce83c4e08d46b754d5a86efcc5ae38f84e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Karnok?= Date: Tue, 6 Oct 2020 23:00:02 +0200 Subject: [PATCH 075/521] Fix minor typos of @{code --- src/main/java/io/reactivex/rxjava3/core/Flowable.java | 6 +++--- src/main/java/io/reactivex/rxjava3/core/Observable.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index b5c52c2583..a8c873bbc4 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -14677,7 +14677,7 @@ public final Flowable sample(@NonNull Publisher sampler, boolean emitL /** * Returns a {@code Flowable} that emits the first value emitted by the current {@code Flowable}, then emits one value * for each subsequent value emitted by the current {@code Flowable}. Each emission after the first is the result of - * applying the specified accumulator function to the previous emission and the corresponding value from the current @{code Flowable}. + * applying the specified accumulator function to the previous emission and the corresponding value from the current {@code Flowable}. *

* *

@@ -14710,7 +14710,7 @@ public final Flowable scan(@NonNull BiFunction accumulator) { /** * Returns a {@code Flowable} that emits the provided initial (seed) value, then emits one value for each value emitted * by the current {@code Flowable}. Each emission after the first is the result of applying the specified accumulator - * function to the previous emission and the corresponding value from the current @{code Flowable}. + * function to the previous emission and the corresponding value from the current {@code Flowable}. *

* *

@@ -14763,7 +14763,7 @@ public final Flowable scan(@NonNull BiFunction accumulator) { /** * Returns a {@code Flowable} that emits the provided initial (seed) value, then emits one value for each value emitted * by the current {@code Flowable}. Each emission after the first is the result of applying the specified accumulator - * function to the previous emission and the corresponding value from the current @{code Flowable}. + * function to the previous emission and the corresponding value from the current {@code Flowable}. *

* *

diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index 3332b624b2..a1e34e9309 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -12204,7 +12204,7 @@ public final Observable sample(@NonNull ObservableSource sampler, bool /** * Returns an {@code Observable} that emits the first value emitted by the current {@code Observable}, then emits one value * for each subsequent value emitted by the current {@code Observable}. Each emission after the first is the result of - * applying the specified accumulator function to the previous emission and the corresponding value from the current @{code Observable}. + * applying the specified accumulator function to the previous emission and the corresponding value from the current {@code Observable}. *

* *

@@ -12233,7 +12233,7 @@ public final Observable scan(@NonNull BiFunction accumulator) { /** * Returns an {@code Observable} that emits the provided initial (seed) value, then emits one value for each value emitted * by the current {@code Observable}. Each emission after the first is the result of applying the specified accumulator - * function to the previous emission and the corresponding value from the current @{code Observable}. + * function to the previous emission and the corresponding value from the current {@code Observable}. *

* *

@@ -12282,7 +12282,7 @@ public final Observable scan(@NonNull R initialValue, @NonNull BiFunction /** * Returns an {@code Observable} that emits the provided initial (seed) value, then emits one value for each value emitted * by the current {@code Observable}. Each emission after the first is the result of applying the specified accumulator - * function to the previous emission and the corresponding value from the current @{code Observable}. + * function to the previous emission and the corresponding value from the current {@code Observable}. *

* *

From 4394f5016fcd7b37049ea727dca91755054d090a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 12:12:03 +0200 Subject: [PATCH 076/521] Bump junit from 4.13 to 4.13.1 (#7094) Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7b31012f8e..64af614706 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { // --------------------------------------- ext.reactiveStreamsVersion = "1.0.3" - ext.junitVersion = "4.13" + ext.junitVersion = "4.13.1" ext.testNgVersion = "7.3.0" ext.mockitoVersion = "3.5.13" ext.jmhLibVersion = "1.21" From 7106980947ce9b2e8d1a17b6081d474f39d0f1c2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 16 Oct 2020 09:45:01 +0200 Subject: [PATCH 077/521] Bump biz.aQute.bnd.gradle from 5.1.2 to 5.2.0 (#7097) Bumps [biz.aQute.bnd.gradle](https://github.com/bndtools/bnd) from 5.1.2 to 5.2.0. - [Release notes](https://github.com/bndtools/bnd/releases) - [Changelog](https://github.com/bndtools/bnd/blob/master/docs/ADDING_RELEASE_DOCS.md) - [Commits](https://github.com/bndtools/bnd/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 64af614706..d782a3089f 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" ext.jfrogExtractorVersion = "4.17.2" - ext.bndVersion = "5.1.2" + ext.bndVersion = "5.2.0" ext.checkstyleVersion = "8.26" // -------------------------------------- From 8511da4e0130d67a7b2ae369349868004e153262 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 19 Oct 2020 09:26:10 +0200 Subject: [PATCH 078/521] Bump guava from 29.0-jre to 30.0-jre (#7098) Bumps [guava](https://github.com/google/guava) from 29.0-jre to 30.0-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d782a3089f..6776c5fb24 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { ext.mockitoVersion = "3.5.13" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.2" - ext.guavaVersion = "29.0-jre" + ext.guavaVersion = "30.0-jre" ext.jacocoVersion = "0.8.4" ext.animalSnifferVersion = "1.5.1" ext.licenseVersion = "0.15.0" From d4ab2532d5914ccbb075fd540c7640578ab54044 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 20 Oct 2020 10:25:55 +0200 Subject: [PATCH 079/521] Bump mockito-core from 3.5.13 to 3.5.15 (#7099) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.13 to 3.5.15. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.13...v3.5.15) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6776c5fb24..1c5e189116 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13.1" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.5.13" + ext.mockitoVersion = "3.5.15" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.2" ext.guavaVersion = "30.0-jre" From d0aa30d5f7a070c5a499ac89028d53f14e9adb74 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2020 08:27:33 +0100 Subject: [PATCH 080/521] Bump mockito-core from 3.5.15 to 3.6.0 (#7103) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.15 to 3.6.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.5.15...v3.6.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1c5e189116..2119a0951f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13.1" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.5.15" + ext.mockitoVersion = "3.6.0" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.2" ext.guavaVersion = "30.0-jre" From 10aedf056ab4be2363b56855ac198bf2be3f83d1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2020 08:28:08 +0100 Subject: [PATCH 081/521] Bump build-info-extractor-gradle from 4.17.2 to 4.18.0 (#7102) Bumps build-info-extractor-gradle from 4.17.2 to 4.18.0. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2119a0951f..257fd8748d 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.1" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" - ext.jfrogExtractorVersion = "4.17.2" + ext.jfrogExtractorVersion = "4.18.0" ext.bndVersion = "5.2.0" ext.checkstyleVersion = "8.26" From ca3a21ae2e79ead471def5dd4c4211ff4fbe1298 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 30 Oct 2020 08:31:42 +0100 Subject: [PATCH 082/521] Switch to travis-ci.com as the org goes away (#7104) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed7ad4a5de..bd1b51f5fa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # RxJava: Reactive Extensions for the JVM - + [![codecov.io](http://codecov.io/github/ReactiveX/RxJava/coverage.svg?branch=3.x)](https://codecov.io/gh/ReactiveX/RxJava/branch/3.x) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.reactivex.rxjava3/rxjava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.reactivex.rxjava3/rxjava) From 57fd72ee683eb1baaed1196bc8feac07850f7146 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 5 Nov 2020 08:49:07 +0100 Subject: [PATCH 083/521] Bump gradle-animalsniffer-plugin from 1.5.1 to 1.5.2 (#7106) Bumps gradle-animalsniffer-plugin from 1.5.1 to 1.5.2. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 257fd8748d..0b72b82524 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { ext.jmhGradleVersion = "0.5.2" ext.guavaVersion = "30.0-jre" ext.jacocoVersion = "0.8.4" - ext.animalSnifferVersion = "1.5.1" + ext.animalSnifferVersion = "1.5.2" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" ext.jfrogExtractorVersion = "4.18.0" From 78b29f7967cd7589c3e53a4a8a73ad2aadb30eff Mon Sep 17 00:00:00 2001 From: Fabio Barata Date: Thu, 12 Nov 2020 10:13:13 +0000 Subject: [PATCH 084/521] 3.x: Flowable scan/scanWith backpressure documentation update (#7110) * 3.x: Flowable scan/scanWith backpressure documentation update Documented Flowable initial value scan variants upstream consumption pattern as it is different from the variant with no initial value. * 3.x: Flowable scan/scanWith backpressure documentation merge

sections --- src/main/java/io/reactivex/rxjava3/core/Flowable.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index a8c873bbc4..364e83fd58 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -14735,7 +14735,9 @@ public final Flowable scan(@NonNull BiFunction accumulator) { *
*
Backpressure:
*
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. - * Violating this expectation, a {@link MissingBackpressureException} may get signaled somewhere downstream.
+ * Violating this expectation, a {@link MissingBackpressureException} may get signaled somewhere downstream. + * The downstream request pattern is not preserved across this operator. + * The upstream is requested {@link #bufferSize()} - 1 upfront and 75% of {@link #bufferSize()} thereafter.
*
Scheduler:
*
{@code scan} does not operate by default on a particular {@link Scheduler}.
* @@ -14774,7 +14776,9 @@ public final Flowable scan(@NonNull BiFunction accumulator) { *
*
Backpressure:
*
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. - * Violating this expectation, a {@link MissingBackpressureException} may get signaled somewhere downstream.
+ * Violating this expectation, a {@link MissingBackpressureException} may get signaled somewhere downstream. + * The downstream request pattern is not preserved across this operator. + * The upstream is requested {@link #bufferSize()} - 1 upfront and 75% of {@link #bufferSize()} thereafter. *
Scheduler:
*
{@code scanWith} does not operate by default on a particular {@link Scheduler}.
*
From 5b408f6a09a2f72761005b72f2c06c70eef32fbb Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 20 Nov 2020 16:35:16 +0100 Subject: [PATCH 085/521] 3.x: Fix source locator code to support GitHub Actions folder layout (#7113) --- .../rxjava3/testsupport/TestHelper.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java b/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java index dfaccd6ac5..1963a38459 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java @@ -3503,18 +3503,26 @@ public static File findSource(String baseClassName, String parentPackage) throws parentPackage = parentPackage.replace(".", "/"); // System.out.println(path); - int i = path.toLowerCase().indexOf("/rxjava"); - if (i < 0) { - System.out.println("Can't find the base RxJava directory"); - return null; - } - - // find end of any potential postfix to /RxJava - int j = path.indexOf("/", i + 6); + // Locate the src/main/java directory + String p = null; + while (true) { + int idx = path.lastIndexOf("/"); + if (idx < 0) { + break; + } + path = path.substring(0, idx); + String check = path + "/src/main/java"; - String basePackage = path.substring(0, j + 1) + "src/main/java"; + if (new File(check).exists()) { + p = check + "/" + parentPackage + "/" + baseClassName + ".java"; + break; + } + } - String p = basePackage + "/" + parentPackage + "/" + baseClassName + ".java"; + if (p == null) { + System.err.println("Unable to locate the RxJava sources"); + return null; + } File f = new File(p); From c816e6f5f4dfd18743ee6ff1ad7e42c9beb2cf01 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 20 Nov 2020 18:28:22 +0100 Subject: [PATCH 086/521] 3.x Switch from Travis CI to GitHub Actions (#7114) * 3.x: Switch to GitHub Actions * Update action names --- .github/workflows/gradle_branch.yml | 31 +++++++++++++++++ .github/workflows/gradle_pr.yml | 31 +++++++++++++++++ .github/workflows/gradle_release.yml | 49 +++++++++++++++++++++++++++ .github/workflows/gradle_snapshot.yml | 45 ++++++++++++++++++++++++ .travis.yml | 29 ---------------- build.gradle | 2 +- gradle/buildViaTravis.sh | 29 ---------------- gradle/push_javadoc.sh | 28 +++++---------- 8 files changed, 165 insertions(+), 79 deletions(-) create mode 100644 .github/workflows/gradle_branch.yml create mode 100644 .github/workflows/gradle_pr.yml create mode 100644 .github/workflows/gradle_release.yml create mode 100644 .github/workflows/gradle_snapshot.yml delete mode 100644 .travis.yml delete mode 100755 gradle/buildViaTravis.sh diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml new file mode 100644 index 0000000000..85594b3549 --- /dev/null +++ b/.github/workflows/gradle_branch.yml @@ -0,0 +1,31 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Branch + +on: + push: + branches-ignore: [ '3.x' ] + +jobs: + build: + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build branch without snapshot + run: ./gradlew -PreleaseMode=pr build --stacktrace + - name: Upload to Codecov + uses: codecov/codecov-action@v1 \ No newline at end of file diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml new file mode 100644 index 0000000000..bb10853960 --- /dev/null +++ b/.github/workflows/gradle_pr.yml @@ -0,0 +1,31 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: PR + +on: + pull_request: + branches: [ 3.x, 3.x_GHA ] + +jobs: + build: + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build PR + run: ./gradlew -PreleaseMode=pr build --stacktrace + - name: Upload to Codecov + uses: codecov/codecov-action@v1 \ No newline at end of file diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml new file mode 100644 index 0000000000..6e4457a883 --- /dev/null +++ b/.github/workflows/gradle_release.yml @@ -0,0 +1,49 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Release + +on: + release: + branches: [ '3.x' ] + tags: + - 'v3.*.*' + +jobs: + build: + + runs-on: ubuntu-latest + env: + # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions + # ------------------------------------------------------------------------------ + bintrayUser: ${{ secrets.BINTRAY_USER }} + bintrayKey: ${{ secrets.BINTRAY_KEY }} + sonatypeUsername: ${{ secrets.SONATYPE_USER }} + sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }} + JAVADOCS_TOKEN: ${{ secrets.JAVADOCS_TOKEN }} + # ------------------------------------------------------------------------------ + CI_BUILD_NUMBER: ${{ github.run_number }} + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Grant execute permission for push + run: chmod +x push_javadoc.sh + - name: Extract version tag + run: echo "BUILD_TAG=${GITHUB_REF:10}" >> $GITHUB_ENV + - name: Build and Release + run: ./gradlew -PreleaseMode=full -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" build --stacktrace + - name: Upload to Codecov + uses: codecov/codecov-action@v1 + - name: Push Javadocs + run: ./push_javadoc.sh \ No newline at end of file diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml new file mode 100644 index 0000000000..a6042fe8ec --- /dev/null +++ b/.github/workflows/gradle_snapshot.yml @@ -0,0 +1,45 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Snapshot + +on: + push: + branches: [ '3.x' ] + +jobs: + build: + + runs-on: ubuntu-latest + env: + # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions + # ------------------------------------------------------------------------------ + bintrayUser: ${{ secrets.BINTRAY_USER }} + bintrayKey: ${{ secrets.BINTRAY_KEY }} + sonatypeUsername: ${{ secrets.SONATYPE_USER }} + sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }} + JAVADOCS_TOKEN: ${{ secrets.JAVADOCS_TOKEN }} + # ------------------------------------------------------------------------------ + CI_BUILD_NUMBER: ${{ github.run_number }} + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Grant execute permission for push + run: chmod +x push_javadoc.sh + - name: Build and Snapshot branch + run: ./gradlew -PreleaseMode=branch -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" build --stacktrace + - name: Upload to Codecov + uses: codecov/codecov-action@v1 + - name: Push Javadocs + run: ./push_javadoc.sh \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6e595b4e93..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: java -jdk: -- openjdk8 - -# prevent travis running gradle assemble; let the build script do it anyway -install: true - -# running in container causes test failures and 2x-3x longer build, use standalone instances -sudo: required - -# script for build and release via Travis to Bintray -script: gradle/buildViaTravis.sh - -# Code coverage -after_success: - - bash <(curl -s --retry 10 https://codecov.io/bash) - - bash gradle/push_javadoc.sh - -# cache between builds -cache: - directories: - - $HOME/.m2 - - $HOME/.gradle -env: - global: - - secure: YcLpYfNc/dyDON+oDvnJK5pFNhpPeJHxlAHV8JBt42e51prAl6njqrg1Qlfdp0pvBiskTPQHUxbFy9DOB1Z+43lPj5vlqz6qBgtS3vtBnsrczr+5Xx7NTdVKq6oZGl45VjfNPT7zdM6GQ5ifdzOid6kJIFu34g9JZkCzOY3BWGM= - - secure: WVmfSeW1UMNdem7+X4cVDjkEkqdeNavYH4udn3bFN1IFaWdliWFp4FYVBVi+p1T/IgkRSqzoW9Bm43DABe1UMFoErFCbfd7B0Ofgb4NZAsxFgokHGVLCe6k5+rQyASseiO7k0itSj3Kq9TrDueKPhv+g+IG0w1A8yZTnXdhXHvY= - - secure: Xt8E09nmSr+5r7ly95hG/EiBitZbhFGPRGp8oqPkNn1A2fzG9+hnvlNLgQhVPsISZGzJwkWa3LGBxAVGmuysVOz7eCwkoqlDZaaSLYAPfWXqkr+cmYGPkErgHSp+n/hnQG4TylX0YxzqX8flr6db21zWyNduiyHmo+xFydI5LeM= - - secure: RmpIsmYa5BdLLWR6DILjhEE/dx2q3O0NIkvnMx5G1cyRCNCrOf1B7fYFHnsTDwpvRA+6H6dZinmeyf6D3G+czOG5q/TW2jcu5nh+YOLhBb6jPIqRDfq/WHAa5Lkdssxs5g9RdWlEDVFMoE62lGc4cnfJz5F5puH29dy2SvXxIQw= diff --git a/build.gradle b/build.gradle index 0b72b82524..c13005541b 100644 --- a/build.gradle +++ b/build.gradle @@ -42,7 +42,7 @@ ext.githubProjectName = "rxjava" version = project.properties["release.version"] -def releaseTag = System.getenv("TRAVIS_TAG"); +def releaseTag = System.getenv("BUILD_TAG"); if (releaseTag != null && !releaseTag.isEmpty()) { if (releaseTag.startsWith("v")) { releaseTag = releaseTag.substring(1); diff --git a/gradle/buildViaTravis.sh b/gradle/buildViaTravis.sh deleted file mode 100755 index ea385c3e92..0000000000 --- a/gradle/buildViaTravis.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# This script will build the project. - -buildTag="$TRAVIS_TAG" - -if [ "$buildTag" != "" ] && [ "${buildTag:0:3}" != "v3." ]; then - echo -e "Wrong tag on the 3.x brach: $buildTag : build stopped" - exit 1 -fi - -export GRADLE_OPTS=-Xmx1024m - -if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then - echo -e "Build Pull Request #$TRAVIS_PULL_REQUEST => Branch [$TRAVIS_BRANCH]" - ./gradlew -PreleaseMode=pr build --stacktrace -elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" == "" ]; then - if [ "$TRAVIS_BRANCH" != "3.x" ]; then - echo -e 'Build secondary Branch (no snapshot) => Branch ['$TRAVIS_BRANCH']' - ./gradlew -PreleaseMode=pr build --stacktrace - else - echo -e 'Build Branch with Snapshot => Branch ['$TRAVIS_BRANCH']' - ./gradlew -PreleaseMode=branch -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" build --stacktrace - fi -elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" != "" ]; then - echo -e 'Build Branch for Release => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG']' - ./gradlew -PreleaseMode=full -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" build --stacktrace -else - echo -e 'WARN: Should not be here => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG'] Pull Request ['$TRAVIS_PULL_REQUEST']' -fi diff --git a/gradle/push_javadoc.sh b/gradle/push_javadoc.sh index c8f648258e..28ce74f1db 100644 --- a/gradle/push_javadoc.sh +++ b/gradle/push_javadoc.sh @@ -8,21 +8,9 @@ targetRepo=github.com/ReactiveX/RxJava.git # ======================================================================= -# only for main pushes, for now -if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then - echo -e "Pull request detected, skipping JavaDocs pushback." - exit 0 -fi - -# only when on the 3.x branch and not tagged -if [ "$TRAVIS_BRANCH" != "3.x" ] && [ "$TRAVIS_TAG" == "" ]; then - echo -e "On a secondary branch '$TRAVIS_BRANCH', skipping JavaDocs pushback." - exit 0 -fi - # get the current build tag if any -buildTag="$TRAVIS_TAG" -echo -e "Travis tag: '$buildTag'" +buildTag="$BUILD_TAG" +echo -e "Build tag: '$buildTag'" if [ "$buildTag" == "" ]; then buildTag="snapshot" @@ -33,18 +21,18 @@ fi echo -e "JavaDocs pushback for tag: $buildTag" # check if the token is actually there -if [ "$GITHUB_TOKEN" == "" ]; then +if [ "$JAVADOCS_TOKEN" == "" ]; then echo -e "No access to GitHub, skipping JavaDocs pushback." exit 0 fi # prepare the git information -git config --global user.email "travis@travis-ci.org" -git config --global user.name "Travis CI" +git config --global user.email "akarnokd+ci@gmail.com" +git config --global user.name "akarnokd+ci" # setup the remote echo -e "Adding the target repository to git" -git remote add origin-pages https://${GITHUB_TOKEN}@${targetRepo} > /dev/null 2>&1 +git remote add origin-pages https://${JAVADOCS_TOKEN}@${targetRepo} > /dev/null 2>&1 # stash changes due to chmod echo -e "Stashing any local non-ignored changes" @@ -119,8 +107,8 @@ echo -e "Removing deleted files" git add -u # commit all -echo -e "commit Travis build: $TRAVIS_BUILD_NUMBER for $buildTag" -git commit --message "Travis build: $TRAVIS_BUILD_NUMBER for $buildTag" +echo -e "commit CI build: $CI_BUILD_NUMBER for $buildTag" +git commit --message "CI build: $CI_BUILD_NUMBER for $buildTag" # debug file list #find -name "*.html" From e4ad465fa663f0d80a9017d9149f8e6093e818a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Karnok?= Date: Fri, 20 Nov 2020 18:30:05 +0100 Subject: [PATCH 087/521] Fix push_javadoc.sh --- .github/workflows/gradle_pr.yml | 2 +- gradle/push_javadoc.sh => push_javadoc.sh | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename gradle/push_javadoc.sh => push_javadoc.sh (100%) diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index bb10853960..40ee972118 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -5,7 +5,7 @@ name: PR on: pull_request: - branches: [ 3.x, 3.x_GHA ] + branches: [ 3.x ] jobs: build: diff --git a/gradle/push_javadoc.sh b/push_javadoc.sh similarity index 100% rename from gradle/push_javadoc.sh rename to push_javadoc.sh From 3d1669da006bd494f7fce7f3b3d6274fa5ba6921 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 20 Nov 2020 19:10:39 +0100 Subject: [PATCH 088/521] 3.x: Update badge in README.md (#7115) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd1b51f5fa..806ff8561b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # RxJava: Reactive Extensions for the JVM - + [![codecov.io](http://codecov.io/github/ReactiveX/RxJava/coverage.svg?branch=3.x)](https://codecov.io/gh/ReactiveX/RxJava/branch/3.x) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.reactivex.rxjava3/rxjava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.reactivex.rxjava3/rxjava) From 3d5feceb3b103062eaea2a4e22d64dbd926f5769 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 25 Nov 2020 09:24:14 +0100 Subject: [PATCH 089/521] Bump mockito-core from 3.6.0 to 3.6.28 (#7116) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.6.0 to 3.6.28. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.6.0...v3.6.28) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c13005541b..c879ded04f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13.1" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.6.0" + ext.mockitoVersion = "3.6.28" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.2" ext.guavaVersion = "30.0-jre" From cdc75108b70c4bfaf2b7c994c3e45e5634e7720a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 26 Nov 2020 09:36:19 +0100 Subject: [PATCH 090/521] Bump build-info-extractor-gradle from 4.18.0 to 4.18.1 (#7118) Bumps build-info-extractor-gradle from 4.18.0 to 4.18.1. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c879ded04f..a53907c864 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.2" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" - ext.jfrogExtractorVersion = "4.18.0" + ext.jfrogExtractorVersion = "4.18.1" ext.bndVersion = "5.2.0" ext.checkstyleVersion = "8.26" From bfd242da5de53bd16f8c7d52c83e0968010bba2c Mon Sep 17 00:00:00 2001 From: David Karnok Date: Mon, 30 Nov 2020 20:21:14 +0100 Subject: [PATCH 091/521] 3.x: Remove unnecessary cancel/dispose calls from terminating using (#7121) --- .../java/io/reactivex/rxjava3/core/FlowableSubscriber.java | 6 ++++-- .../rxjava3/internal/operators/flowable/FlowableUsing.java | 4 ---- .../internal/operators/observable/ObservableUsing.java | 4 ---- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/FlowableSubscriber.java b/src/main/java/io/reactivex/rxjava3/core/FlowableSubscriber.java index 38b5bf3459..e07662b16b 100644 --- a/src/main/java/io/reactivex/rxjava3/core/FlowableSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/core/FlowableSubscriber.java @@ -18,8 +18,10 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Represents a Reactive-Streams inspired {@link Subscriber} that is RxJava 2 only - * and weakens rules §1.3 and §3.9 of the specification for gaining performance. + * Represents a Reactive-Streams inspired {@link Subscriber} that is RxJava 3 only + * and weakens the Reactive Streams rules §1.3 + * and §3.9 of the specification + * for gaining performance. * *

History: 2.0.7 - experimental; 2.1 - beta * @param the value type diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsing.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsing.java index 5a5b177d35..49fe0b8fb4 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsing.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsing.java @@ -117,7 +117,6 @@ public void onError(Throwable t) { } } - upstream.cancel(); if (innerError != null) { downstream.onError(new CompositeException(t, innerError)); } else { @@ -125,7 +124,6 @@ public void onError(Throwable t) { } } else { downstream.onError(t); - upstream.cancel(); disposeResource(); } } @@ -143,11 +141,9 @@ public void onComplete() { } } - upstream.cancel(); downstream.onComplete(); } else { downstream.onComplete(); - upstream.cancel(); disposeResource(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsing.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsing.java index dec3da5ebe..90954d41d8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsing.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsing.java @@ -115,11 +115,9 @@ public void onError(Throwable t) { } } - upstream.dispose(); downstream.onError(t); } else { downstream.onError(t); - upstream.dispose(); disposeResource(); } } @@ -137,11 +135,9 @@ public void onComplete() { } } - upstream.dispose(); downstream.onComplete(); } else { downstream.onComplete(); - upstream.dispose(); disposeResource(); } } From a315c55663f6c80c420ccce2cec11298abff5651 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 1 Dec 2020 09:26:46 +0100 Subject: [PATCH 092/521] Bump build-info-extractor-gradle from 4.18.1 to 4.18.2 (#7123) Bumps build-info-extractor-gradle from 4.18.1 to 4.18.2. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a53907c864..b371aea34f 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.2" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" - ext.jfrogExtractorVersion = "4.18.1" + ext.jfrogExtractorVersion = "4.18.2" ext.bndVersion = "5.2.0" ext.checkstyleVersion = "8.26" From 20fc2ba5a0c793796bca7416fdcc90da629a5b10 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 2 Dec 2020 12:09:46 +0100 Subject: [PATCH 093/521] Limit to released event types --- .github/workflows/gradle_release.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 6e4457a883..f5c40ce37e 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -5,9 +5,10 @@ name: Release on: release: + types: [ released ] branches: [ '3.x' ] tags: - - 'v3.*.*' + - 'v3.*.*' jobs: build: @@ -46,4 +47,4 @@ jobs: - name: Upload to Codecov uses: codecov/codecov-action@v1 - name: Push Javadocs - run: ./push_javadoc.sh \ No newline at end of file + run: ./push_javadoc.sh From 67c1a367397f1167f9ad5639dd5071b6608e4230 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 2 Dec 2020 13:44:29 +0100 Subject: [PATCH 094/521] Trigger on prereleases too --- .github/workflows/gradle_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index f5c40ce37e..6e5af8f48f 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -5,7 +5,7 @@ name: Release on: release: - types: [ released ] + types: [ released, prereleased ] branches: [ '3.x' ] tags: - 'v3.*.*' From 7e7b223ccf4af4ef594677f04e731998914190b9 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Sun, 6 Dec 2020 22:32:52 +0100 Subject: [PATCH 095/521] 3.x: Improve error messages in the test consumers (#7126) * 3.x: Improve error reporting in the test consumers * Verify assertValueAt with negative index --- .../rxjava3/observers/BaseTestConsumer.java | 38 +++++++++----- .../rxjava3/observers/TestObserverTest.java | 49 +++++++++++++---- .../subscribers/TestSubscriberTest.java | 52 +++++++++++++++++-- 3 files changed, 112 insertions(+), 27 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java b/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java index 458325c280..9c6947c5aa 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java +++ b/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java @@ -228,7 +228,7 @@ public final U assertNoErrors() { */ @NonNull public final U assertError(@NonNull Throwable error) { - return assertError(Functions.equalsWith(error)); + return assertError(Functions.equalsWith(error), true); } /** @@ -240,7 +240,7 @@ public final U assertError(@NonNull Throwable error) { @SuppressWarnings({ "unchecked", "rawtypes" }) @NonNull public final U assertError(@NonNull Class errorClass) { - return (U)assertError((Predicate)Functions.isInstanceOf(errorClass)); + return (U)assertError((Predicate)Functions.isInstanceOf(errorClass), true); } /** @@ -251,9 +251,14 @@ public final U assertError(@NonNull Class errorClass) { * and should return {@code true} for expected errors. * @return this */ - @SuppressWarnings("unchecked") @NonNull public final U assertError(@NonNull Predicate errorPredicate) { + return assertError(errorPredicate, false); + } + + @SuppressWarnings("unchecked") + @NonNull + private U assertError(@NonNull Predicate errorPredicate, boolean exact) { int s = errors.size(); if (s == 0) { throw fail("No errors"); @@ -274,10 +279,16 @@ public final U assertError(@NonNull Predicate errorPredicate) { if (found) { if (s != 1) { - throw fail("Error present but other errors as well"); + if (exact) { + throw fail("Error present but other errors as well"); + } + throw fail("One error passed the predicate but other errors are present as well"); } } else { - throw fail("Error not present"); + if (exact) { + throw fail("Error not present"); + } + throw fail("No error(s) passed the predicate"); } return (U)this; } @@ -316,7 +327,7 @@ public final U assertValue(@NonNull Predicate valuePredicate) { assertValueAt(0, valuePredicate); if (values.size() > 1) { - throw fail("Value present but other values as well"); + throw fail("The first value passed the predicate but this consumer received more than one value"); } return (U)this; @@ -339,13 +350,13 @@ public final U assertValueAt(int index, @NonNull T value) { throw fail("No values"); } - if (index >= s) { - throw fail("Invalid index: " + index); + if (index < 0 || index >= s) { + throw fail("Index " + index + " is out of range [0, " + s + ")"); } T v = values.get(index); if (!Objects.equals(value, v)) { - throw fail("expected: " + valueAndClass(value) + " but was: " + valueAndClass(v)); + throw fail("expected: " + valueAndClass(value) + " but was: " + valueAndClass(v) + " at position " + index); } return (U)this; } @@ -367,14 +378,15 @@ public final U assertValueAt(int index, @NonNull Predicate valuePredicate) { throw fail("No values"); } - if (index >= values.size()) { - throw fail("Invalid index: " + index); + if (index < 0 || index >= s) { + throw fail("Index " + index + " is out of range [0, " + s + ")"); } boolean found = false; + T v = values.get(index); try { - if (valuePredicate.test(values.get(index))) { + if (valuePredicate.test(v)) { found = true; } } catch (Throwable ex) { @@ -382,7 +394,7 @@ public final U assertValueAt(int index, @NonNull Predicate valuePredicate) { } if (!found) { - throw fail("Value not present"); + throw fail("Value " + valueAndClass(v) + " at position " + index + " did not pass the predicate"); } return (U)this; } diff --git a/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java index 31e66d808b..56ed09ac44 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java @@ -21,6 +21,7 @@ import java.util.concurrent.TimeUnit; import org.junit.Test; +import org.junit.function.ThrowingRunnable; import org.mockito.InOrder; import org.reactivestreams.Subscriber; @@ -39,6 +40,10 @@ public class TestObserverTest extends RxJavaTest { + static void assertThrowsWithMessage(String message, Class clazz, ThrowingRunnable run) { + assertEquals(message, assertThrows(clazz, run).getMessage()); + } + @Test public void assertTestObserver() { Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); @@ -843,7 +848,7 @@ public void errorMeansDisposed() { @Test public void assertValuePredicateEmpty() { - assertThrows("No values", AssertionError.class, () -> { + assertThrowsWithMessage("No values (latch = 0, values = 0, errors = 0, completions = 1)", AssertionError.class, () -> { TestObserver to = new TestObserver<>(); Observable.empty().subscribe(to); @@ -871,7 +876,7 @@ public void assertValuePredicateMatch() { @Test public void assertValuePredicateNoMatch() { - assertThrows("Value not present", AssertionError.class, () -> { + assertThrowsWithMessage("Value 1 (class: Integer) at position 0 did not pass the predicate (latch = 0, values = 1, errors = 0, completions = 1)", AssertionError.class, () -> { TestObserver to = new TestObserver<>(); Observable.just(1).subscribe(to); @@ -886,7 +891,7 @@ public void assertValuePredicateNoMatch() { @Test public void assertValuePredicateMatchButMore() { - assertThrows("Value present but other values as well", AssertionError.class, () -> { + assertThrowsWithMessage("The first value passed the predicate but this consumer received more than one value (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { TestObserver to = new TestObserver<>(); Observable.just(1, 2).subscribe(to); @@ -901,7 +906,7 @@ public void assertValuePredicateMatchButMore() { @Test public void assertValueAtPredicateEmpty() { - assertThrows("No values", AssertionError.class, () -> { + assertThrowsWithMessage("No values (latch = 0, values = 0, errors = 0, completions = 1)", AssertionError.class, () -> { TestObserver to = new TestObserver<>(); Observable.empty().subscribe(to); @@ -929,7 +934,7 @@ public void assertValueAtPredicateMatch() { @Test public void assertValueAtPredicateNoMatch() { - assertThrows("Value not present", AssertionError.class, () -> { + assertThrowsWithMessage("Value 3 (class: Integer) at position 2 did not pass the predicate (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { TestObserver to = new TestObserver<>(); Observable.just(1, 2, 3).subscribe(to); @@ -944,7 +949,7 @@ public void assertValueAtPredicateNoMatch() { @Test public void assertValueAtInvalidIndex() { - assertThrows("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + assertThrowsWithMessage("Index 2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { TestObserver to = new TestObserver<>(); Observable.just(1, 2).subscribe(to); @@ -957,9 +962,24 @@ public void assertValueAtInvalidIndex() { }); } + @Test + public void assertValueAtInvalidIndexNegative() { + assertThrowsWithMessage("Index -2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just(1, 2).subscribe(to); + + to.assertValueAt(-2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); + }); + } + @Test public void assertValueAtIndexEmpty() { - assertThrows("No values", AssertionError.class, () -> { + assertThrowsWithMessage("No values (latch = 0, values = 0, errors = 0, completions = 1)", AssertionError.class, () -> { TestObserver to = new TestObserver<>(); Observable.empty().subscribe(to); @@ -979,7 +999,7 @@ public void assertValueAtIndexMatch() { @Test public void assertValueAtIndexNoMatch() { - assertThrows("expected: b (class: String) but was: c (class: String) (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { + assertThrowsWithMessage("expected: b (class: String) but was: c (class: String) at position 2 (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { TestObserver to = new TestObserver<>(); Observable.just("a", "b", "c").subscribe(to); @@ -990,7 +1010,7 @@ public void assertValueAtIndexNoMatch() { @Test public void assertValueAtIndexInvalidIndex() { - assertThrows("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + assertThrowsWithMessage("Index 2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { TestObserver to = new TestObserver<>(); Observable.just("a", "b").subscribe(to); @@ -999,6 +1019,17 @@ public void assertValueAtIndexInvalidIndex() { }); } + @Test + public void assertValueAtIndexInvalidIndexNegative() { + assertThrowsWithMessage("Index -2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just("a", "b").subscribe(to); + + to.assertValueAt(-2, "c"); + }); + } + @Test public void withTag() { try { diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java index 48bb722585..67e397b4bd 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java @@ -22,6 +22,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; +import org.junit.function.ThrowingRunnable; import org.mockito.InOrder; import org.reactivestreams.*; @@ -1391,9 +1392,13 @@ public void assertValuePredicateMatch() { }); } + static void assertThrowsWithMessage(String message, Class clazz, ThrowingRunnable run) { + assertEquals(message, assertThrows(clazz, run).getMessage()); + } + @Test public void assertValuePredicateNoMatch() { - assertThrows("Value not present", AssertionError.class, () -> { + assertThrowsWithMessage("Value 1 (class: Integer) at position 0 did not pass the predicate (latch = 0, values = 1, errors = 0, completions = 1)", AssertionError.class, () -> { TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).subscribe(ts); @@ -1408,7 +1413,7 @@ public void assertValuePredicateNoMatch() { @Test public void assertValuePredicateMatchButMore() { - assertThrows("Value present but other values as well", AssertionError.class, () -> { + assertThrowsWithMessage("The first value passed the predicate but this consumer received more than one value (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1, 2).subscribe(ts); @@ -1423,7 +1428,7 @@ public void assertValuePredicateMatchButMore() { @Test public void assertValueAtPredicateEmpty() { - assertThrows("No values", AssertionError.class, () -> { + assertThrowsWithMessage("No values (latch = 0, values = 0, errors = 0, completions = 1)", AssertionError.class, () -> { TestSubscriber ts = new TestSubscriber<>(); Flowable.empty().subscribe(ts); @@ -1451,7 +1456,7 @@ public void assertValueAtPredicateMatch() { @Test public void assertValueAtPredicateNoMatch() { - assertThrows("Value not present", AssertionError.class, () -> { + assertThrowsWithMessage("Value 3 (class: Integer) at position 2 did not pass the predicate (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1, 2, 3).subscribe(ts); @@ -1466,7 +1471,7 @@ public void assertValueAtPredicateNoMatch() { @Test public void assertValueAtInvalidIndex() { - assertThrows("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + assertThrowsWithMessage("Index 2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1, 2).subscribe(ts); @@ -1479,6 +1484,43 @@ public void assertValueAtInvalidIndex() { }); } + @Test + public void assertValueAtIndexInvalidIndex() { + assertThrowsWithMessage("Index 2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1, 2).subscribe(ts); + + ts.assertValueAt(2, 3); + }); + } + + @Test + public void assertValueAtIndexInvalidIndexNegative() { + assertThrowsWithMessage("Index -2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1, 2).subscribe(ts); + + ts.assertValueAt(-2, 3); + }); + } + + @Test + public void assertValueAtInvalidIndexNegative() { + assertThrowsWithMessage("Index -2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1, 2).subscribe(ts); + + ts.assertValueAt(-2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); + }); + } + @Test public void requestMore() { Flowable.range(1, 5) From c573219f520c30583f8a62f9c996c6c17b2de447 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Mon, 7 Dec 2020 13:31:41 +0100 Subject: [PATCH 096/521] 3.x: Improve Javadocs of Connectable sources (#7127) --- .../flowables/ConnectableFlowable.java | 83 +++++------ .../observables/ConnectableObservable.java | 135 +++++++++--------- .../validators/JavadocCodesAndLinks.java | 10 ++ 3 files changed, 120 insertions(+), 108 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java b/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java index 041fc3bd1b..a46371f545 100644 --- a/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java @@ -16,7 +16,7 @@ import java.util.Objects; import java.util.concurrent.TimeUnit; -import org.reactivestreams.Subscriber; +import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.*; @@ -37,9 +37,9 @@ * *

* When the upstream terminates, the {@code ConnectableFlowable} remains in this terminated state and, - * depending on the actual underlying implementation, relays cached events to late {@link Subscriber}s. + * depending on the actual underlying implementation, relays cached events to late {@code Subscriber}s. * In order to reuse and restart this {@code ConnectableFlowable}, the {@link #reset()} method has to be called. - * When called, this {@code ConnectableFlowable} will appear as fresh, unconnected source to new {@link Subscriber}s. + * When called, this {@code ConnectableFlowable} will appear as fresh, unconnected source to new {@code Subscriber}s. * Disposing the connection will reset the {@code ConnectableFlowable} to its fresh state and there is no need to call * {@code reset()} in this case. *

@@ -48,8 +48,7 @@ * there is no unwanted signal loss due to early {@code connect()} or {@code reset()} calls while {@code Subscriber}s are * still being subscribed to to this {@code ConnectableFlowable} to receive signals from the get go. *

- * @see RxJava Wiki: - * Connectable Observable Operators + * @see RxJava Wiki: Connectable Observable Operators * @param * the type of items emitted by the {@code ConnectableFlowable} * @since 2.0.0 @@ -74,7 +73,7 @@ public abstract class ConnectableFlowable extends Flowable { public abstract void connect(@NonNull Consumer connection); /** - * Resets this ConnectableFlowable into its fresh state if it has terminated. + * Resets this {@code ConnectableFlowable} into its fresh state if it has terminated. *

* Calling this method on a fresh or active {@code ConnectableFlowable} has no effect. *

@@ -108,7 +107,7 @@ public final Disposable connect() { } /** - * Returns a {@code Flowable} that stays connected to this {@code ConnectableFlowable} as long as there + * Returns a {@link Flowable} that stays connected to this {@code ConnectableFlowable} as long as there * is at least one subscription to this {@code ConnectableFlowable}. *
*
Backpressure:
@@ -117,7 +116,7 @@ public final Disposable connect() { *
Scheduler:
*
This {@code refCount} overload does not operate on any particular {@link Scheduler}.
*
- * @return a {@link Flowable} + * @return the new {@code Flowable} instance * @see ReactiveX documentation: RefCount * @see #refCount(int) * @see #refCount(long, TimeUnit) @@ -143,7 +142,8 @@ public Flowable refCount() { *
*

History: 2.1.14 - experimental * @param subscriberCount the number of subscribers required to connect to the upstream - * @return the new Flowable instance + * @return the new {@link Flowable} instance + * @throws IllegalArgumentException if {@code subscriberCount} is non-positive * @since 2.2 */ @CheckReturnValue @@ -168,7 +168,7 @@ public final Flowable refCount(int subscriberCount) { *

History: 2.1.14 - experimental * @param timeout the time to wait before disconnecting after all subscribers unsubscribed * @param unit the time unit of the timeout - * @return the new Flowable instance + * @return the new {@link Flowable} instance * @throws NullPointerException if {@code unit} is {@code null} * @see #refCount(long, TimeUnit, Scheduler) * @since 2.2 @@ -196,7 +196,7 @@ public final Flowable refCount(long timeout, @NonNull TimeUnit unit) { * @param timeout the time to wait before disconnecting after all subscribers unsubscribed * @param unit the time unit of the timeout * @param scheduler the target scheduler to wait on before disconnecting - * @return the new Flowable instance + * @return the new {@link Flowable} instance * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @since 2.2 */ @@ -223,8 +223,9 @@ public final Flowable refCount(long timeout, @NonNull TimeUnit unit, @NonNull * @param subscriberCount the number of subscribers required to connect to the upstream * @param timeout the time to wait before disconnecting after all subscribers unsubscribed * @param unit the time unit of the timeout - * @return the new Flowable instance + * @return the new {@link Flowable} instance * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code subscriberCount} is non-positive * @see #refCount(int, long, TimeUnit, Scheduler) * @since 2.2 */ @@ -252,7 +253,7 @@ public final Flowable refCount(int subscriberCount, long timeout, @NonNull Ti * @param timeout the time to wait before disconnecting after all subscribers unsubscribed * @param unit the time unit of the timeout * @param scheduler the target scheduler to wait on before disconnecting - * @return the new Flowable instance + * @return the new {@link Flowable} instance * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @throws IllegalArgumentException if {@code subscriberCount} is non-positive * @since 2.2 @@ -269,20 +270,20 @@ public final Flowable refCount(int subscriberCount, long timeout, @NonNull Ti } /** - * Returns a Flowable that automatically connects (at most once) to this ConnectableFlowable - * when the first Subscriber subscribes. + * Returns a {@link Flowable} that automatically connects (at most once) to this {@code ConnectableFlowable} + * when the first {@link Subscriber} subscribes. *

* *

* The connection happens after the first subscription and happens at most once - * during the lifetime of the returned Flowable. If this ConnectableFlowable - * terminates, the connection is never renewed, no matter how Subscribers come + * during the lifetime of the returned {@code Flowable}. If this {@code ConnectableFlowable} + * terminates, the connection is never renewed, no matter how {@code Subscriber}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active - * connection when all {@code Subscriber}s have cancelled their {@code Subscription}s. + * connection when all {@code Subscriber}s have cancelled their {@link Subscription}s. *

* This overload does not allow disconnecting the connection established via * {@link #connect(Consumer)}. Use the {@link #autoConnect(int, Consumer)} overload - * to gain access to the {@code Disposable} representing the only connection. + * to gain access to the {@link Disposable} representing the only connection. *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by @@ -291,8 +292,8 @@ public final Flowable refCount(int subscriberCount, long timeout, @NonNull Ti *
{@code autoConnect} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Flowable that automatically connects to this ConnectableFlowable - * when the first Subscriber subscribes + * @return a new {@code Flowable} instance that automatically connects to this {@code ConnectableFlowable} + * when the first {@code Subscriber} subscribes * @see #refCount() * @see #autoConnect(int, Consumer) */ @@ -304,20 +305,20 @@ public Flowable autoConnect() { return autoConnect(1); } /** - * Returns a Flowable that automatically connects (at most once) to this ConnectableFlowable - * when the specified number of Subscribers subscribe to it. + * Returns a {@link Flowable} that automatically connects (at most once) to this {@code ConnectableFlowable} + * when the specified number of {@link Subscriber}s subscribe to it. *

* *

* The connection happens after the given number of subscriptions and happens at most once - * during the lifetime of the returned Flowable. If this ConnectableFlowable - * terminates, the connection is never renewed, no matter how Subscribers come + * during the lifetime of the returned {@code Flowable}. If this {@code ConnectableFlowable} + * terminates, the connection is never renewed, no matter how {@code Subscriber}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active - * connection when all {@code Subscriber}s have cancelled their {@code Subscription}s. + * connection when all {@code Subscriber}s have cancelled their {@link Subscription}s. *

* This overload does not allow disconnecting the connection established via * {@link #connect(Consumer)}. Use the {@link #autoConnect(int, Consumer)} overload - * to gain access to the {@code Disposable} representing the only connection. + * to gain access to the {@link Disposable} representing the only connection. *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by @@ -327,10 +328,10 @@ public Flowable autoConnect() { *
* * @param numberOfSubscribers the number of subscribers to await before calling connect - * on the ConnectableFlowable. A non-positive value indicates + * on the {@code ConnectableFlowable}. A non-positive value indicates * an immediate connection. - * @return a Flowable that automatically connects to this ConnectableFlowable - * when the specified number of Subscribers subscribe to it + * @return a new {@code Flowable} instance that automatically connects to this {@code ConnectableFlowable} + * when the specified number of {@code Subscriber}s subscribe to it */ @NonNull @CheckReturnValue @@ -341,17 +342,17 @@ public Flowable autoConnect(int numberOfSubscribers) { } /** - * Returns a Flowable that automatically connects (at most once) to this ConnectableFlowable - * when the specified number of Subscribers subscribe to it and calls the - * specified callback with the Subscription associated with the established connection. + * Returns a {@link Flowable} that automatically connects (at most once) to this {@code ConnectableFlowable} + * when the specified number of {@link Subscriber}s subscribe to it and calls the + * specified callback with the {@link Disposable} associated with the established connection. *

* *

* The connection happens after the given number of subscriptions and happens at most once - * during the lifetime of the returned Flowable. If this ConnectableFlowable - * terminates, the connection is never renewed, no matter how Subscribers come + * during the lifetime of the returned {@code Flowable}. If this {@code ConnectableFlowable} + * terminates, the connection is never renewed, no matter how {@code Subscriber}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active - * connection when all {@code Subscriber}s have cancelled their {@code Subscription}s. + * connection when all {@code Subscriber}s have cancelled their {@link Subscription}s. *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by @@ -361,13 +362,13 @@ public Flowable autoConnect(int numberOfSubscribers) { *
* * @param numberOfSubscribers the number of subscribers to await before calling connect - * on the ConnectableFlowable. A non-positive value indicates + * on the {@code ConnectableFlowable}. A non-positive value indicates * an immediate connection. - * @param connection the callback Consumer that will receive the Subscription representing the + * @param connection the callback {@link Consumer} that will receive the {@code Disposable} representing the * established connection - * @return a Flowable that automatically connects to this ConnectableFlowable - * when the specified number of Subscribers subscribe to it and calls the - * specified callback with the Subscription associated with the established connection + * @return a new {@code Flowable} instance that automatically connects to this {@code ConnectableFlowable} + * when the specified number of {@code Subscriber}s subscribe to it and calls the + * specified callback with the {@code Disposable} associated with the established connection * @throws NullPointerException if {@code connection} is {@code null} */ @NonNull diff --git a/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java b/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java index 107971ca88..9a0b54c2cf 100644 --- a/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java +++ b/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java @@ -35,19 +35,18 @@ * *

* When the upstream terminates, the {@code ConnectableObservable} remains in this terminated state and, - * depending on the actual underlying implementation, relays cached events to late {@link Observer}s. + * depending on the actual underlying implementation, relays cached events to late {@code Observer}s. * In order to reuse and restart this {@code ConnectableObservable}, the {@link #reset()} method has to be called. - * When called, this {@code ConnectableObservable} will appear as fresh, unconnected source to new {@link Observer}s. - * Disposing the connection will reset the {@code ConnectableFlowable} to its fresh state and there is no need to call - * {@code reset()} in this case. + * When called, this {@code ConnectableObservable} will appear as fresh, unconnected source to new {@code Observer}s. + * Disposing the connection will reset the {@code ConnectableObservable} to its fresh state and there is no need to call + * {@link #reset()} in this case. *

* Note that although {@link #connect()} and {@link #reset()} are safe to call from multiple threads, it is recommended * a dedicated thread or business logic manages the connection or resetting of a {@code ConnectableObservable} so that * there is no unwanted signal loss due to early {@code connect()} or {@code reset()} calls while {@code Observer}s are * still being subscribed to to this {@code ConnectableObservable} to receive signals from the get go. * - * @see RxJava Wiki: - * Connectable Observable Operators + * @see RxJava Wiki: Connectable Observable Operators * @param * the type of items emitted by the {@code ConnectableObservable} */ @@ -71,7 +70,7 @@ public abstract class ConnectableObservable extends Observable { public abstract void connect(@NonNull Consumer connection); /** - * Resets this ConnectableObservable into its fresh state if it has terminated + * Resets this {@code ConnectableObservable} into its fresh state if it has terminated * or has been disposed. *

* Calling this method on a fresh or active {@code ConnectableObservable} has no effect. @@ -94,7 +93,7 @@ public abstract class ConnectableObservable extends Observable { *

The behavior is determined by the implementor of this abstract class.
* * - * @return the subscription representing the connection + * @return the {@link Disposable} representing the connection * @see ReactiveX documentation: Connect */ @NonNull @@ -106,13 +105,13 @@ public final Disposable connect() { } /** - * Returns an {@code Observable} that stays connected to this {@code ConnectableObservable} as long as there + * Returns an {@link Observable} that stays connected to this {@code ConnectableObservable} as long as there * is at least one subscription to this {@code ConnectableObservable}. *
*
Scheduler:
*
This {@code refCount} overload does not operate on any particular {@link Scheduler}.
*
- * @return an {@link Observable} + * @return a new {@code Observable} instance * @see ReactiveX documentation: RefCount * @see #refCount(int) * @see #refCount(long, TimeUnit) @@ -127,35 +126,36 @@ public Observable refCount() { /** * Connects to the upstream {@code ConnectableObservable} if the number of subscribed - * observers reaches the specified count and disconnect if all subscribers have unsubscribed. + * observers reaches the specified count and disconnect if all {@link Observer}s have unsubscribed. *
*
Scheduler:
*
This {@code refCount} overload does not operate on any particular {@link Scheduler}.
*
*

History: 2.1.14 - experimental - * @param subscriberCount the number of subscribers required to connect to the upstream - * @return the new Observable instance + * @param observerCount the number of {@code Observer}s required to connect to the upstream + * @return the new {@link Observable} instance + * @throws IllegalArgumentException if {@code observerCount} is non-positive * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @NonNull - public final Observable refCount(int subscriberCount) { - return refCount(subscriberCount, 0, TimeUnit.NANOSECONDS, Schedulers.trampoline()); + public final Observable refCount(int observerCount) { + return refCount(observerCount, 0, TimeUnit.NANOSECONDS, Schedulers.trampoline()); } /** * Connects to the upstream {@code ConnectableObservable} if the number of subscribed * observers reaches 1 and disconnect after the specified - * timeout if all subscribers have unsubscribed. + * timeout if all {@link Observer}s have unsubscribed. *

*
Scheduler:
*
This {@code refCount} overload operates on the {@code computation} {@link Scheduler}.
*
*

History: 2.1.14 - experimental - * @param timeout the time to wait before disconnecting after all subscribers unsubscribed + * @param timeout the time to wait before disconnecting after all {@code Observer}s unsubscribed * @param unit the time unit of the timeout - * @return the new Observable instance + * @return the new {@link Observable} instance * @throws NullPointerException if {@code unit} is {@code null} * @see #refCount(long, TimeUnit, Scheduler) * @since 2.2 @@ -170,16 +170,16 @@ public final Observable refCount(long timeout, @NonNull TimeUnit unit) { /** * Connects to the upstream {@code ConnectableObservable} if the number of subscribed * observers reaches 1 and disconnect after the specified - * timeout if all subscribers have unsubscribed. + * timeout if all {@link Observer}s have unsubscribed. *

*
Scheduler:
*
This {@code refCount} overload operates on the specified {@link Scheduler}.
*
*

History: 2.1.14 - experimental - * @param timeout the time to wait before disconnecting after all subscribers unsubscribed + * @param timeout the time to wait before disconnecting after all {@code Observer}s unsubscribed * @param unit the time unit of the timeout * @param scheduler the target scheduler to wait on before disconnecting - * @return the new Observable instance + * @return the new {@link Observable} instance * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @since 2.2 */ @@ -193,66 +193,67 @@ public final Observable refCount(long timeout, @NonNull TimeUnit unit, @NonNu /** * Connects to the upstream {@code ConnectableObservable} if the number of subscribed * observers reaches the specified count and disconnect after the specified - * timeout if all subscribers have unsubscribed. + * timeout if all {@link Observer}s have unsubscribed. *

*
Scheduler:
*
This {@code refCount} overload operates on the {@code computation} {@link Scheduler}.
*
*

History: 2.1.14 - experimental - * @param subscriberCount the number of subscribers required to connect to the upstream - * @param timeout the time to wait before disconnecting after all subscribers unsubscribed + * @param observerCount the number of {@code Observer}s required to connect to the upstream + * @param timeout the time to wait before disconnecting after all {@code Observer}s unsubscribed * @param unit the time unit of the timeout - * @return the new Observable instance + * @return the new {@link Observable} instance * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code observerCount} is non-positive * @see #refCount(int, long, TimeUnit, Scheduler) * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) @NonNull - public final Observable refCount(int subscriberCount, long timeout, @NonNull TimeUnit unit) { - return refCount(subscriberCount, timeout, unit, Schedulers.computation()); + public final Observable refCount(int observerCount, long timeout, @NonNull TimeUnit unit) { + return refCount(observerCount, timeout, unit, Schedulers.computation()); } /** * Connects to the upstream {@code ConnectableObservable} if the number of subscribed * observers reaches the specified count and disconnect after the specified - * timeout if all subscribers have unsubscribed. + * timeout if all {@link Observer}s have unsubscribed. *

*
Scheduler:
*
This {@code refCount} overload operates on the specified {@link Scheduler}.
*
*

History: 2.1.14 - experimental - * @param subscriberCount the number of subscribers required to connect to the upstream - * @param timeout the time to wait before disconnecting after all subscribers unsubscribed + * @param observerCount the number of {@code Observer}s required to connect to the upstream + * @param timeout the time to wait before disconnecting after all {@code Observer}s unsubscribed * @param unit the time unit of the timeout * @param scheduler the target scheduler to wait on before disconnecting - * @return the new Observable instance + * @return the new {@link Observable} instance * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} - * @throws IllegalArgumentException if {@code subscriberCount} is non-positive + * @throws IllegalArgumentException if {@code observerCount} is non-positive * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) @NonNull - public final Observable refCount(int subscriberCount, long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { - ObjectHelper.verifyPositive(subscriberCount, "subscriberCount"); + public final Observable refCount(int observerCount, long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + ObjectHelper.verifyPositive(observerCount, "observerCount"); Objects.requireNonNull(unit, "unit is null"); Objects.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableRefCount<>(this, subscriberCount, timeout, unit, scheduler)); + return RxJavaPlugins.onAssembly(new ObservableRefCount<>(this, observerCount, timeout, unit, scheduler)); } /** - * Returns an Observable that automatically connects (at most once) to this ConnectableObservable - * when the first Observer subscribes. + * Returns an {@link Observable} that automatically connects (at most once) to this {@code ConnectableObservable} + * when the first {@link Observer} subscribes. *

* *

* The connection happens after the first subscription and happens at most once - * during the lifetime of the returned Observable. If this ConnectableObservable - * terminates, the connection is never renewed, no matter how Observers come + * during the lifetime of the returned {@code Observable}. If this {@code ConnectableObservable} + * terminates, the connection is never renewed, no matter how {@code Observer}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active - * connection when all {@code Observer}s have disposed their {@code Disposable}s. + * connection when all {@code Observer}s have disposed their {@link Disposable}s. *

* This overload does not allow disconnecting the connection established via * {@link #connect(Consumer)}. Use the {@link #autoConnect(int, Consumer)} overload @@ -262,8 +263,8 @@ public final Observable refCount(int subscriberCount, long timeout, @NonNull *

{@code autoConnect} overload does not operate on any particular {@link Scheduler}.
* * - * @return an Observable that automatically connects to this ConnectableObservable - * when the first Observer subscribes + * @return a new {@code Observable} instance that automatically connects to this {@code ConnectableObservable} + * when the first {@code Observer} subscribes */ @NonNull @CheckReturnValue @@ -273,16 +274,16 @@ public Observable autoConnect() { } /** - * Returns an Observable that automatically connects (at most once) to this ConnectableObservable - * when the specified number of Observers subscribe to it. + * Returns an {@link Observable} that automatically connects (at most once) to this {@code ConnectableObservable} + * when the specified number of {@link Observer}s subscribe to it. *

* *

* The connection happens after the given number of subscriptions and happens at most once - * during the lifetime of the returned Observable. If this ConnectableObservable - * terminates, the connection is never renewed, no matter how Observers come + * during the lifetime of the returned {@code Observable}. If this {@code ConnectableObservable} + * terminates, the connection is never renewed, no matter how {@code Observer}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active - * connection when all {@code Observer}s have disposed their {@code Disposable}s. + * connection when all {@code Observer}s have disposed their {@link Disposable}s. *

* This overload does not allow disconnecting the connection established via * {@link #connect(Consumer)}. Use the {@link #autoConnect(int, Consumer)} overload @@ -292,29 +293,29 @@ public Observable autoConnect() { *

{@code autoConnect} overload does not operate on any particular {@link Scheduler}.
* * - * @param numberOfSubscribers the number of subscribers to await before calling connect - * on the ConnectableObservable. A non-positive value indicates + * @param numberOfObservers the number of subscribers to await before calling connect + * on the {@code ConnectableObservable}. A non-positive value indicates * an immediate connection. - * @return an Observable that automatically connects to this ConnectableObservable - * when the specified number of Subscribers subscribe to it + * @return a new {@code Observable} instance that automatically connects to this {@code ConnectableObservable} + * when the specified number of {@code Observer}s subscribe to it */ @NonNull @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public Observable autoConnect(int numberOfSubscribers) { - return autoConnect(numberOfSubscribers, Functions.emptyConsumer()); + public Observable autoConnect(int numberOfObservers) { + return autoConnect(numberOfObservers, Functions.emptyConsumer()); } /** - * Returns an Observable that automatically connects (at most once) to this ConnectableObservable - * when the specified number of Subscribers subscribe to it and calls the - * specified callback with the Subscription associated with the established connection. + * Returns an {@link Observable} that automatically connects (at most once) to this {@code ConnectableObservable} + * when the specified number of {@link Observer}s subscribe to it and calls the + * specified callback with the {@link Disposable} associated with the established connection. *

* *

* The connection happens after the given number of subscriptions and happens at most once - * during the lifetime of the returned Observable. If this ConnectableObservable - * terminates, the connection is never renewed, no matter how Observers come + * during the lifetime of the returned {@code Observable}. If this {@code ConnectableObservable} + * terminates, the connection is never renewed, no matter how {@code Observer}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active * connection when all {@code Observer}s have disposed their {@code Disposable}s. *

@@ -322,25 +323,25 @@ public Observable autoConnect(int numberOfSubscribers) { *
{@code autoConnect} overload does not operate on any particular {@link Scheduler}.
*
* - * @param numberOfSubscribers the number of subscribers to await before calling connect - * on the ConnectableObservable. A non-positive value indicates + * @param numberOfObservers the number of subscribers to await before calling connect + * on the {@code ConnectableObservable}. A non-positive value indicates * an immediate connection. - * @param connection the callback Consumer that will receive the Subscription representing the + * @param connection the callback {@link Consumer} that will receive the {@code Disposable} representing the * established connection - * @return an Observable that automatically connects to this ConnectableObservable - * when the specified number of Subscribers subscribe to it and calls the - * specified callback with the Subscription associated with the established connection + * @return a new {@code Observable} instance that automatically connects to this {@code ConnectableObservable} + * when the specified number of {@code Observer}s subscribe to it and calls the + * specified callback with the {@code Disposable} associated with the established connection * @throws NullPointerException if {@code connection} is {@code null} */ @NonNull @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public Observable autoConnect(int numberOfSubscribers, @NonNull Consumer connection) { + public Observable autoConnect(int numberOfObservers, @NonNull Consumer connection) { Objects.requireNonNull(connection, "connection is null"); - if (numberOfSubscribers <= 0) { + if (numberOfObservers <= 0) { this.connect(connection); return RxJavaPlugins.onAssembly(this); } - return RxJavaPlugins.onAssembly(new ObservableAutoConnect<>(this, numberOfSubscribers, connection)); + return RxJavaPlugins.onAssembly(new ObservableAutoConnect<>(this, numberOfObservers, connection)); } } diff --git a/src/test/java/io/reactivex/rxjava3/validators/JavadocCodesAndLinks.java b/src/test/java/io/reactivex/rxjava3/validators/JavadocCodesAndLinks.java index 55cecb6097..5b90072306 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/JavadocCodesAndLinks.java +++ b/src/test/java/io/reactivex/rxjava3/validators/JavadocCodesAndLinks.java @@ -65,6 +65,16 @@ public void checkCompositeDisposable() throws Exception { checkSource("CompositeDisposable", "io.reactivex.rxjava3.disposables"); } + @Test + public void checkConnectableFlowable() throws Exception { + checkSource("ConnectableFlowable", "io.reactivex.rxjava3.flowables"); + } + + @Test + public void checkConnectableObservable() throws Exception { + checkSource("ConnectableObservable", "io.reactivex.rxjava3.observables"); + } + static void checkSource(String baseClassName, String packageName) throws Exception { File f = TestHelper.findSource(baseClassName, packageName); if (f == null) { From 7741c59fd05196d40b5a6214314842552290c156 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 9 Dec 2020 13:15:51 +0300 Subject: [PATCH 097/521] 3.x: Add onBackpressureReduce operator (#7124) * onBackpressureReduce operator added * onBackpressureReduce operator added * onBackpressureReduce operator added * 3.x: onBackpressureReduce operator added * 3.x: onBackpressureReduce operator added * 3.x: onBackpressureReduce operator added * 3.x: onBackpressureReduce operator added * 3.x: onBackpressureReduce operator added * 3.x: onBackpressureReduce operator * 3.x: onBackpressureReduce operator * 3.x: onBackpressureReduce operator * 3.x: onBackpressureReduce operator --- .../io/reactivex/rxjava3/core/Flowable.java | 34 +++ ...tractBackpressureThrottlingSubscriber.java | 163 ++++++++++++ .../FlowableOnBackpressureLatest.java | 135 +--------- .../FlowableOnBackpressureReduce.java | 68 +++++ .../FlowableOnBackpressureLatestTest.java | 6 + .../FlowableOnBackpressureReduceTest.java | 238 ++++++++++++++++++ 6 files changed, 513 insertions(+), 131 deletions(-) create mode 100644 src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java create mode 100644 src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java create mode 100644 src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 364e83fd58..7aa9c277a8 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -12676,6 +12676,40 @@ public final Flowable onBackpressureLatest() { return RxJavaPlugins.onAssembly(new FlowableOnBackpressureLatest<>(this)); } + /** + * Reduces a sequence of two not emitted values via a function into a single value if the downstream is not ready to receive + * new items (indicated by a lack of {@link Subscription#request(long)} calls from it) and emits this latest + * item when the downstream becomes ready. + *

+ * + *

+ * Note that if the current {@code Flowable} does support backpressure, this operator ignores that capability + * and doesn't propagate any backpressure requests from downstream. + *

+ * Note that due to the nature of how backpressure requests are propagated through subscribeOn/observeOn, + * requesting more than 1 from downstream doesn't guarantee a continuous delivery of {@code onNext} events. + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded + * manner (i.e., not applying backpressure to it).
+ *
Scheduler:
+ *
{@code onBackpressureReduce} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param reducer the bi-function to call when there is more than one non-emitted value to downstream, + * the first argument of the bi-function is previous item and the second one is currently emitting from upstream + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code reducer} is {@code null} + * @since 3.0.9 - experimental + */ + @Experimental + @CheckReturnValue + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Flowable onBackpressureReduce(@NonNull BiFunction reducer) { + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureReduce<>(this, reducer)); + } + /** * Returns a {@code Flowable} instance that if the current {@code Flowable} emits an error, it will emit an {@code onComplete} * and swallow the throwable. diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java new file mode 100644 index 0000000000..e51d870828 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import io.reactivex.rxjava3.core.FlowableSubscriber; +import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Abstract base class for operators that throttle excessive updates from upstream in case if + * downstream {@link Subscriber} is not ready to receive updates + * + * @param the upstream and downstream value type + */ +abstract class AbstractBackpressureThrottlingSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { + + final Subscriber downstream; + + Subscription upstream; + + volatile boolean done; + Throwable error; + + volatile boolean cancelled; + + final AtomicLong requested = new AtomicLong(); + + final AtomicReference current = new AtomicReference<>(); + + AbstractBackpressureThrottlingSubscriber(Subscriber downstream) { + this.downstream = downstream; + } + + @Override + public void onSubscribe(Subscription s) { + if (SubscriptionHelper.validate(this.upstream, s)) { + this.upstream = s; + downstream.onSubscribe(this); + s.request(Long.MAX_VALUE); + } + } + + @Override + abstract public void onNext(T t); + + @Override + public void onError(Throwable t) { + error = t; + done = true; + drain(); + } + + @Override + public void onComplete() { + done = true; + drain(); + } + + @Override + public void request(long n) { + if (SubscriptionHelper.validate(n)) { + BackpressureHelper.add(requested, n); + drain(); + } + } + + @Override + public void cancel() { + if (!cancelled) { + cancelled = true; + upstream.cancel(); + + if (getAndIncrement() == 0) { + current.lazySet(null); + } + } + } + + void drain() { + if (getAndIncrement() != 0) { + return; + } + final Subscriber a = downstream; + int missed = 1; + final AtomicLong r = requested; + final AtomicReference q = current; + + for (;;) { + long e = 0L; + + while (e != r.get()) { + boolean d = done; + T v = q.getAndSet(null); + boolean empty = v == null; + + if (checkTerminated(d, empty, a, q)) { + return; + } + + if (empty) { + break; + } + + a.onNext(v); + + e++; + } + + if (e == r.get() && checkTerminated(done, q.get() == null, a, q)) { + return; + } + + if (e != 0L) { + BackpressureHelper.produced(r, e); + } + + missed = addAndGet(-missed); + if (missed == 0) { + break; + } + } + } + + boolean checkTerminated(boolean d, boolean empty, Subscriber a, AtomicReference q) { + if (cancelled) { + q.lazySet(null); + return true; + } + + if (d) { + Throwable e = error; + if (e != null) { + q.lazySet(null); + a.onError(e); + return true; + } else + if (empty) { + a.onComplete(); + return true; + } + } + + return false; + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java index 6123475ef0..c28aa4c9b1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java @@ -13,13 +13,8 @@ package io.reactivex.rxjava3.internal.operators.flowable; -import java.util.concurrent.atomic.*; - -import org.reactivestreams.*; - -import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; -import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.core.Flowable; +import org.reactivestreams.Subscriber; public final class FlowableOnBackpressureLatest extends AbstractFlowableWithUpstream { @@ -32,34 +27,12 @@ protected void subscribeActual(Subscriber s) { source.subscribe(new BackpressureLatestSubscriber<>(s)); } - static final class BackpressureLatestSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { + static final class BackpressureLatestSubscriber extends AbstractBackpressureThrottlingSubscriber { private static final long serialVersionUID = 163080509307634843L; - final Subscriber downstream; - - Subscription upstream; - - volatile boolean done; - Throwable error; - - volatile boolean cancelled; - - final AtomicLong requested = new AtomicLong(); - - final AtomicReference current = new AtomicReference<>(); - BackpressureLatestSubscriber(Subscriber downstream) { - this.downstream = downstream; - } - - @Override - public void onSubscribe(Subscription s) { - if (SubscriptionHelper.validate(this.upstream, s)) { - this.upstream = s; - downstream.onSubscribe(this); - s.request(Long.MAX_VALUE); - } + super(downstream); } @Override @@ -67,105 +40,5 @@ public void onNext(T t) { current.lazySet(t); drain(); } - - @Override - public void onError(Throwable t) { - error = t; - done = true; - drain(); - } - - @Override - public void onComplete() { - done = true; - drain(); - } - - @Override - public void request(long n) { - if (SubscriptionHelper.validate(n)) { - BackpressureHelper.add(requested, n); - drain(); - } - } - - @Override - public void cancel() { - if (!cancelled) { - cancelled = true; - upstream.cancel(); - - if (getAndIncrement() == 0) { - current.lazySet(null); - } - } - } - - void drain() { - if (getAndIncrement() != 0) { - return; - } - final Subscriber a = downstream; - int missed = 1; - final AtomicLong r = requested; - final AtomicReference q = current; - - for (;;) { - long e = 0L; - - while (e != r.get()) { - boolean d = done; - T v = q.getAndSet(null); - boolean empty = v == null; - - if (checkTerminated(d, empty, a, q)) { - return; - } - - if (empty) { - break; - } - - a.onNext(v); - - e++; - } - - if (e == r.get() && checkTerminated(done, q.get() == null, a, q)) { - return; - } - - if (e != 0L) { - BackpressureHelper.produced(r, e); - } - - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } - } - - boolean checkTerminated(boolean d, boolean empty, Subscriber a, AtomicReference q) { - if (cancelled) { - q.lazySet(null); - return true; - } - - if (d) { - Throwable e = error; - if (e != null) { - q.lazySet(null); - a.onError(e); - return true; - } else - if (empty) { - a.onComplete(); - return true; - } - } - - return false; - } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java new file mode 100644 index 0000000000..e4db04b83f --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.BiFunction; +import org.reactivestreams.Subscriber; + +import java.util.Objects; + +public final class FlowableOnBackpressureReduce extends AbstractFlowableWithUpstream { + + final BiFunction reducer; + + public FlowableOnBackpressureReduce(Flowable source, BiFunction reducer) { + super(source); + this.reducer = Objects.requireNonNull(reducer, "reducer is null"); + } + + @Override + protected void subscribeActual(Subscriber s) { + source.subscribe(new BackpressureReduceSubscriber<>(s, reducer)); + } + + static final class BackpressureReduceSubscriber extends AbstractBackpressureThrottlingSubscriber { + + private static final long serialVersionUID = 821363947659780367L; + + final BiFunction reducer; + + BackpressureReduceSubscriber(Subscriber downstream, BiFunction reducer) { + super(downstream); + this.reducer = reducer; + } + + @Override + public void onNext(T t) { + T v = current.get(); + if (v == null) { + current.lazySet(t); + } else if ((v = current.getAndSet(null)) != null) { + try { + current.lazySet(Objects.requireNonNull(reducer.apply(v, t), "The reducer returned a null value")); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + onError(ex); + cancel(); + return; + } + } else { + current.lazySet(t); + } + drain(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatestTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatestTest.java index 438ebb1415..2b4f490bca 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatestTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatestTest.java @@ -133,6 +133,12 @@ public void onNext(Integer t) { int n = ts.values().size(); System.out.println("testAsynchronousDrop -> " + n); Assert.assertTrue("All events received?", n < m); + int previous = 0; + for (Integer current : ts.values()) { + Assert.assertTrue("The sequence must be increasing [current value=" + previous + + ", previous value=" + current + "]", previous <= current); + previous = current; + } } @Test diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java new file mode 100644 index 0000000000..53d258db9e --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java @@ -0,0 +1,238 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.BiFunction; +import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.TestSubscriberEx; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +public class FlowableOnBackpressureReduceTest extends RxJavaTest { + + static final BiFunction TEST_INT_REDUCER = (previous, current) -> previous + current + 50; + + static final BiFunction TEST_OBJECT_REDUCER = (previous, current) -> current; + + @Test + public void simple() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + Flowable.range(1, 5).onBackpressureReduce(TEST_INT_REDUCER).subscribe(ts); + + ts.assertNoErrors(); + ts.assertTerminated(); + ts.assertValues(1, 2, 3, 4, 5); + } + + @Test + public void simpleError() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + Flowable.range(1, 5).concatWith(Flowable.error(new TestException())) + .onBackpressureReduce(TEST_INT_REDUCER).subscribe(ts); + + ts.assertTerminated(); + ts.assertError(TestException.class); + ts.assertValues(1, 2, 3, 4, 5); + } + + @Test + public void simpleBackpressure() { + TestSubscriber ts = new TestSubscriber<>(2L); + + Flowable.range(1, 5).onBackpressureReduce(TEST_INT_REDUCER).subscribe(ts); + + ts.assertNoErrors(); + ts.assertValues(1, 2); + ts.assertNotComplete(); + } + + @Test + public void synchronousDrop() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); + + source.onBackpressureReduce(TEST_INT_REDUCER).subscribe(ts); + + ts.assertNoValues(); + + source.onNext(1); + ts.request(2); + + ts.assertValue(1); + + source.onNext(2); + + ts.assertValues(1, 2); + + source.onNext(3); + source.onNext(4);//3 + 4 + 50 == 57 + source.onNext(5);//57 + 5 + 50 == 112 + source.onNext(6);//112 + 6 + 50 == 168 + + ts.request(2); + + ts.assertValues(1, 2, 168); + + source.onNext(7); + + ts.assertValues(1, 2, 168, 7); + + source.onNext(8); + source.onNext(9);//8 + 9 + 50 == 67 + source.onComplete(); + + ts.request(1); + + ts.assertValues(1, 2, 168, 7, 67); + ts.assertNoErrors(); + ts.assertTerminated(); + } + + private TestSubscriberEx createDelayedSubscriber() { + return new TestSubscriberEx(1L) { + final Random rnd = new Random(); + + @Override + public void onNext(T t) { + super.onNext(t); + if (rnd.nextDouble() < 0.001) { + try { + Thread.sleep(1); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + request(1); + } + }; + } + + private void assertValuesDropped(TestSubscriberEx ts, int totalValues) { + int n = ts.values().size(); + System.out.println("testAsynchronousDrop -> " + n); + Assert.assertTrue("All events received?", n < totalValues); + } + + private void assertIncreasingSequence(TestSubscriberEx ts) { + int previous = 0; + for (Integer current : ts.values()) { + Assert.assertTrue("The sequence must be increasing [current value=" + previous + + ", previous value=" + current + "]", previous <= current); + previous = current; + } + } + + @Test + public void asynchronousDrop() { + TestSubscriberEx ts = createDelayedSubscriber(); + int m = 100000; + Flowable.range(1, m) + .subscribeOn(Schedulers.computation()) + .onBackpressureReduce((previous, current) -> { + //in that case it works like onBackpressureLatest + //the output sequence of number must be increasing + return current; + }) + .observeOn(Schedulers.io()) + .subscribe(ts); + + ts.awaitDone(2, TimeUnit.SECONDS); + ts.assertTerminated(); + assertValuesDropped(ts, m); + assertIncreasingSequence(ts); + } + + @Test + public void asynchronousDrop2() { + TestSubscriberEx ts = createDelayedSubscriber(); + int m = 100000; + Flowable.rangeLong(1, m) + .subscribeOn(Schedulers.computation()) + .onBackpressureReduce(Long::sum) + .observeOn(Schedulers.io()) + .subscribe(ts); + + ts.awaitDone(2, TimeUnit.SECONDS); + ts.assertTerminated(); + assertValuesDropped(ts, m); + long sum = 0; + for (Long i : ts.values()) { + sum += i; + } + //sum = (A1 + An) * n / 2 = 100_001 * 50_000 = 50_000_00000 + 50_000 = 50_000_50_000 + Assert.assertEquals("Wrong sum: " + sum, 5000050000L, sum); + } + + @Test + public void nullPointerFromReducer() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0); + source.onBackpressureReduce((l, r) -> null).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, NullPointerException.class, "The reducer returned a null value"); + } + + @Test + public void exceptionFromReducer() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0); + source.onBackpressureReduce((l, r) -> { + throw new IOException("Test exception"); + }).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, IOException.class, "Test exception"); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.onBackpressureReduce(TEST_OBJECT_REDUCER)); + } + + @Test + public void take() { + Flowable.just(1, 2) + .onBackpressureReduce(TEST_INT_REDUCER) + .take(1) + .test() + .assertResult(1); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Flowable.never().onBackpressureReduce(TEST_OBJECT_REDUCER)); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().onBackpressureReduce(TEST_OBJECT_REDUCER)); + } +} From e0122a4c264761ae9eaf969740acf17a0678ad7b Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 10 Dec 2020 00:52:30 +0300 Subject: [PATCH 098/521] 3.x: Add onBackpressureReduce operator (#7129) * onBackpressureReduce operator added * onBackpressureReduce operator added * onBackpressureReduce operator added * 3.x: onBackpressureReduce operator added * 3.x: onBackpressureReduce operator added * 3.x: onBackpressureReduce operator added * 3.x: onBackpressureReduce operator added * 3.x: onBackpressureReduce operator added * 3.x: onBackpressureReduce operator * 3.x: onBackpressureReduce operator * 3.x: onBackpressureReduce operator * 3.x: onBackpressureReduce operator * 3.x: add onBackpressureReduce operator * 3.x: add onBackpressureReduce operator * 3.x: add onBackpressureReduce operator --- .../io/reactivex/rxjava3/core/Flowable.java | 43 +++ ...tractBackpressureThrottlingSubscriber.java | 19 +- .../FlowableOnBackpressureLatest.java | 2 +- .../FlowableOnBackpressureReduce.java | 11 +- .../FlowableOnBackpressureReduceWith.java | 83 +++++ .../FlowableOnBackpressureReduceTest.java | 5 +- .../FlowableOnBackpressureReduceWithTest.java | 307 ++++++++++++++++++ 7 files changed, 452 insertions(+), 18 deletions(-) create mode 100644 src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWith.java create mode 100644 src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWithTest.java diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 7aa9c277a8..62879af477 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -12707,9 +12707,52 @@ public final Flowable onBackpressureLatest() { @SchedulerSupport(SchedulerSupport.NONE) @NonNull public final Flowable onBackpressureReduce(@NonNull BiFunction reducer) { + Objects.requireNonNull(reducer, "reducer is null"); return RxJavaPlugins.onAssembly(new FlowableOnBackpressureReduce<>(this, reducer)); } + /** + * Reduces upstream values into an aggregate value, provided by a supplier and combined via a reducer function, + * while the downstream is not ready to receive items, then emits this aggregate value when the downstream becomes ready. + *

+ * + *

+ * Note that even if the downstream is ready to receive an item, the upstream item will always be aggregated into the output type, + * calling both the supplier and the reducer to produce the output value. + *

+ * Note that if the current {@code Flowable} does support backpressure, this operator ignores that capability + * and doesn't propagate any backpressure requests from downstream. + *

+ * Note that due to the nature of how backpressure requests are propagated through subscribeOn/observeOn, + * requesting more than 1 from downstream doesn't guarantee a continuous delivery of {@code onNext} events. + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded + * manner (i.e., not applying backpressure to it).
+ *
Scheduler:
+ *
{@code onBackpressureReduce} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param supplier the factory to call to create new item of type R to pass it as the first argument to {@code reducer}. + * It is called when previous returned value by {@code reducer} already sent to downstream or the very first update from upstream received. + * @param reducer the bi-function to call to reduce excessive updates which downstream is not ready to receive. + * The first argument of type R is the object returned by {@code supplier} or result of previous {@code reducer} invocation. + * The second argument of type T is the current update from upstream. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code supplier} or {@code reducer} is {@code null} + * @see #onBackpressureReduce(BiFunction) + * @since 3.0.9 - experimental + */ + @Experimental + @CheckReturnValue + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Flowable onBackpressureReduce(@NonNull Supplier supplier, @NonNull BiFunction reducer) { + Objects.requireNonNull(supplier, "supplier is null"); + Objects.requireNonNull(reducer, "reducer is null"); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureReduceWith<>(this, supplier, reducer)); + } + /** * Returns a {@code Flowable} instance that if the current {@code Flowable} emits an error, it will emit an {@code onComplete} * and swallow the throwable. diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java index e51d870828..f04c0183ce 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java @@ -28,11 +28,12 @@ * Abstract base class for operators that throttle excessive updates from upstream in case if * downstream {@link Subscriber} is not ready to receive updates * - * @param the upstream and downstream value type + * @param the upstream value type + * @param the downstream value type */ -abstract class AbstractBackpressureThrottlingSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { +abstract class AbstractBackpressureThrottlingSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { - final Subscriber downstream; + final Subscriber downstream; Subscription upstream; @@ -43,9 +44,9 @@ abstract class AbstractBackpressureThrottlingSubscriber extends AtomicInteger final AtomicLong requested = new AtomicLong(); - final AtomicReference current = new AtomicReference<>(); + final AtomicReference current = new AtomicReference<>(); - AbstractBackpressureThrottlingSubscriber(Subscriber downstream) { + AbstractBackpressureThrottlingSubscriber(Subscriber downstream) { this.downstream = downstream; } @@ -98,17 +99,17 @@ void drain() { if (getAndIncrement() != 0) { return; } - final Subscriber a = downstream; + final Subscriber a = downstream; int missed = 1; final AtomicLong r = requested; - final AtomicReference q = current; + final AtomicReference q = current; for (;;) { long e = 0L; while (e != r.get()) { boolean d = done; - T v = q.getAndSet(null); + R v = q.getAndSet(null); boolean empty = v == null; if (checkTerminated(d, empty, a, q)) { @@ -139,7 +140,7 @@ void drain() { } } - boolean checkTerminated(boolean d, boolean empty, Subscriber a, AtomicReference q) { + boolean checkTerminated(boolean d, boolean empty, Subscriber a, AtomicReference q) { if (cancelled) { q.lazySet(null); return true; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java index c28aa4c9b1..cbbca5c9f2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java @@ -27,7 +27,7 @@ protected void subscribeActual(Subscriber s) { source.subscribe(new BackpressureLatestSubscriber<>(s)); } - static final class BackpressureLatestSubscriber extends AbstractBackpressureThrottlingSubscriber { + static final class BackpressureLatestSubscriber extends AbstractBackpressureThrottlingSubscriber { private static final long serialVersionUID = 163080509307634843L; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java index e4db04b83f..708070f60c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; @@ -24,23 +25,23 @@ public final class FlowableOnBackpressureReduce extends AbstractFlowableWithU final BiFunction reducer; - public FlowableOnBackpressureReduce(Flowable source, BiFunction reducer) { + public FlowableOnBackpressureReduce(@NonNull Flowable source, @NonNull BiFunction reducer) { super(source); - this.reducer = Objects.requireNonNull(reducer, "reducer is null"); + this.reducer = reducer; } @Override - protected void subscribeActual(Subscriber s) { + protected void subscribeActual(@NonNull Subscriber s) { source.subscribe(new BackpressureReduceSubscriber<>(s, reducer)); } - static final class BackpressureReduceSubscriber extends AbstractBackpressureThrottlingSubscriber { + static final class BackpressureReduceSubscriber extends AbstractBackpressureThrottlingSubscriber { private static final long serialVersionUID = 821363947659780367L; final BiFunction reducer; - BackpressureReduceSubscriber(Subscriber downstream, BiFunction reducer) { + BackpressureReduceSubscriber(@NonNull Subscriber downstream, @NonNull BiFunction reducer) { super(downstream); this.reducer = reducer; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWith.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWith.java new file mode 100644 index 0000000000..064b952377 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWith.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.BiFunction; +import io.reactivex.rxjava3.functions.Supplier; +import org.reactivestreams.Subscriber; + +import java.util.Objects; + +public final class FlowableOnBackpressureReduceWith extends AbstractFlowableWithUpstream { + + final BiFunction reducer; + final Supplier supplier; + + public FlowableOnBackpressureReduceWith(@NonNull Flowable source, + @NonNull Supplier supplier, + @NonNull BiFunction reducer) { + super(source); + this.reducer = reducer; + this.supplier = supplier; + } + + @Override + protected void subscribeActual(@NonNull Subscriber s) { + source.subscribe(new BackpressureReduceWithSubscriber<>(s, supplier, reducer)); + } + + static final class BackpressureReduceWithSubscriber extends AbstractBackpressureThrottlingSubscriber { + + private static final long serialVersionUID = 8255923705960622424L; + + final BiFunction reducer; + final Supplier supplier; + + BackpressureReduceWithSubscriber(@NonNull Subscriber downstream, + @NonNull Supplier supplier, + @NonNull BiFunction reducer) { + super(downstream); + this.reducer = reducer; + this.supplier = supplier; + } + + @Override + public void onNext(T t) { + R v = current.get(); + try { + if (v == null) { + current.lazySet(Objects.requireNonNull( + reducer.apply(Objects.requireNonNull(supplier.get(), "The supplier returned a null value"), t), + "The reducer returned a null value" + )); + } else if ((v = current.getAndSet(null)) != null) { + current.lazySet(Objects.requireNonNull(reducer.apply(v, t), "The reducer returned a null value")); + } else { + current.lazySet(Objects.requireNonNull( + reducer.apply(Objects.requireNonNull(supplier.get(), "The supplier returned a null value"), t), + "The reducer returned a null value" + )); + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + onError(ex); + cancel(); + } + drain(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java index 53d258db9e..67436322ef 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java @@ -25,7 +25,6 @@ import org.junit.Assert; import org.junit.Test; -import java.io.IOException; import java.util.Random; import java.util.concurrent.TimeUnit; @@ -203,13 +202,13 @@ public void exceptionFromReducer() { PublishProcessor source = PublishProcessor.create(); TestSubscriberEx ts = new TestSubscriberEx<>(0); source.onBackpressureReduce((l, r) -> { - throw new IOException("Test exception"); + throw new TestException("Test exception"); }).subscribe(ts); source.onNext(1); source.onNext(2); - TestHelper.assertError(ts.errors(), 0, IOException.class, "Test exception"); + TestHelper.assertError(ts.errors(), 0, TestException.class, "Test exception"); } @Test diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWithTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWithTest.java new file mode 100644 index 0000000000..4dec546044 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWithTest.java @@ -0,0 +1,307 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.BiFunction; +import io.reactivex.rxjava3.functions.Supplier; +import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.TestSubscriberEx; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class FlowableOnBackpressureReduceWithTest extends RxJavaTest { + + private static BiFunction, T, List> createTestReducer() { + return (list, number) -> { + list.add(number); + return list; + }; + } + + private static Supplier> createTestSupplier() { + return ArrayList::new; + } + + @Test + public void simple() { + TestSubscriberEx> ts = new TestSubscriberEx<>(); + + Flowable.range(1, 5).onBackpressureReduce(createTestSupplier(), createTestReducer()).subscribe(ts); + + ts.assertNoErrors(); + ts.assertTerminated(); + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2), + Collections.singletonList(3), + Collections.singletonList(4), + Collections.singletonList(5) + ); + } + + @Test + public void simpleError() { + TestSubscriberEx> ts = new TestSubscriberEx<>(); + + Flowable.range(1, 5).concatWith(Flowable.error(new TestException())) + .onBackpressureReduce(createTestSupplier(), createTestReducer()).subscribe(ts); + + ts.assertTerminated(); + ts.assertError(TestException.class); + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2), + Collections.singletonList(3), + Collections.singletonList(4), + Collections.singletonList(5) + ); + } + + @Test + public void simpleBackpressure() { + TestSubscriberEx> ts = new TestSubscriberEx<>(2L); + + Flowable.range(1, 5).onBackpressureReduce(createTestSupplier(), createTestReducer()).subscribe(ts); + + ts.assertNoErrors(); + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2) + ); + ts.assertNotComplete(); + } + + @Test + public void synchronousDrop() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx> ts = new TestSubscriberEx<>(0L); + + source.onBackpressureReduce(createTestSupplier(), createTestReducer()).subscribe(ts); + + ts.assertNoValues(); + + source.onNext(1); + ts.request(2); + + ts.assertValues(Collections.singletonList(1)); + + source.onNext(2); + + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2) + ); + + source.onNext(3); + source.onNext(4); + source.onNext(5); + source.onNext(6); + + ts.request(2); + + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2), + Arrays.asList(3, 4, 5, 6) + ); + + source.onNext(7); + + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2), + Arrays.asList(3, 4, 5, 6), + Collections.singletonList(7) + ); + + source.onNext(8); + source.onNext(9); + source.onComplete(); + + ts.request(1); + + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2), + Arrays.asList(3, 4, 5, 6), + Collections.singletonList(7), + Arrays.asList(8, 9) + ); + ts.assertNoErrors(); + ts.assertTerminated(); + } + + private TestSubscriberEx createDelayedSubscriber() { + return new TestSubscriberEx(1L) { + final Random rnd = new Random(); + + @Override + public void onNext(T t) { + super.onNext(t); + if (rnd.nextDouble() < 0.001) { + try { + Thread.sleep(1); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + request(1); + } + }; + } + + private void assertValuesDropped(TestSubscriberEx ts, int totalValues) { + int n = ts.values().size(); + System.out.println("testAsynchronousDrop -> " + n); + Assert.assertTrue("All events received?", n < totalValues); + } + + private void assertIncreasingSequence(TestSubscriberEx ts) { + int previous = 0; + for (Integer current : ts.values()) { + Assert.assertTrue("The sequence must be increasing [current value=" + previous + + ", previous value=" + current + "]", previous <= current); + previous = current; + } + } + + @Test + public void asynchronousDrop() { + TestSubscriberEx ts = createDelayedSubscriber(); + int m = 100000; + Flowable.range(1, m) + .subscribeOn(Schedulers.computation()) + .onBackpressureReduce((Supplier>) Collections::emptyList, (list, current) -> { + //in that case it works like onBackpressureLatest + //the output sequence of number must be increasing + return Collections.singletonList(current); + }) + .observeOn(Schedulers.io()) + .concatMap(Flowable::fromIterable) + .subscribe(ts); + + ts.awaitDone(2, TimeUnit.SECONDS); + ts.assertTerminated(); + assertValuesDropped(ts, m); + assertIncreasingSequence(ts); + } + + @Test + public void asynchronousDrop2() { + TestSubscriberEx ts = createDelayedSubscriber(); + int m = 100000; + Flowable.rangeLong(1, m) + .subscribeOn(Schedulers.computation()) + .onBackpressureReduce(createTestSupplier(), createTestReducer()) + .observeOn(Schedulers.io()) + .concatMap(list -> Flowable.just(list.stream().reduce(Long::sum).orElseThrow(() -> { + throw new IllegalArgumentException("No value in list"); + }))) + .subscribe(ts); + + ts.awaitDone(2, TimeUnit.SECONDS); + ts.assertTerminated(); + assertValuesDropped(ts, m); + long sum = 0; + for (Long i : ts.values()) { + sum += i; + } + //sum = (A1 + An) * n / 2 = 100_001 * 50_000 = 50_000_00000 + 50_000 = 50_000_50_000 + Assert.assertEquals("Wrong sum: " + sum, 5000050000L, sum); + } + + @Test + public void nullPointerFromReducer() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx> ts = new TestSubscriberEx<>(0L); + source.onBackpressureReduce(createTestSupplier(), (BiFunction, ? super Integer, List>) (list, number) -> null).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, NullPointerException.class, "The reducer returned a null value"); + } + + @Test + public void nullPointerFromSupplier() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx> ts = new TestSubscriberEx<>(0L); + source.onBackpressureReduce(() -> null, createTestReducer()).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, NullPointerException.class, "The supplier returned a null value"); + } + + @Test + public void exceptionFromReducer() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx> ts = new TestSubscriberEx<>(0L); + source.onBackpressureReduce(createTestSupplier(), (BiFunction, ? super Integer, List>) (l, r) -> { + throw new TestException("Test exception"); + }).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, TestException.class, "Test exception"); + } + + @Test + public void exceptionFromSupplier() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx> ts = new TestSubscriberEx<>(0L); + source.onBackpressureReduce(() -> { + throw new TestException("Test exception"); + }, createTestReducer()).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, TestException.class, "Test exception"); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.onBackpressureReduce(createTestSupplier(), createTestReducer())); + } + + @Test + public void take() { + Flowable.just(1, 2) + .onBackpressureReduce(createTestSupplier(), createTestReducer()) + .take(1) + .test() + .assertResult(Collections.singletonList(1)); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Flowable.never().onBackpressureReduce(createTestSupplier(), createTestReducer())); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().onBackpressureReduce(createTestSupplier(), createTestReducer())); + } +} From dde2c0e7b0435b4195d02699ef4c8f8d666480e2 Mon Sep 17 00:00:00 2001 From: akarnokd Date: Thu, 10 Dec 2020 11:41:30 +0100 Subject: [PATCH 099/521] Fix code style --- .../java/io/reactivex/rxjava3/core/Flowable.java | 12 ++++++++---- .../AbstractBackpressureThrottlingSubscriber.java | 7 ++++--- .../flowable/FlowableOnBackpressureReduceTest.java | 8 ++++---- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 62879af477..13a7e52b51 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -12696,10 +12696,12 @@ public final Flowable onBackpressureLatest() { *

{@code onBackpressureReduce} does not operate by default on a particular {@link Scheduler}.
* * @param reducer the bi-function to call when there is more than one non-emitted value to downstream, - * the first argument of the bi-function is previous item and the second one is currently emitting from upstream + * the first argument of the bi-function is previous item and the second one is currently + * emitting from upstream * @return the new {@code Flowable} instance * @throws NullPointerException if {@code reducer} is {@code null} * @since 3.0.9 - experimental + * @see #onBackpressureReduce(Supplier, BiFunction) */ @Experimental @CheckReturnValue @@ -12732,11 +12734,13 @@ public final Flowable onBackpressureReduce(@NonNull BiFunction reduc *
Scheduler:
*
{@code onBackpressureReduce} does not operate by default on a particular {@link Scheduler}.
* + * @param the aggregate type emitted when the downstream requests more items * @param supplier the factory to call to create new item of type R to pass it as the first argument to {@code reducer}. - * It is called when previous returned value by {@code reducer} already sent to downstream or the very first update from upstream received. + * It is called when previous returned value by {@code reducer} already sent to + * downstream or the very first update from upstream received. * @param reducer the bi-function to call to reduce excessive updates which downstream is not ready to receive. - * The first argument of type R is the object returned by {@code supplier} or result of previous {@code reducer} invocation. - * The second argument of type T is the current update from upstream. + * The first argument of type R is the object returned by {@code supplier} or result of previous + * {@code reducer} invocation. The second argument of type T is the current update from upstream. * @return the new {@code Flowable} instance * @throws NullPointerException if {@code supplier} or {@code reducer} is {@code null} * @see #onBackpressureReduce(BiFunction) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java index f04c0183ce..36ba2992cf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java @@ -16,7 +16,6 @@ import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; -import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -26,13 +25,15 @@ /** * Abstract base class for operators that throttle excessive updates from upstream in case if - * downstream {@link Subscriber} is not ready to receive updates + * downstream {@link Subscriber} is not ready to receive updates. * * @param the upstream value type * @param the downstream value type */ abstract class AbstractBackpressureThrottlingSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { + private static final long serialVersionUID = -5050301752721603566L; + final Subscriber downstream; Subscription upstream; @@ -60,7 +61,7 @@ public void onSubscribe(Subscription s) { } @Override - abstract public void onNext(T t); + public abstract void onNext(T t); @Override public void onError(Throwable t) { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java index 67436322ef..e74b6af8af 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java @@ -87,9 +87,9 @@ public void synchronousDrop() { ts.assertValues(1, 2); source.onNext(3); - source.onNext(4);//3 + 4 + 50 == 57 - source.onNext(5);//57 + 5 + 50 == 112 - source.onNext(6);//112 + 6 + 50 == 168 + source.onNext(4); //3 + 4 + 50 == 57 + source.onNext(5); //57 + 5 + 50 == 112 + source.onNext(6); //112 + 6 + 50 == 168 ts.request(2); @@ -100,7 +100,7 @@ public void synchronousDrop() { ts.assertValues(1, 2, 168, 7); source.onNext(8); - source.onNext(9);//8 + 9 + 50 == 67 + source.onNext(9); //8 + 9 + 50 == 67 source.onComplete(); ts.request(1); From 17ba82c4ab9f9d4cc66f8dfd90b6930d1ac9aaeb Mon Sep 17 00:00:00 2001 From: Jean Lecordier <47030586+jlecordier@users.noreply.github.com> Date: Mon, 14 Dec 2020 07:22:05 +0100 Subject: [PATCH 100/521] adding 1.x 2.x 3.x javadoc to wiki sidebar (#7133) * fix javadoc url from 2.x to 3.x * using suggested fix by reviewer --- docs/_Sidebar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_Sidebar.md b/docs/_Sidebar.md index 32e201fc46..22961acec3 100644 --- a/docs/_Sidebar.md +++ b/docs/_Sidebar.md @@ -27,6 +27,6 @@ * [Writing operators](https://github.com/ReactiveX/RxJava/wiki/Writing-operators-for-2.0) * [Backpressure](https://github.com/ReactiveX/RxJava/wiki/Backpressure-(2.0)) * [another explanation](https://github.com/ReactiveX/RxJava/wiki/Backpressure) -* [JavaDoc](http://reactivex.io/RxJava/2.x/javadoc) +* JavaDoc: [1.x](http://reactivex.io/RxJava/1.x/javadoc), [2.x](http://reactivex.io/RxJava/2.x/javadoc), [3.x](http://reactivex.io/RxJava/3.x/javadoc) * [Coming from RxJava 1](https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0) * [Additional Reading](https://github.com/ReactiveX/RxJava/wiki/Additional-Reading) From 0811a65aabbf40ef2bc2392c38193507a0eb9801 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 15 Dec 2020 08:58:55 +0100 Subject: [PATCH 101/521] Bump guava from 30.0-jre to 30.1-jre (#7134) Bumps [guava](https://github.com/google/guava) from 30.0-jre to 30.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b371aea34f..894364b0f0 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { ext.mockitoVersion = "3.6.28" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.2" - ext.guavaVersion = "30.0-jre" + ext.guavaVersion = "30.1-jre" ext.jacocoVersion = "0.8.4" ext.animalSnifferVersion = "1.5.2" ext.licenseVersion = "0.15.0" From ad50bcda192a2e73ad1fc3532ac27362d50dc898 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 30 Dec 2020 19:39:43 +0100 Subject: [PATCH 102/521] Bump build-info-extractor-gradle from 4.18.2 to 4.18.3 (#7140) Bumps build-info-extractor-gradle from 4.18.2 to 4.18.3. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 894364b0f0..09a68fa151 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.2" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" - ext.jfrogExtractorVersion = "4.18.2" + ext.jfrogExtractorVersion = "4.18.3" ext.bndVersion = "5.2.0" ext.checkstyleVersion = "8.26" From b75c1858b7c223a06b20c090ae86bf4976867e90 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 5 Jan 2021 08:21:51 +0100 Subject: [PATCH 103/521] Bump mockito-core from 3.6.28 to 3.7.0 (#7141) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.6.28 to 3.7.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.6.28...v3.7.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 09a68fa151..9379b48d8f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13.1" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.6.28" + ext.mockitoVersion = "3.7.0" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.2" ext.guavaVersion = "30.1-jre" From a55f43e9f3d82091cb0266179afead151361b00d Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 8 Jan 2021 11:05:10 +0100 Subject: [PATCH 104/521] 3.x: Update marbles of amb(), ambArray() and ambWith() (#7144) * 3.x: Update marbles of amb(), ambArray() and ambWith() * Fix image size --- .../io/reactivex/rxjava3/core/Flowable.java | 31 +++++++++++++++++-- .../io/reactivex/rxjava3/core/Observable.java | 31 +++++++++++++++++-- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 13a7e52b51..89de8993ae 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -163,13 +163,21 @@ public abstract class Flowable<@NonNull T> implements Publisher { * Mirrors the one {@link Publisher} in an {@link Iterable} of several {@code Publisher}s that first either emits an item or sends * a termination notification. *

- * + * + *

+ * When one of the {@code Publisher}s signal an item or terminates first, all subscriptions to the other + * {@code Publisher}s are canceled. *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by the winning * {@code Publisher}'s backpressure behavior.
*
Scheduler:
*
{@code amb} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If any of the losing {@code Publisher}s signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param the common element type @@ -193,13 +201,21 @@ public static Flowable amb(@NonNull Iterable<@NonNull ? extends Publisher * Mirrors the one {@link Publisher} in an array of several {@code Publisher}s that first either emits an item or sends * a termination notification. *

- * + * + *

+ * When one of the {@code Publisher}s signal an item or terminates first, all subscriptions to the other + * {@code Publisher}s are canceled. *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by the winning * {@code Publisher}'s backpressure behavior.
*
Scheduler:
*
{@code ambArray} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If any of the losing {@code Publisher}s signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param the common element type @@ -5927,13 +5943,22 @@ public final Single all(@NonNull Predicate predicate) { * Mirrors the {@link Publisher} (current or provided) that first either emits an item or sends a termination * notification. *

- * + * + *

+ * When the current {@code Flowable} signals an item or terminates first, the subscription to the other + * {@code Publisher} is canceled. If the other {@code Publisher} signals an item or terminates first, + * the subscription to the current {@code Flowable} is canceled. *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by the winning * {@code Publisher}'s backpressure behavior.
*
Scheduler:
*
{@code ambWith} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If the losing {@code Publisher} signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param other diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index a1e34e9309..7fe071a5b2 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -105,10 +105,18 @@ public abstract class Observable<@NonNull T> implements ObservableSource { * Mirrors the one {@link ObservableSource} in an {@link Iterable} of several {@code ObservableSource}s that first either emits an item or sends * a termination notification. *

- * + * + *

+ * When one of the {@code ObservableSource}s signal an item or terminates first, all subscriptions to the other + * {@code ObservableSource}s are disposed. *

*
Scheduler:
*
{@code amb} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If any of the losing {@code ObservableSource}s signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param the common element type @@ -131,10 +139,18 @@ public static Observable amb(@NonNull Iterable<@NonNull ? extends Observa * Mirrors the one {@link ObservableSource} in an array of several {@code ObservableSource}s that first either emits an item or sends * a termination notification. *

- * + * + *

+ * When one of the {@code ObservableSource}s signal an item or terminates first, all subscriptions to the other + * {@code ObservableSource}s are disposed. *

*
Scheduler:
*
{@code ambArray} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If any of the losing {@code ObservableSource}s signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param the common element type @@ -5350,10 +5366,19 @@ public final Single all(@NonNull Predicate predicate) { * Mirrors the current {@code Observable} or the other {@link ObservableSource} provided of which the first either emits an item or sends a termination * notification. *

- * + * + *

+ * When the current {@code Observable} signals an item or terminates first, the subscription to the other + * {@code ObservableSource} is disposed. If the other {@code ObservableSource} signals an item or terminates first, + * the subscription to the current {@code Observable} is disposed. *

*
Scheduler:
*
{@code ambWith} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If the losing {@code ObservableSource} signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param other From 54850d0d344434022070fb034cdb7bda59b17b2b Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 8 Jan 2021 11:23:02 +0100 Subject: [PATCH 105/521] 3.x: Fix take() mentioning the old limit() operator (#7145) --- src/main/java/io/reactivex/rxjava3/core/Flowable.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 89de8993ae..4d6afb2249 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -16436,8 +16436,8 @@ public final Flowable switchMapSingleDelayError(@NonNull Function * *

@@ -16450,15 +16450,15 @@ public final Flowable switchMapSingleDelayError(@NonNull Function * The operator requests at most the given {@code count} of items from upstream even - * if the downstream requests more than that. For example, given a {@code limit(5)}, + * if the downstream requests more than that. For example, given a {@code take(5)}, * if the downstream requests 1, a request of 1 is submitted to the upstream * and the operator remembers that only 4 items can be requested now on. A request * of 5 at this point will request 4 from the upstream and any subsequent requests will * be ignored. *

- * Note that requests are negotiated on an operator boundary and {@code limit}'s amount + * Note that requests are negotiated on an operator boundary and {@code take}'s amount * may not be preserved further upstream. For example, - * {@code source.observeOn(Schedulers.computation()).limit(5)} will still request the + * {@code source.observeOn(Schedulers.computation()).take(5)} will still request the * default (128) elements from the given {@code source}. *

*
Backpressure:
From e3a0302161155090b637a13d9bbce1fc6dd638fe Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 18 Jan 2021 09:44:58 +0100 Subject: [PATCH 106/521] Bump mockito-core from 3.7.0 to 3.7.7 (#7152) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.7.0 to 3.7.7. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.7.0...v3.7.7) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9379b48d8f..3a8cb8ddaa 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13.1" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.7.0" + ext.mockitoVersion = "3.7.7" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.2" ext.guavaVersion = "30.1-jre" From b77aa1ed55d9fc4879d25f26d128d1b6b322b265 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Tue, 19 Jan 2021 17:41:36 +0100 Subject: [PATCH 107/521] 3.x: onReduceBackpressure internals cleanup (#7151) * 3.x: onReduceBackpressure internals cleanup * Update FlowableOnBackpressureReduceTest.java * Update FlowableOnBackpressureReduceWithTest.java --- .../FlowableOnBackpressureReduce.java | 9 +++---- .../FlowableOnBackpressureReduceWith.java | 13 +++++----- .../FlowableOnBackpressureReduceTest.java | 24 +++++++++++++++++++ .../FlowableOnBackpressureReduceWithTest.java | 24 +++++++++++++++++++ 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java index 708070f60c..8d38e9822a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java @@ -49,19 +49,20 @@ static final class BackpressureReduceSubscriber extends AbstractBackpressureT @Override public void onNext(T t) { T v = current.get(); + if (v != null) { + v = current.getAndSet(null); + } if (v == null) { current.lazySet(t); - } else if ((v = current.getAndSet(null)) != null) { + } else { try { current.lazySet(Objects.requireNonNull(reducer.apply(v, t), "The reducer returned a null value")); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); + upstream.cancel(); onError(ex); - cancel(); return; } - } else { - current.lazySet(t); } drain(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWith.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWith.java index 064b952377..904be881ec 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWith.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWith.java @@ -58,24 +58,23 @@ static final class BackpressureReduceWithSubscriber extends AbstractBackpr @Override public void onNext(T t) { R v = current.get(); + if (v != null) { + v = current.getAndSet(null); + } try { if (v == null) { current.lazySet(Objects.requireNonNull( reducer.apply(Objects.requireNonNull(supplier.get(), "The supplier returned a null value"), t), "The reducer returned a null value" )); - } else if ((v = current.getAndSet(null)) != null) { - current.lazySet(Objects.requireNonNull(reducer.apply(v, t), "The reducer returned a null value")); } else { - current.lazySet(Objects.requireNonNull( - reducer.apply(Objects.requireNonNull(supplier.get(), "The supplier returned a null value"), t), - "The reducer returned a null value" - )); + current.lazySet(Objects.requireNonNull(reducer.apply(v, t), "The reducer returned a null value")); } } catch (Throwable ex) { Exceptions.throwIfFatal(ex); + upstream.cancel(); onError(ex); - cancel(); + return; } drain(); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java index e74b6af8af..ac1950630a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java @@ -110,6 +110,30 @@ public void synchronousDrop() { ts.assertTerminated(); } + @Test + public void reduceBackpressuredSync() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); + + source.onBackpressureReduce(Integer::sum).subscribe(ts); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + ts.request(1); + + ts.assertValuesOnly(6); + + source.onNext(4); + source.onComplete(); + + ts.assertValuesOnly(6); + + ts.request(1); + ts.assertResult(6, 4); + } + private TestSubscriberEx createDelayedSubscriber() { return new TestSubscriberEx(1L) { final Random rnd = new Random(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWithTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWithTest.java index 4dec546044..e0a77692ec 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWithTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWithTest.java @@ -90,6 +90,30 @@ public void simpleBackpressure() { ts.assertNotComplete(); } + @Test + public void reduceBackpressuredSync() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); + + source.onBackpressureReduce(() -> 0, Integer::sum).subscribe(ts); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + ts.request(1); + + ts.assertValuesOnly(6); + + source.onNext(4); + source.onComplete(); + + ts.assertValuesOnly(6); + + ts.request(1); + ts.assertResult(6, 4); + } + @Test public void synchronousDrop() { PublishProcessor source = PublishProcessor.create(); From af297c444dd54af6b335275dac2149bc514acc8e Mon Sep 17 00:00:00 2001 From: David Karnok Date: Tue, 19 Jan 2021 18:23:31 +0100 Subject: [PATCH 108/521] 3.x: Schedulers.from vs. RejectedExecutionException (#7150) --- .../rxjava3/schedulers/Schedulers.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java index 4d4c3517a5..ad7ff69bc7 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java @@ -340,6 +340,16 @@ public static Scheduler single() { * } * *

+ * Note that the provided {@code Executor} should avoid throwing a {@link RejectedExecutionException} + * (for example, by shutting it down prematurely or using a bounded-queue {@code ExecutorService}) + * because such circumstances prevent RxJava from progressing flow-related activities correctly. + * If the {@link Executor#execute(Runnable)} or {@link ExecutorService#submit(Callable)} throws, + * the {@code RejectedExecutionException} is routed to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-reladed problems, it is recommended + * all flows using the returned {@code Scheduler} to be canceled/disposed before the underlying + * {@code Executor} is shut down. To avoid problems due to the {@code Executor} having a bounded-queue, + * it is recommended to rephrase the flow to utilize backpressure as the means to limit outstanding work. + *

* This type of scheduler is less sensitive to leaking {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} instances, although * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". @@ -404,6 +414,16 @@ public static Scheduler from(@NonNull Executor executor) { * } * *

+ * Note that the provided {@code Executor} should avoid throwing a {@link RejectedExecutionException} + * (for example, by shutting it down prematurely or using a bounded-queue {@code ExecutorService}) + * because such circumstances prevent RxJava from progressing flow-related activities correctly. + * If the {@link Executor#execute(Runnable)} or {@link ExecutorService#submit(Callable)} throws, + * the {@code RejectedExecutionException} is routed to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-reladed problems, it is recommended + * all flows using the returned {@code Scheduler} to be canceled/disposed before the underlying + * {@code Executor} is shut down. To avoid problems due to the {@code Executor} having a bounded-queue, + * it is recommended to rephrase the flow to utilize backpressure as the means to limit outstanding work. + *

* This type of scheduler is less sensitive to leaking {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} instances, although * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". @@ -474,6 +494,16 @@ public static Scheduler from(@NonNull Executor executor, boolean interruptibleWo * } * *

+ * Note that the provided {@code Executor} should avoid throwing a {@link RejectedExecutionException} + * (for example, by shutting it down prematurely or using a bounded-queue {@code ExecutorService}) + * because such circumstances prevent RxJava from progressing flow-related activities correctly. + * If the {@link Executor#execute(Runnable)} or {@link ExecutorService#submit(Callable)} throws, + * the {@code RejectedExecutionException} is routed to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-reladed problems, it is recommended + * all flows using the returned {@code Scheduler} to be canceled/disposed before the underlying + * {@code Executor} is shut down. To avoid problems due to the {@code Executor} having a bounded-queue, + * it is recommended to rephrase the flow to utilize backpressure as the means to limit outstanding work. + *

* This type of scheduler is less sensitive to leaking {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} instances, although * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". From e46ce76b95653006a709f230a71768a731c7d830 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 25 Jan 2021 08:42:07 +0100 Subject: [PATCH 109/521] Bump build-info-extractor-gradle from 4.18.3 to 4.19.0 (#7159) Bumps build-info-extractor-gradle from 4.18.3 to 4.19.0. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3a8cb8ddaa..1fb259d600 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.2" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" - ext.jfrogExtractorVersion = "4.18.3" + ext.jfrogExtractorVersion = "4.19.0" ext.bndVersion = "5.2.0" ext.checkstyleVersion = "8.26" From e65763506e6641c4672337e0311407640f7f188d Mon Sep 17 00:00:00 2001 From: Sergio Garcia Date: Mon, 25 Jan 2021 20:09:18 +0000 Subject: [PATCH 110/521] Io Scheduler, Scheduled worker release. (#7160) * Support for scheduled release of threads in Io Scheduler * Test for rx3.io-scheduled-release * Fix tests, addressed Javadoc feedback --- .../internal/schedulers/IoScheduler.java | 21 ++++++-- .../rxjava3/schedulers/Schedulers.java | 19 +++++++ .../schedulers/IoScheduledReleaseTest.java | 51 +++++++++++++++++++ 3 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 src/test/java/io/reactivex/rxjava3/internal/schedulers/IoScheduledReleaseTest.java diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java index 6d63715770..13c62fcb10 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java @@ -48,6 +48,10 @@ public final class IoScheduler extends Scheduler { /** The name of the system property for setting the thread priority for this Scheduler. */ private static final String KEY_IO_PRIORITY = "rx3.io-priority"; + /** The name of the system property for setting the release behaviour for this Scheduler. */ + private static final String KEY_SCHEDULED_RELEASE = "rx3.io-scheduled-release"; + static boolean USE_SCHEDULED_RELEASE; + static final CachedWorkerPool NONE; static { @@ -63,6 +67,8 @@ public final class IoScheduler extends Scheduler { EVICTOR_THREAD_FACTORY = new RxThreadFactory(EVICTOR_THREAD_NAME_PREFIX, priority); + USE_SCHEDULED_RELEASE = Boolean.getBoolean(KEY_SCHEDULED_RELEASE); + NONE = new CachedWorkerPool(0, null, WORKER_THREAD_FACTORY); NONE.shutdown(); } @@ -194,7 +200,7 @@ public int size() { return pool.get().allWorkers.size(); } - static final class EventLoopWorker extends Scheduler.Worker { + static final class EventLoopWorker extends Scheduler.Worker implements Runnable { private final CompositeDisposable tasks; private final CachedWorkerPool pool; private final ThreadWorker threadWorker; @@ -212,11 +218,20 @@ public void dispose() { if (once.compareAndSet(false, true)) { tasks.dispose(); - // releasing the pool should be the last action - pool.release(threadWorker); + if (USE_SCHEDULED_RELEASE) { + threadWorker.scheduleActual(this, 0, TimeUnit.NANOSECONDS, null); + } else { + // releasing the pool should be the last action + pool.release(threadWorker); + } } } + @Override + public void run() { + pool.release(threadWorker); + } + @Override public boolean isDisposed() { return once.get(); diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java index ad7ff69bc7..6711be2645 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java @@ -32,6 +32,8 @@ *

    *
  • {@code rx3.io-keep-alive-time} (long): sets the keep-alive time of the {@link #io()} Scheduler workers, default is {@link IoScheduler#KEEP_ALIVE_TIME_DEFAULT}
  • *
  • {@code rx3.io-priority} (int): sets the thread priority of the {@link #io()} Scheduler, default is {@link Thread#NORM_PRIORITY}
  • + *
  • {@code rx3.io-scheduled-release} (boolean): {@code true} sets the worker release mode of the + * {@link #io()} Scheduler to scheduled, default is {@code false} for eager mode.
  • *
  • {@code rx3.computation-threads} (int): sets the number of threads in the {@link #computation()} Scheduler, default is the number of available CPUs
  • *
  • {@code rx3.computation-priority} (int): sets the thread priority of the {@link #computation()} Scheduler, default is {@link Thread#NORM_PRIORITY}
  • *
  • {@code rx3.newthread-priority} (int): sets the thread priority of the {@link #newThread()} Scheduler, default is {@link Thread#NORM_PRIORITY}
  • @@ -159,6 +161,8 @@ public static Scheduler computation() { *
      *
    • {@code rx3.io-keep-alive-time} (long): sets the keep-alive time of the {@code io()} Scheduler workers, default is {@link IoScheduler#KEEP_ALIVE_TIME_DEFAULT}
    • *
    • {@code rx3.io-priority} (int): sets the thread priority of the {@code io()} Scheduler, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.io-scheduled-release} (boolean): {@code true} sets the worker release mode of the + * {@code #io()} Scheduler to scheduled, default is {@code false} for eager mode.
    • *
    *

    * The default value of this scheduler can be overridden at initialization time via the @@ -175,6 +179,21 @@ public static Scheduler computation() { *

    Operators on the base reactive classes that use this scheduler are marked with the * @{@link io.reactivex.rxjava3.annotations.SchedulerSupport SchedulerSupport}({@link io.reactivex.rxjava3.annotations.SchedulerSupport#IO IO}) * annotation. + *

    + * When the {@link Scheduler.Worker} is disposed, the underlying worker can be released to the cached worker pool in two modes: + *

      + *
    • In eager mode (default), the underlying worker is returned immediately to the cached worker pool + * and can be reused much quicker by operators. The drawback is that if the currently running task doesn't + * respond to interruption in time or at all, this may lead to delays or deadlock with the reuse use of the + * underlying worker. + *
    • + *
    • In scheduled mode (enabled via the system parameter {@code rx3.io-scheduled-release} + * set to {@code true}), the underlying worker is returned to the cached worker pool only after the currently running task + * has finished. This can help prevent premature reuse of the underlying worker and likely won't lead to delays or + * deadlock with such reuses. The drawback is that the delay in release may lead to an excess amount of underlying + * workers being created. + *
    • + *
    * @return a {@link Scheduler} meant for IO-bound work */ @NonNull diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoScheduledReleaseTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoScheduledReleaseTest.java new file mode 100644 index 0000000000..b845c08ae2 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoScheduledReleaseTest.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.schedulers; + +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.schedulers.Schedulers; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +public class IoScheduledReleaseTest extends RxJavaTest { + + /* This test will be stuck in a deadlock if IoScheduler.USE_SCHEDULED_RELEASE is not set */ + @Test + public void scheduledRelease() { + boolean savedScheduledRelease = IoScheduler.USE_SCHEDULED_RELEASE; + IoScheduler.USE_SCHEDULED_RELEASE = true; + try { + Flowable.just("item") + .observeOn(Schedulers.io()) + .firstOrError() + .map(item -> { + for (int i = 0; i < 50; i++) { + Completable.complete() + .observeOn(Schedulers.io()) + .blockingAwait(); + } + return "Done"; + }) + .ignoreElement() + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertComplete(); + } finally { + IoScheduler.USE_SCHEDULED_RELEASE = savedScheduledRelease; + } + } +} From 80a4842a7603da2d1c96fc2bb16249605cde5bbd Mon Sep 17 00:00:00 2001 From: Archish Thakkar Date: Tue, 26 Jan 2021 22:53:17 +0530 Subject: [PATCH 111/521] Updating documentation for scheduleActual method (#7164) --- .../rxjava3/internal/schedulers/NewThreadWorker.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadWorker.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadWorker.java index d811c8e782..a342b246b8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadWorker.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadWorker.java @@ -116,10 +116,8 @@ public Disposable schedulePeriodicallyDirect(Runnable run, long initialDelay, lo } /** - * Wraps the given runnable into a ScheduledRunnable and schedules it + * Wraps and returns the given runnable into a ScheduledRunnable and schedules it * on the underlying ScheduledExecutorService. - *

    If the schedule has been rejected, the ScheduledRunnable.wasScheduled will return - * false. * @param run the runnable instance * @param delayTime the time to delay the execution * @param unit the time unit From 1a745a14ab4dc9c9dbdb7e11345377ef464fc432 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 27 Jan 2021 10:22:03 +0100 Subject: [PATCH 112/521] 3.x: TestScheduler option to use onSchedule hook (#7163) --- .../rxjava3/schedulers/Schedulers.java | 3 +- .../rxjava3/schedulers/TestScheduler.java | 76 +++++++++++++-- .../rxjava3/schedulers/TestSchedulerTest.java | 92 +++++++++++++++++++ 3 files changed, 160 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java index 6711be2645..72a3990af4 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java @@ -180,7 +180,8 @@ public static Scheduler computation() { * @{@link io.reactivex.rxjava3.annotations.SchedulerSupport SchedulerSupport}({@link io.reactivex.rxjava3.annotations.SchedulerSupport#IO IO}) * annotation. *

    - * When the {@link Scheduler.Worker} is disposed, the underlying worker can be released to the cached worker pool in two modes: + * When the {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} is disposed, + * the underlying worker can be released to the cached worker pool in two modes: *

      *
    • In eager mode (default), the underlying worker is returned immediately to the cached worker pool * and can be reused much quicker by operators. The drawback is that if the currently running task doesn't diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java b/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java index 3e05665283..f016beca18 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java @@ -15,20 +15,28 @@ import java.util.Queue; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; -import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.Scheduler; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** * A special, non thread-safe scheduler for testing operators that require * a scheduler without introducing real concurrency and allows manually advancing * a virtual time. + *

      + * By default, the tasks submitted via the various {@code schedule} methods are not + * wrapped by the {@link RxJavaPlugins#onSchedule(Runnable)} hook. To enable this behavior, + * create a {@code TestScheduler} via {@link #TestScheduler(boolean)} or {@link #TestScheduler(long, TimeUnit, boolean)}. */ public final class TestScheduler extends Scheduler { /** The ordered queue for the runnable tasks. */ final Queue queue = new PriorityBlockingQueue<>(11); + /** Use the {@link RxJavaPlugins#onSchedule(Runnable)} hook when scheduling tasks. */ + final boolean useOnScheduleHook; /** The per-scheduler global order counter. */ long counter; // Storing time in nanoseconds internally. @@ -38,7 +46,20 @@ public final class TestScheduler extends Scheduler { * Creates a new TestScheduler with initial virtual time of zero. */ public TestScheduler() { - // No-op. + this(false); + } + + /** + * Creates a new TestScheduler with the option to use the + * {@link RxJavaPlugins#onSchedule(Runnable)} hook when scheduling tasks. + * @param useOnScheduleHook if {@code true}, the tasks submitted to this + * TestScheduler is wrapped via the + * {@link RxJavaPlugins#onSchedule(Runnable)} hook + * @since 3.0.10 - experimental + */ + @Experimental + public TestScheduler(boolean useOnScheduleHook) { + this.useOnScheduleHook = useOnScheduleHook; } /** @@ -50,7 +71,27 @@ public TestScheduler() { * the units of time that {@code delayTime} is expressed in */ public TestScheduler(long delayTime, TimeUnit unit) { + this(delayTime, unit, false); + } + + /** + * Creates a new TestScheduler with the specified initial virtual time + * and with the option to use the + * {@link RxJavaPlugins#onSchedule(Runnable)} hook when scheduling tasks. + * + * @param delayTime + * the point in time to move the Scheduler's clock to + * @param unit + * the units of time that {@code delayTime} is expressed in + * @param useOnScheduleHook if {@code true}, the tasks submitted to this + * TestScheduler is wrapped via the + * {@link RxJavaPlugins#onSchedule(Runnable)} hook + * @since 3.0.10 - experimental + */ + @Experimental + public TestScheduler(long delayTime, TimeUnit unit, boolean useOnScheduleHook) { time = unit.toNanos(delayTime); + this.useOnScheduleHook = useOnScheduleHook; } static final class TimedRunnable implements Comparable { @@ -163,10 +204,13 @@ public Disposable schedule(@NonNull Runnable run, long delayTime, @NonNull TimeU if (disposed) { return EmptyDisposable.INSTANCE; } + if (useOnScheduleHook) { + run = RxJavaPlugins.onSchedule(run); + } final TimedRunnable timedAction = new TimedRunnable(this, time + unit.toNanos(delayTime), run, counter++); queue.add(timedAction); - return Disposable.fromRunnable(new QueueRemove(timedAction)); + return new QueueRemove(timedAction); } @NonNull @@ -175,9 +219,12 @@ public Disposable schedule(@NonNull Runnable run) { if (disposed) { return EmptyDisposable.INSTANCE; } + if (useOnScheduleHook) { + run = RxJavaPlugins.onSchedule(run); + } final TimedRunnable timedAction = new TimedRunnable(this, 0, run, counter++); queue.add(timedAction); - return Disposable.fromRunnable(new QueueRemove(timedAction)); + return new QueueRemove(timedAction); } @Override @@ -185,16 +232,25 @@ public long now(@NonNull TimeUnit unit) { return TestScheduler.this.now(unit); } - final class QueueRemove implements Runnable { - final TimedRunnable timedAction; + final class QueueRemove extends AtomicReference implements Disposable { + + private static final long serialVersionUID = -7874968252110604360L; QueueRemove(TimedRunnable timedAction) { - this.timedAction = timedAction; + this.lazySet(timedAction); + } + + @Override + public void dispose() { + TimedRunnable tr = getAndSet(null); + if (tr != null) { + queue.remove(tr); + } } @Override - public void run() { - queue.remove(timedAction); + public boolean isDisposed() { + return get() == null; } } } diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/TestSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/TestSchedulerTest.java index 8bcb177053..010b271359 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/TestSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/TestSchedulerTest.java @@ -30,6 +30,7 @@ import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.TestScheduler.*; public class TestSchedulerTest extends RxJavaTest { @@ -260,4 +261,95 @@ public void constructorTimeSetsTime() { assertEquals(5, ts.now(TimeUnit.SECONDS)); assertEquals(5000, ts.now(TimeUnit.MILLISECONDS)); } + + @Test + public void withOnScheduleHook() { + AtomicInteger run = new AtomicInteger(); + AtomicInteger counter = new AtomicInteger(); + RxJavaPlugins.setScheduleHandler(r -> { + counter.getAndIncrement(); + return r; + }); + try { + Runnable r = () -> run.getAndIncrement(); + TestScheduler ts = new TestScheduler(true); + + ts.createWorker().schedule(r); + ts.createWorker().schedule(r, 1, TimeUnit.SECONDS); + + ts.advanceTimeBy(1, TimeUnit.SECONDS); + + assertEquals(2, run.get()); + assertEquals(2, counter.get()); + + ts = new TestScheduler(); + + ts.createWorker().schedule(r); + ts.createWorker().schedule(r, 1, TimeUnit.SECONDS); + + ts.advanceTimeBy(1, TimeUnit.SECONDS); + + assertEquals(4, run.get()); + assertEquals(2, counter.get()); + } finally { + RxJavaPlugins.setScheduleHandler(null); + } + } + + @Test + public void withOnScheduleHookInitialTime() { + AtomicInteger run = new AtomicInteger(); + AtomicInteger counter = new AtomicInteger(); + RxJavaPlugins.setScheduleHandler(r -> { + counter.getAndIncrement(); + return r; + }); + try { + Runnable r = () -> run.getAndIncrement(); + TestScheduler ts = new TestScheduler(1, TimeUnit.HOURS, true); + + ts.createWorker().schedule(r); + ts.createWorker().schedule(r, 1, TimeUnit.SECONDS); + + ts.advanceTimeBy(1, TimeUnit.SECONDS); + + assertEquals(2, run.get()); + assertEquals(2, counter.get()); + + ts = new TestScheduler(1, TimeUnit.HOURS); + + ts.createWorker().schedule(r); + ts.createWorker().schedule(r, 1, TimeUnit.SECONDS); + + ts.advanceTimeBy(1, TimeUnit.SECONDS); + + assertEquals(4, run.get()); + assertEquals(2, counter.get()); + } finally { + RxJavaPlugins.setScheduleHandler(null); + } + } + + @Test + public void disposeWork() { + AtomicInteger run = new AtomicInteger(); + Runnable r = () -> run.getAndIncrement(); + TestScheduler ts = new TestScheduler(1, TimeUnit.HOURS, true); + + Disposable d = ts.createWorker().schedule(r); + + assertFalse(d.isDisposed()); + + d.dispose(); + + assertTrue(d.isDisposed()); + + d.dispose(); + + assertTrue(d.isDisposed()); + + ts.advanceTimeBy(1, TimeUnit.SECONDS); + + assertEquals(0, run.get()); + } } From 005f1553f3089c9c81ff00087e7708185084aa7a Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 27 Jan 2021 11:46:56 +0100 Subject: [PATCH 113/521] 3.x: Improve Javadocs style of Schedulers (#7168) --- .../rxjava3/schedulers/Schedulers.java | 140 +++++++++--------- .../validators/JavadocCodesAndLinks.java | 9 +- 2 files changed, 78 insertions(+), 71 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java index 72a3990af4..5456ea1924 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java @@ -22,7 +22,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** - * Static factory methods for returning standard Scheduler instances. + * Static factory methods for returning standard {@link Scheduler} instances. *

      * The initial and runtime values of the various scheduler types can be overridden via the * {@code RxJavaPlugins.setInit(scheduler name)SchedulerHandler()} and @@ -30,16 +30,16 @@ *

      * Supported system properties ({@code System.getProperty()}): *

        - *
      • {@code rx3.io-keep-alive-time} (long): sets the keep-alive time of the {@link #io()} Scheduler workers, default is {@link IoScheduler#KEEP_ALIVE_TIME_DEFAULT}
      • - *
      • {@code rx3.io-priority} (int): sets the thread priority of the {@link #io()} Scheduler, default is {@link Thread#NORM_PRIORITY}
      • + *
      • {@code rx3.io-keep-alive-time} (long): sets the keep-alive time of the {@link #io()} {@code Scheduler} workers, default is {@link IoScheduler#KEEP_ALIVE_TIME_DEFAULT}
      • + *
      • {@code rx3.io-priority} (int): sets the thread priority of the {@link #io()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
      • *
      • {@code rx3.io-scheduled-release} (boolean): {@code true} sets the worker release mode of the - * {@link #io()} Scheduler to scheduled, default is {@code false} for eager mode.
      • - *
      • {@code rx3.computation-threads} (int): sets the number of threads in the {@link #computation()} Scheduler, default is the number of available CPUs
      • - *
      • {@code rx3.computation-priority} (int): sets the thread priority of the {@link #computation()} Scheduler, default is {@link Thread#NORM_PRIORITY}
      • - *
      • {@code rx3.newthread-priority} (int): sets the thread priority of the {@link #newThread()} Scheduler, default is {@link Thread#NORM_PRIORITY}
      • - *
      • {@code rx3.single-priority} (int): sets the thread priority of the {@link #single()} Scheduler, default is {@link Thread#NORM_PRIORITY}
      • - *
      • {@code rx3.purge-enabled} (boolean): enables periodic purging of all Scheduler's backing thread pools, default is false
      • - *
      • {@code rx3.purge-period-seconds} (int): specifies the periodic purge interval of all Scheduler's backing thread pools, default is 1 second
      • + * {@link #io()} {@code Scheduler} to scheduled, default is {@code false} for eager mode. + *
      • {@code rx3.computation-threads} (int): sets the number of threads in the {@link #computation()} {@code Scheduler}, default is the number of available CPUs
      • + *
      • {@code rx3.computation-priority} (int): sets the thread priority of the {@link #computation()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
      • + *
      • {@code rx3.newthread-priority} (int): sets the thread priority of the {@link #newThread()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
      • + *
      • {@code rx3.single-priority} (int): sets the thread priority of the {@link #single()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
      • + *
      • {@code rx3.purge-enabled} (boolean): enables periodic purging of all {@code Scheduler}'s backing thread pools, default is {@code false}
      • + *
      • {@code rx3.purge-period-seconds} (int): specifies the periodic purge interval of all {@code Scheduler}'s backing thread pools, default is 1 second
      • *
      */ public final class Schedulers { @@ -107,32 +107,32 @@ private Schedulers() { * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". *

      - * If the {@link RxJavaPlugins#setFailOnNonBlockingScheduler(boolean)} is set to true, attempting to execute + * If the {@link RxJavaPlugins#setFailOnNonBlockingScheduler(boolean)} is set to {@code true}, attempting to execute * operators that block while running on this scheduler will throw an {@link IllegalStateException}. *

      * You can control certain properties of this standard scheduler via system properties that have to be set - * before the {@link Schedulers} class is referenced in your code. + * before the {@code Schedulers} class is referenced in your code. *

      Supported system properties ({@code System.getProperty()}): *

        - *
      • {@code rx3.computation-threads} (int): sets the number of threads in the {@code computation()} Scheduler, default is the number of available CPUs
      • - *
      • {@code rx3.computation-priority} (int): sets the thread priority of the {@code computation()} Scheduler, default is {@link Thread#NORM_PRIORITY}
      • + *
      • {@code rx3.computation-threads} (int): sets the number of threads in the {@code computation()} {@code Scheduler}, default is the number of available CPUs
      • + *
      • {@code rx3.computation-priority} (int): sets the thread priority of the {@code computation()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
      • *
      *

      * The default value of this scheduler can be overridden at initialization time via the * {@link RxJavaPlugins#setInitComputationSchedulerHandler(io.reactivex.rxjava3.functions.Function)} plugin method. * Note that due to possible initialization cycles, using any of the other scheduler-returning methods will - * result in a {@code NullPointerException}. - * Once the {@link Schedulers} class has been initialized, you can override the returned {@link Scheduler} instance + * result in a {@link NullPointerException}. + * Once the {@code Schedulers} class has been initialized, you can override the returned {@code Scheduler} instance * via the {@link RxJavaPlugins#setComputationSchedulerHandler(io.reactivex.rxjava3.functions.Function)} method. *

      - * It is possible to create a fresh instance of this scheduler with a custom ThreadFactory, via the + * It is possible to create a fresh instance of this scheduler with a custom {@link ThreadFactory}, via the * {@link RxJavaPlugins#createComputationScheduler(ThreadFactory)} method. Note that such custom * instances require a manual call to {@link Scheduler#shutdown()} to allow the JVM to exit or the * (J2EE) container to unload properly. *

      Operators on the base reactive classes that use this scheduler are marked with the * @{@link io.reactivex.rxjava3.annotations.SchedulerSupport SchedulerSupport}({@link io.reactivex.rxjava3.annotations.SchedulerSupport#COMPUTATION COMPUTATION}) * annotation. - * @return a {@link Scheduler} meant for computation-bound work + * @return a {@code Scheduler} meant for computation-bound work */ @NonNull public static Scheduler computation() { @@ -148,7 +148,7 @@ public static Scheduler computation() { * that will try to reuse previously started instances used by the worker * returned by {@link io.reactivex.rxjava3.core.Scheduler#createWorker()} but otherwise will start a new backing * {@link ScheduledExecutorService} instance. Note that this scheduler may create an unbounded number - * of worker threads that can result in system slowdowns or {@code OutOfMemoryError}. Therefore, for casual uses + * of worker threads that can result in system slowdowns or {@link OutOfMemoryError}. Therefore, for casual uses * or when implementing an operator, the Worker instances must be disposed via {@link io.reactivex.rxjava3.core.Scheduler.Worker#dispose()}. *

      * It is not recommended to perform computational work on this scheduler. Use {@link #computation()} instead. @@ -156,23 +156,23 @@ public static Scheduler computation() { * Unhandled errors will be delivered to the scheduler Thread's {@link java.lang.Thread.UncaughtExceptionHandler}. *

      * You can control certain properties of this standard scheduler via system properties that have to be set - * before the {@link Schedulers} class is referenced in your code. + * before the {@code Schedulers} class is referenced in your code. *

      Supported system properties ({@code System.getProperty()}): *

        - *
      • {@code rx3.io-keep-alive-time} (long): sets the keep-alive time of the {@code io()} Scheduler workers, default is {@link IoScheduler#KEEP_ALIVE_TIME_DEFAULT}
      • - *
      • {@code rx3.io-priority} (int): sets the thread priority of the {@code io()} Scheduler, default is {@link Thread#NORM_PRIORITY}
      • + *
      • {@code rx3.io-keep-alive-time} (long): sets the keep-alive time of the {@code io()} {@code Scheduler} workers, default is {@link IoScheduler#KEEP_ALIVE_TIME_DEFAULT}
      • + *
      • {@code rx3.io-priority} (int): sets the thread priority of the {@code io()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
      • *
      • {@code rx3.io-scheduled-release} (boolean): {@code true} sets the worker release mode of the - * {@code #io()} Scheduler to scheduled, default is {@code false} for eager mode.
      • + * {@code #io()} {@code Scheduler} to scheduled, default is {@code false} for eager mode. *
      *

      * The default value of this scheduler can be overridden at initialization time via the * {@link RxJavaPlugins#setInitIoSchedulerHandler(io.reactivex.rxjava3.functions.Function)} plugin method. * Note that due to possible initialization cycles, using any of the other scheduler-returning methods will - * result in a {@code NullPointerException}. - * Once the {@link Schedulers} class has been initialized, you can override the returned {@link Scheduler} instance + * result in a {@link NullPointerException}. + * Once the {@code Schedulers} class has been initialized, you can override the returned {@code Scheduler} instance * via the {@link RxJavaPlugins#setIoSchedulerHandler(io.reactivex.rxjava3.functions.Function)} method. *

      - * It is possible to create a fresh instance of this scheduler with a custom ThreadFactory, via the + * It is possible to create a fresh instance of this scheduler with a custom {@link ThreadFactory}, via the * {@link RxJavaPlugins#createIoScheduler(ThreadFactory)} method. Note that such custom * instances require a manual call to {@link Scheduler#shutdown()} to allow the JVM to exit or the * (J2EE) container to unload properly. @@ -195,7 +195,7 @@ public static Scheduler computation() { * workers being created. *

    • *
    - * @return a {@link Scheduler} meant for IO-bound work + * @return a {@code Scheduler} meant for IO-bound work */ @NonNull public static Scheduler io() { @@ -214,7 +214,7 @@ public static Scheduler io() { * by RxJava itself but may be found in external libraries. *

    * This scheduler can't be overridden via an {@link RxJavaPlugins} method. - * @return a {@link Scheduler} that queues work on the current thread + * @return a {@code Scheduler} that queues work on the current thread */ @NonNull public static Scheduler trampoline() { @@ -227,33 +227,33 @@ public static Scheduler trampoline() { * The default implementation of this scheduler creates a new, single-threaded {@link ScheduledExecutorService} for * each invocation of the {@link Scheduler#scheduleDirect(Runnable)} (plus its overloads) and {@link Scheduler#createWorker()} * methods, thus an unbounded number of worker threads may be created that can - * result in system slowdowns or {@code OutOfMemoryError}. Therefore, for casual uses or when implementing an operator, + * result in system slowdowns or {@link OutOfMemoryError}. Therefore, for casual uses or when implementing an operator, * the Worker instances must be disposed via {@link io.reactivex.rxjava3.core.Scheduler.Worker#dispose()}. *

    * Unhandled errors will be delivered to the scheduler Thread's {@link java.lang.Thread.UncaughtExceptionHandler}. *

    * You can control certain properties of this standard scheduler via system properties that have to be set - * before the {@link Schedulers} class is referenced in your code. + * before the {@code Schedulers} class is referenced in your code. *

    Supported system properties ({@code System.getProperty()}): *

      - *
    • {@code rx3.newthread-priority} (int): sets the thread priority of the {@code newThread()} Scheduler, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.newthread-priority} (int): sets the thread priority of the {@code newThread()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
    • *
    *

    * The default value of this scheduler can be overridden at initialization time via the * {@link RxJavaPlugins#setInitNewThreadSchedulerHandler(io.reactivex.rxjava3.functions.Function)} plugin method. * Note that due to possible initialization cycles, using any of the other scheduler-returning methods will - * result in a {@code NullPointerException}. - * Once the {@link Schedulers} class has been initialized, you can override the returned {@link Scheduler} instance + * result in a {@link NullPointerException}. + * Once the {@code Schedulers} class has been initialized, you can override the returned {@code Scheduler} instance * via the {@link RxJavaPlugins#setNewThreadSchedulerHandler(io.reactivex.rxjava3.functions.Function)} method. *

    - * It is possible to create a fresh instance of this scheduler with a custom ThreadFactory, via the + * It is possible to create a fresh instance of this scheduler with a custom {@link ThreadFactory}, via the * {@link RxJavaPlugins#createNewThreadScheduler(ThreadFactory)} method. Note that such custom * instances require a manual call to {@link Scheduler#shutdown()} to allow the JVM to exit or the * (J2EE) container to unload properly. *

    Operators on the base reactive classes that use this scheduler are marked with the * @{@link io.reactivex.rxjava3.annotations.SchedulerSupport SchedulerSupport}({@link io.reactivex.rxjava3.annotations.SchedulerSupport#NEW_THREAD NEW_TRHEAD}) * annotation. - * @return a {@link Scheduler} that creates new threads + * @return a {@code Scheduler} that creates new threads */ @NonNull public static Scheduler newThread() { @@ -267,7 +267,7 @@ public static Scheduler newThread() { * Uses: *

      *
    • event loop
    • - *
    • support Schedulers.from(Executor) and from(ExecutorService) with delayed scheduling
    • + *
    • support {@code Schedulers.from(}{@link Executor}{@code )} and {@code from(}{@link ExecutorService}{@code )} with delayed scheduling
    • *
    • support benchmarks that pipeline data from some thread to another thread and * avoid core-bashing of computation's round-robin nature
    • *
    @@ -278,31 +278,31 @@ public static Scheduler newThread() { * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". *

    - * If the {@link RxJavaPlugins#setFailOnNonBlockingScheduler(boolean)} is set to true, attempting to execute + * If the {@link RxJavaPlugins#setFailOnNonBlockingScheduler(boolean)} is set to {@code true}, attempting to execute * operators that block while running on this scheduler will throw an {@link IllegalStateException}. *

    * You can control certain properties of this standard scheduler via system properties that have to be set - * before the {@link Schedulers} class is referenced in your code. + * before the {@code Schedulers} class is referenced in your code. *

    Supported system properties ({@code System.getProperty()}): *

      - *
    • {@code rx3.single-priority} (int): sets the thread priority of the {@code single()} Scheduler, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.single-priority} (int): sets the thread priority of the {@code single()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
    • *
    *

    * The default value of this scheduler can be overridden at initialization time via the * {@link RxJavaPlugins#setInitSingleSchedulerHandler(io.reactivex.rxjava3.functions.Function)} plugin method. * Note that due to possible initialization cycles, using any of the other scheduler-returning methods will - * result in a {@code NullPointerException}. - * Once the {@link Schedulers} class has been initialized, you can override the returned {@link Scheduler} instance + * result in a {@link NullPointerException}. + * Once the {@code Schedulers} class has been initialized, you can override the returned {@code Scheduler} instance * via the {@link RxJavaPlugins#setSingleSchedulerHandler(io.reactivex.rxjava3.functions.Function)} method. *

    - * It is possible to create a fresh instance of this scheduler with a custom ThreadFactory, via the + * It is possible to create a fresh instance of this scheduler with a custom {@link ThreadFactory}, via the * {@link RxJavaPlugins#createSingleScheduler(ThreadFactory)} method. Note that such custom * instances require a manual call to {@link Scheduler#shutdown()} to allow the JVM to exit or the * (J2EE) container to unload properly. *

    Operators on the base reactive classes that use this scheduler are marked with the * @{@link io.reactivex.rxjava3.annotations.SchedulerSupport SchedulerSupport}({@link io.reactivex.rxjava3.annotations.SchedulerSupport#SINGLE SINGLE}) * annotation. - * @return a {@link Scheduler} that shares a single backing thread. + * @return a {@code Scheduler} that shares a single backing thread. * @since 2.0 */ @NonNull @@ -311,7 +311,7 @@ public static Scheduler single() { } /** - * Wraps an {@link Executor} into a new Scheduler instance and delegates {@code schedule()} + * Wraps an {@link Executor} into a new {@link Scheduler} instance and delegates {@code schedule()} * calls to it. *

    * If the provided executor doesn't support any of the more specific standard Java executor @@ -337,11 +337,11 @@ public static Scheduler single() { * with a time delay close to each other may end up executing in different order than * the original schedule() call was issued. This limitation may be lifted in a future patch. *

    - * The implementation of the Worker of this wrapper Scheduler is eager and will execute as many + * The implementation of the Worker of this wrapper {@code Scheduler} is eager and will execute as many * non-delayed tasks as it can, which may result in a longer than expected occupation of a - * thread of the given backing Executor. In other terms, it does not allow per-Runnable fairness - * in case the worker runs on a shared underlying thread of the Executor. - * See {@link #from(Executor, boolean, boolean)} to create a wrapper that uses the underlying Executor + * thread of the given backing {@code Executor}. In other terms, it does not allow per-{@link Runnable} fairness + * in case the worker runs on a shared underlying thread of the {@code Executor}. + * See {@link #from(Executor, boolean, boolean)} to create a wrapper that uses the underlying {@code Executor} * more fairly. *

    * Starting, stopping and restarting this scheduler is not supported (no-op) and the provided @@ -374,10 +374,10 @@ public static Scheduler single() { * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". *

    - * Note that this method returns a new {@link Scheduler} instance, even for the same {@link Executor} instance. + * Note that this method returns a new {@code Scheduler} instance, even for the same {@code Executor} instance. * @param executor * the executor to wrap - * @return the new Scheduler wrapping the Executor + * @return the new {@code Scheduler} wrapping the {@code Executor} * @see #from(Executor, boolean, boolean) */ @NonNull @@ -386,10 +386,10 @@ public static Scheduler from(@NonNull Executor executor) { } /** - * Wraps an {@link Executor} into a new Scheduler instance and delegates {@code schedule()} + * Wraps an {@link Executor} into a new {@link Scheduler} instance and delegates {@code schedule()} * calls to it. *

    - * The tasks scheduled by the returned {@link Scheduler} and its {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} + * The tasks scheduled by the returned {@code Scheduler} and its {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} * can be optionally interrupted. *

    * If the provided executor doesn't support any of the more specific standard Java executor @@ -411,11 +411,11 @@ public static Scheduler from(@NonNull Executor executor) { * with a time delay close to each other may end up executing in different order than * the original schedule() call was issued. This limitation may be lifted in a future patch. *

    - * The implementation of the Worker of this wrapper Scheduler is eager and will execute as many + * The implementation of the {@code Worker} of this wrapper {@code Scheduler} is eager and will execute as many * non-delayed tasks as it can, which may result in a longer than expected occupation of a - * thread of the given backing Executor. In other terms, it does not allow per-Runnable fairness - * in case the worker runs on a shared underlying thread of the Executor. - * See {@link #from(Executor, boolean, boolean)} to create a wrapper that uses the underlying Executor + * thread of the given backing {@code Executor}. In other terms, it does not allow per-{@link Runnable} fairness + * in case the worker runs on a shared underlying thread of the {@code Executor}. + * See {@link #from(Executor, boolean, boolean)} to create a wrapper that uses the underlying {@code Executor} * more fairly. *

    * Starting, stopping and restarting this scheduler is not supported (no-op) and the provided @@ -448,13 +448,13 @@ public static Scheduler from(@NonNull Executor executor) { * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". *

    - * Note that this method returns a new {@link Scheduler} instance, even for the same {@link Executor} instance. + * Note that this method returns a new {@code Scheduler} instance, even for the same {@code Executor} instance. *

    History: 2.2.6 - experimental * @param executor * the executor to wrap * @param interruptibleWorker if {@code true} the tasks submitted to the {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} will * be interrupted when the task is disposed. - * @return the new Scheduler wrapping the Executor + * @return the new {@code Scheduler} wrapping the {@code Executor} * @since 3.0.0 * @see #from(Executor, boolean, boolean) */ @@ -464,10 +464,10 @@ public static Scheduler from(@NonNull Executor executor, boolean interruptibleWo } /** - * Wraps an {@link Executor} into a new Scheduler instance and delegates {@code schedule()} + * Wraps an {@link Executor} into a new {@link Scheduler} instance and delegates {@code schedule()} * calls to it. *

    - * The tasks scheduled by the returned {@link Scheduler} and its {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} + * The tasks scheduled by the returned {@code Scheduler} and its {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} * can be optionally interrupted. *

    * If the provided executor doesn't support any of the more specific standard Java executor @@ -489,14 +489,14 @@ public static Scheduler from(@NonNull Executor executor, boolean interruptibleWo * with a time delay close to each other may end up executing in different order than * the original schedule() call was issued. This limitation may be lifted in a future patch. *

    - * The implementation of the Worker of this wrapper Scheduler can operate in both eager (non-fair) and + * The implementation of the Worker of this wrapper {@code Scheduler} can operate in both eager (non-fair) and * fair modes depending on the specified parameter. In eager mode, it will execute as many * non-delayed tasks as it can, which may result in a longer than expected occupation of a - * thread of the given backing Executor. In other terms, it does not allow per-Runnable fairness - * in case the worker runs on a shared underlying thread of the Executor. In fair mode, + * thread of the given backing {@code Executor}. In other terms, it does not allow per-{@link Runnable} fairness + * in case the worker runs on a shared underlying thread of the {@code Executor}. In fair mode, * non-delayed tasks will still be executed in a FIFO and non-overlapping manner, but after each task, - * the execution for the next task is rescheduled with the same underlying Executor, allowing interleaving - * from both the same Scheduler or other external usages of the underlying Executor. + * the execution for the next task is rescheduled with the same underlying {@code Executor}, allowing interleaving + * from both the same {@code Scheduler} or other external usages of the underlying {@code Executor}. *

    * Starting, stopping and restarting this scheduler is not supported (no-op) and the provided * executor's lifecycle must be managed externally: @@ -528,15 +528,15 @@ public static Scheduler from(@NonNull Executor executor, boolean interruptibleWo * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". *

    - * Note that this method returns a new {@link Scheduler} instance, even for the same {@link Executor} instance. + * Note that this method returns a new {@code Scheduler} instance, even for the same {@code Executor} instance. * @param executor * the executor to wrap * @param interruptibleWorker if {@code true} the tasks submitted to the {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} will * be interrupted when the task is disposed. - * @param fair if {@code true} tasks submitted to the will be executed by the underlying {@link Executor} one after the other, still + * @param fair if {@code true} tasks submitted to the will be executed by the underlying {@code Executor} one after the other, still * in a FIFO and non-overlapping manner, but allows interleaving with other tasks submitted to the underlying {@code Executor}. * If {@code false}, the underlying FIFO scheme will execute as many tasks as it can before giving up the underlying {@code Executor} thread. - * @return the new Scheduler wrapping the Executor + * @return the new {@code Scheduler} wrapping the {@code Executor} * @since 3.0.0 */ @NonNull @@ -545,7 +545,7 @@ public static Scheduler from(@NonNull Executor executor, boolean interruptibleWo } /** - * Shuts down the standard Schedulers. + * Shuts down the standard {@link Scheduler}s. *

    The operation is idempotent and thread-safe. */ public static void shutdown() { @@ -558,7 +558,7 @@ public static void shutdown() { } /** - * Starts the standard Schedulers. + * Starts the standard {@link Scheduler}s. *

    The operation is idempotent and thread-safe. */ public static void start() { diff --git a/src/test/java/io/reactivex/rxjava3/validators/JavadocCodesAndLinks.java b/src/test/java/io/reactivex/rxjava3/validators/JavadocCodesAndLinks.java index 5b90072306..8f80a9b2be 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/JavadocCodesAndLinks.java +++ b/src/test/java/io/reactivex/rxjava3/validators/JavadocCodesAndLinks.java @@ -75,6 +75,11 @@ public void checkConnectableObservable() throws Exception { checkSource("ConnectableObservable", "io.reactivex.rxjava3.observables"); } + @Test + public void checkSchedulers() throws Exception { + checkSource("Schedulers", "io.reactivex.rxjava3.schedulers"); + } + static void checkSource(String baseClassName, String packageName) throws Exception { File f = TestHelper.findSource(baseClassName, packageName); if (f == null) { @@ -418,7 +423,9 @@ static void blankRange(StringBuilder builder, int start, int end) { "List", "ArrayList", "HashMap", "HashSet", "CharSequence", - "TestSubscriber", "TestObserver", "Class" + "TestSubscriber", "TestObserver", "Class", + + "ThreadFactory", "Runnable", "Executor", "ExecutorService", "Executors", "RejectedExecutionException" ); static final Set ALWAYS_CODE = new HashSet<>(Arrays.asList( From a99b2e0a01d85f3f792a56c68ac1df9afa3a75c7 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 27 Jan 2021 16:46:33 +0100 Subject: [PATCH 114/521] 3.x: Add fusion support to concatMap{Maybe|Single|Completable} (#7165) --- .../mixed/ConcatMapXMainObserver.java | 150 +++++++++++++++++ .../mixed/ConcatMapXMainSubscriber.java | 155 ++++++++++++++++++ .../mixed/FlowableConcatMapCompletable.java | 133 ++++++--------- .../mixed/FlowableConcatMapMaybe.java | 111 +++++-------- .../mixed/FlowableConcatMapSingle.java | 103 ++++-------- .../mixed/ObservableConcatMapCompletable.java | 129 +++------------ .../mixed/ObservableConcatMapMaybe.java | 93 ++++------- .../mixed/ObservableConcatMapSingle.java | 103 ++++-------- .../DeferredScalarSubscription.java | 2 +- .../FlowableConcatMapCompletableTest.java | 60 ++++++- .../mixed/FlowableConcatMapMaybeTest.java | 74 +++++++-- .../mixed/FlowableConcatMapSingleTest.java | 72 +++++++- .../ObservableConcatMapCompletableTest.java | 50 ++++++ .../mixed/ObservableConcatMapMaybeTest.java | 50 ++++++ .../mixed/ObservableConcatMapSingleTest.java | 50 ++++++ .../rxjava3/testsupport/TestHelper.java | 12 ++ 16 files changed, 856 insertions(+), 491 deletions(-) create mode 100644 src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainObserver.java create mode 100644 src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainSubscriber.java diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainObserver.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainObserver.java new file mode 100644 index 0000000000..f4e95e8a9d --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainObserver.java @@ -0,0 +1,150 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.operators.mixed; + +import java.util.concurrent.atomic.AtomicInteger; + +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; +import io.reactivex.rxjava3.internal.util.*; + +/** + * Base class for implementing concatMapX main observers. + * + * @param the upstream value type + * @since 3.0.10 + */ +public abstract class ConcatMapXMainObserver extends AtomicInteger +implements Observer, Disposable { + + private static final long serialVersionUID = -3214213361171757852L; + + final AtomicThrowable errors; + + final int prefetch; + + final ErrorMode errorMode; + + SimpleQueue queue; + + Disposable upstream; + + volatile boolean done; + + volatile boolean disposed; + + public ConcatMapXMainObserver(int prefetch, ErrorMode errorMode) { + this.errorMode = errorMode; + this.errors = new AtomicThrowable(); + this.prefetch = prefetch; + } + + @Override + public final void onSubscribe(Disposable d) { + if (DisposableHelper.validate(upstream, d)) { + upstream = d; + if (d instanceof QueueDisposable) { + @SuppressWarnings("unchecked") + QueueDisposable qd = (QueueDisposable)d; + int mode = qd.requestFusion(QueueFuseable.ANY | QueueFuseable.BOUNDARY); + if (mode == QueueFuseable.SYNC) { + queue = qd; + done = true; + + onSubscribeDownstream(); + + drain(); + return; + } + else if (mode == QueueFuseable.ASYNC) { + queue = qd; + + onSubscribeDownstream(); + + return; + } + } + + queue = new SpscLinkedArrayQueue<>(prefetch); + onSubscribeDownstream(); + } + } + + @Override + public final void onNext(T t) { + // In async fusion mode, t is a drain indicator + if (t != null) { + queue.offer(t); + } + drain(); + } + + @Override + public final void onError(Throwable t) { + if (errors.tryAddThrowableOrReport(t)) { + if (errorMode == ErrorMode.IMMEDIATE) { + disposeInner(); + } + done = true; + drain(); + } + } + + @Override + public final void onComplete() { + done = true; + drain(); + } + + @Override + public final void dispose() { + disposed = true; + upstream.dispose(); + disposeInner(); + errors.tryTerminateAndReport(); + if (getAndIncrement() == 0) { + queue.clear(); + clearValue(); + } + } + + @Override + public final boolean isDisposed() { + return disposed; + } + + /** + * Override this to clear values when the downstream disposes. + */ + void clearValue() { + } + + /** + * Typically, this should be {@code downstream.onSubscribe(this)}. + */ + abstract void onSubscribeDownstream(); + + /** + * Typically, this should be {@code inner.dispose()}. + */ + abstract void disposeInner(); + + /** + * Implement the serialized inner subscribing and value emission here. + */ + abstract void drain(); +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainSubscriber.java new file mode 100644 index 0000000000..398e4fb015 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainSubscriber.java @@ -0,0 +1,155 @@ +/** + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +package io.reactivex.rxjava3.internal.operators.mixed; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.reactivestreams.Subscription; + +import io.reactivex.rxjava3.core.FlowableSubscriber; +import io.reactivex.rxjava3.exceptions.MissingBackpressureException; +import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; +import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.internal.util.*; + +/** + * Base class for implementing concatMapX main subscribers. + * + * @param the upstream value type + * @since 3.0.10 + */ +public abstract class ConcatMapXMainSubscriber extends AtomicInteger +implements FlowableSubscriber { + + private static final long serialVersionUID = -3214213361171757852L; + + final AtomicThrowable errors; + + final int prefetch; + + final ErrorMode errorMode; + + SimpleQueue queue; + + Subscription upstream; + + volatile boolean done; + + volatile boolean cancelled; + + boolean syncFused; + + public ConcatMapXMainSubscriber(int prefetch, ErrorMode errorMode) { + this.errorMode = errorMode; + this.errors = new AtomicThrowable(); + this.prefetch = prefetch; + } + + @Override + public final void onSubscribe(Subscription s) { + if (SubscriptionHelper.validate(upstream, s)) { + upstream = s; + if (s instanceof QueueSubscription) { + @SuppressWarnings("unchecked") + QueueSubscription qs = (QueueSubscription)s; + int mode = qs.requestFusion(QueueFuseable.ANY | QueueFuseable.BOUNDARY); + if (mode == QueueFuseable.SYNC) { + queue = qs; + syncFused = true; + done = true; + + onSubscribeDownstream(); + + drain(); + return; + } + else if (mode == QueueFuseable.ASYNC) { + queue = qs; + + onSubscribeDownstream(); + + upstream.request(prefetch); + return; + } + } + + queue = new SpscArrayQueue<>(prefetch); + onSubscribeDownstream(); + upstream.request(prefetch); + } + } + + @Override + public final void onNext(T t) { + // In async fusion mode, t is a drain indicator + if (t != null) { + if (!queue.offer(t)) { + upstream.cancel(); + onError(new MissingBackpressureException("queue full?!")); + return; + } + } + drain(); + } + + @Override + public final void onError(Throwable t) { + if (errors.tryAddThrowableOrReport(t)) { + if (errorMode == ErrorMode.IMMEDIATE) { + disposeInner(); + } + done = true; + drain(); + } + } + + @Override + public final void onComplete() { + done = true; + drain(); + } + + final void stop() { + cancelled = true; + upstream.cancel(); + disposeInner(); + errors.tryTerminateAndReport(); + if (getAndIncrement() == 0) { + queue.clear(); + clearValue(); + } + } + + /** + * Override this to clear values when the downstream disposes. + */ + void clearValue() { + } + + /** + * Typically, this should be {@code downstream.onSubscribe(this);}. + */ + abstract void onSubscribeDownstream(); + + /** + * Typically, this should be {@code inner.dispose()}. + */ + abstract void disposeInner(); + + /** + * Implement the serialized inner subscribing and value emission here. + */ + abstract void drain(); +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletable.java index e4a3f36c05..2838138a09 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletable.java @@ -14,18 +14,14 @@ package io.reactivex.rxjava3.internal.operators.mixed; import java.util.Objects; -import java.util.concurrent.atomic.*; - -import org.reactivestreams.Subscription; +import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; -import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.internal.fuseable.SimpleQueue; import io.reactivex.rxjava3.internal.util.*; /** @@ -61,8 +57,8 @@ protected void subscribeActual(CompletableObserver observer) { } static final class ConcatMapCompletableObserver - extends AtomicInteger - implements FlowableSubscriber, Disposable { + extends ConcatMapXMainSubscriber + implements Disposable { private static final long serialVersionUID = 3610901111000061034L; @@ -70,93 +66,39 @@ static final class ConcatMapCompletableObserver final Function mapper; - final ErrorMode errorMode; - - final AtomicThrowable errors; - final ConcatMapInnerObserver inner; - final int prefetch; - - final SimplePlainQueue queue; - - Subscription upstream; - volatile boolean active; - volatile boolean done; - - volatile boolean disposed; - int consumed; ConcatMapCompletableObserver(CompletableObserver downstream, Function mapper, ErrorMode errorMode, int prefetch) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.errorMode = errorMode; - this.prefetch = prefetch; - this.errors = new AtomicThrowable(); this.inner = new ConcatMapInnerObserver(this); - this.queue = new SpscArrayQueue<>(prefetch); - } - - @Override - public void onSubscribe(Subscription s) { - if (SubscriptionHelper.validate(upstream, s)) { - this.upstream = s; - downstream.onSubscribe(this); - s.request(prefetch); - } } @Override - public void onNext(T t) { - if (queue.offer(t)) { - drain(); - } else { - upstream.cancel(); - onError(new MissingBackpressureException("Queue full?!")); - } + void onSubscribeDownstream() { + downstream.onSubscribe(this); } @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - inner.dispose(); - errors.tryTerminateConsumer(downstream); - if (getAndIncrement() == 0) { - queue.clear(); - } - } else { - done = true; - drain(); - } - } - } - - @Override - public void onComplete() { - done = true; - drain(); + void disposeInner() { + inner.dispose(); } @Override public void dispose() { - disposed = true; - upstream.cancel(); - inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - } + stop(); } @Override public boolean isDisposed() { - return disposed; + return cancelled; } void innerError(Throwable ex) { @@ -179,29 +121,45 @@ void innerComplete() { drain(); } + @Override void drain() { if (getAndIncrement() != 0) { return; } + ErrorMode errorMode = this.errorMode; + SimpleQueue queue = this.queue; + AtomicThrowable errors = this.errors; + boolean syncFused = this.syncFused; + do { - if (disposed) { + if (cancelled) { queue.clear(); return; } - if (!active) { - - if (errorMode == ErrorMode.BOUNDARY) { - if (errors.get() != null) { - queue.clear(); - errors.tryTerminateConsumer(downstream); - return; - } + if (errors.get() != null) { + if (errorMode == ErrorMode.IMMEDIATE + || (errorMode == ErrorMode.BOUNDARY && !active)) { + queue.clear(); + errors.tryTerminateConsumer(downstream); + return; } + } + + if (!active) { boolean d = done; - T v = queue.poll(); + T v; + try { + v = queue.poll(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + errors.tryAddThrowableOrReport(ex); + errors.tryTerminateConsumer(downstream); + return; + } boolean empty = v == null; if (d && empty) { @@ -212,12 +170,15 @@ void drain() { if (!empty) { int limit = prefetch - (prefetch >> 1); - int c = consumed + 1; - if (c == limit) { - consumed = 0; - upstream.request(limit); - } else { - consumed = c; + + if (!syncFused) { + int c = consumed + 1; + if (c == limit) { + consumed = 0; + upstream.request(limit); + } else { + consumed = c; + } } CompletableSource cs; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybe.java index 5f71148e0d..c0d151e6fd 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybe.java @@ -20,12 +20,10 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; -import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.internal.fuseable.SimpleQueue; import io.reactivex.rxjava3.internal.util.*; /** @@ -62,8 +60,7 @@ protected void subscribeActual(Subscriber s) { } static final class ConcatMapMaybeSubscriber - extends AtomicInteger - implements FlowableSubscriber, Subscription { + extends ConcatMapXMainSubscriber implements Subscription { private static final long serialVersionUID = -9140123220065488293L; @@ -71,24 +68,10 @@ static final class ConcatMapMaybeSubscriber final Function> mapper; - final int prefetch; - final AtomicLong requested; - final AtomicThrowable errors; - final ConcatMapMaybeObserver inner; - final SimplePlainQueue queue; - - final ErrorMode errorMode; - - Subscription upstream; - - volatile boolean done; - - volatile boolean cancelled; - long emitted; int consumed; @@ -107,50 +90,16 @@ static final class ConcatMapMaybeSubscriber ConcatMapMaybeSubscriber(Subscriber downstream, Function> mapper, int prefetch, ErrorMode errorMode) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.prefetch = prefetch; - this.errorMode = errorMode; this.requested = new AtomicLong(); - this.errors = new AtomicThrowable(); this.inner = new ConcatMapMaybeObserver<>(this); - this.queue = new SpscArrayQueue<>(prefetch); - } - - @Override - public void onSubscribe(Subscription s) { - if (SubscriptionHelper.validate(upstream, s)) { - upstream = s; - downstream.onSubscribe(this); - s.request(prefetch); - } } @Override - public void onNext(T t) { - if (!queue.offer(t)) { - upstream.cancel(); - onError(new MissingBackpressureException("queue full?!")); - return; - } - drain(); - } - - @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - inner.dispose(); - } - done = true; - drain(); - } - } - - @Override - public void onComplete() { - done = true; - drain(); + void onSubscribeDownstream() { + downstream.onSubscribe(this); } @Override @@ -161,14 +110,7 @@ public void request(long n) { @Override public void cancel() { - cancelled = true; - upstream.cancel(); - inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - item = null; - } + stop(); } void innerSuccess(R item) { @@ -192,6 +134,17 @@ void innerError(Throwable ex) { } } + @Override + void clearValue() { + item = null; + } + + @Override + void disposeInner() { + inner.dispose(); + } + + @Override void drain() { if (getAndIncrement() != 0) { return; @@ -200,10 +153,11 @@ void drain() { int missed = 1; Subscriber downstream = this.downstream; ErrorMode errorMode = this.errorMode; - SimplePlainQueue queue = this.queue; + SimpleQueue queue = this.queue; AtomicThrowable errors = this.errors; AtomicLong requested = this.requested; int limit = prefetch - (prefetch >> 1); + boolean syncFused = this.syncFused; for (;;) { @@ -228,7 +182,16 @@ void drain() { if (s == STATE_INACTIVE) { boolean d = done; - T v = queue.poll(); + T v; + try { + v = queue.poll(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + errors.tryAddThrowableOrReport(ex); + errors.tryTerminateConsumer(downstream); + return; + } boolean empty = v == null; if (d && empty) { @@ -240,12 +203,14 @@ void drain() { break; } - int c = consumed + 1; - if (c == limit) { - consumed = 0; - upstream.request(limit); - } else { - consumed = c; + if (!syncFused) { + int c = consumed + 1; + if (c == limit) { + consumed = 0; + upstream.request(limit); + } else { + consumed = c; + } } MaybeSource ms; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingle.java index f6cd939546..49009a722e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingle.java @@ -20,12 +20,10 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; -import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.internal.fuseable.SimpleQueue; import io.reactivex.rxjava3.internal.util.*; /** @@ -62,8 +60,7 @@ protected void subscribeActual(Subscriber s) { } static final class ConcatMapSingleSubscriber - extends AtomicInteger - implements FlowableSubscriber, Subscription { + extends ConcatMapXMainSubscriber implements Subscription { private static final long serialVersionUID = -9140123220065488293L; @@ -71,24 +68,10 @@ static final class ConcatMapSingleSubscriber final Function> mapper; - final int prefetch; - final AtomicLong requested; - final AtomicThrowable errors; - final ConcatMapSingleObserver inner; - final SimplePlainQueue queue; - - final ErrorMode errorMode; - - Subscription upstream; - - volatile boolean done; - - volatile boolean cancelled; - long emitted; int consumed; @@ -107,68 +90,37 @@ static final class ConcatMapSingleSubscriber ConcatMapSingleSubscriber(Subscriber downstream, Function> mapper, int prefetch, ErrorMode errorMode) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.prefetch = prefetch; - this.errorMode = errorMode; this.requested = new AtomicLong(); - this.errors = new AtomicThrowable(); this.inner = new ConcatMapSingleObserver<>(this); - this.queue = new SpscArrayQueue<>(prefetch); } @Override - public void onSubscribe(Subscription s) { - if (SubscriptionHelper.validate(upstream, s)) { - upstream = s; - downstream.onSubscribe(this); - s.request(prefetch); - } + void onSubscribeDownstream() { + downstream.onSubscribe(this); } @Override - public void onNext(T t) { - if (!queue.offer(t)) { - upstream.cancel(); - onError(new MissingBackpressureException("queue full?!")); - return; - } + public void request(long n) { + BackpressureHelper.add(requested, n); drain(); } @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - inner.dispose(); - } - done = true; - drain(); - } - } - - @Override - public void onComplete() { - done = true; - drain(); + public void cancel() { + stop(); } @Override - public void request(long n) { - BackpressureHelper.add(requested, n); - drain(); + void clearValue() { + item = null; } @Override - public void cancel() { - cancelled = true; - upstream.cancel(); + void disposeInner() { inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - item = null; - } } void innerSuccess(R item) { @@ -187,6 +139,7 @@ void innerError(Throwable ex) { } } + @Override void drain() { if (getAndIncrement() != 0) { return; @@ -195,10 +148,11 @@ void drain() { int missed = 1; Subscriber downstream = this.downstream; ErrorMode errorMode = this.errorMode; - SimplePlainQueue queue = this.queue; + SimpleQueue queue = this.queue; AtomicThrowable errors = this.errors; AtomicLong requested = this.requested; int limit = prefetch - (prefetch >> 1); + boolean syncFused = this.syncFused; for (;;) { @@ -223,7 +177,16 @@ void drain() { if (s == STATE_INACTIVE) { boolean d = done; - T v = queue.poll(); + T v; + try { + v = queue.poll(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + errors.tryAddThrowableOrReport(ex); + errors.tryTerminateConsumer(downstream); + return; + } boolean empty = v == null; if (d && empty) { @@ -235,12 +198,14 @@ void drain() { break; } - int c = consumed + 1; - if (c == limit) { - consumed = 0; - upstream.request(limit); - } else { - consumed = c; + if (!syncFused) { + int c = consumed + 1; + if (c == limit) { + consumed = 0; + upstream.request(limit); + } else { + consumed = c; + } } SingleSource ss; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletable.java index 2295f4397e..49fcbcff83 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletable.java @@ -14,15 +14,14 @@ package io.reactivex.rxjava3.internal.operators.mixed; import java.util.Objects; -import java.util.concurrent.atomic.*; +import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; +import io.reactivex.rxjava3.internal.fuseable.SimpleQueue; import io.reactivex.rxjava3.internal.util.*; /** @@ -60,8 +59,7 @@ protected void subscribeActual(CompletableObserver observer) { } static final class ConcatMapCompletableObserver - extends AtomicInteger - implements Observer, Disposable { + extends ConcatMapXMainObserver { private static final long serialVersionUID = 3610901111000061034L; @@ -69,122 +67,36 @@ static final class ConcatMapCompletableObserver final Function mapper; - final ErrorMode errorMode; - - final AtomicThrowable errors; - final ConcatMapInnerObserver inner; - final int prefetch; - - SimpleQueue queue; - - Disposable upstream; - volatile boolean active; - volatile boolean done; - - volatile boolean disposed; - ConcatMapCompletableObserver(CompletableObserver downstream, Function mapper, ErrorMode errorMode, int prefetch) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.errorMode = errorMode; - this.prefetch = prefetch; - this.errors = new AtomicThrowable(); this.inner = new ConcatMapInnerObserver(this); } @Override - public void onSubscribe(Disposable d) { - if (DisposableHelper.validate(upstream, d)) { - this.upstream = d; - if (d instanceof QueueDisposable) { - @SuppressWarnings("unchecked") - QueueDisposable qd = (QueueDisposable) d; - - int m = qd.requestFusion(QueueDisposable.ANY); - if (m == QueueDisposable.SYNC) { - queue = qd; - done = true; - downstream.onSubscribe(this); - drain(); - return; - } - if (m == QueueDisposable.ASYNC) { - queue = qd; - downstream.onSubscribe(this); - return; - } - } - queue = new SpscLinkedArrayQueue<>(prefetch); - downstream.onSubscribe(this); - } - } - - @Override - public void onNext(T t) { - if (t != null) { - queue.offer(t); - } - drain(); - } - - @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - disposed = true; - inner.dispose(); - errors.tryTerminateConsumer(downstream); - if (getAndIncrement() == 0) { - queue.clear(); - } - } else { - done = true; - drain(); - } - } - } - - @Override - public void onComplete() { - done = true; - drain(); + void onSubscribeDownstream() { + downstream.onSubscribe(this); } @Override - public void dispose() { - disposed = true; - upstream.dispose(); + void disposeInner() { inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - } - } - - @Override - public boolean isDisposed() { - return disposed; } void innerError(Throwable ex) { if (errors.tryAddThrowableOrReport(ex)) { - if (errorMode == ErrorMode.IMMEDIATE) { - disposed = true; + if (errorMode != ErrorMode.END) { upstream.dispose(); - errors.tryTerminateConsumer(downstream); - if (getAndIncrement() == 0) { - queue.clear(); - } - } else { - active = false; - drain(); } + active = false; + drain(); } } @@ -193,6 +105,7 @@ void innerComplete() { drain(); } + @Override void drain() { if (getAndIncrement() != 0) { return; @@ -200,6 +113,7 @@ void drain() { AtomicThrowable errors = this.errors; ErrorMode errorMode = this.errorMode; + SimpleQueue queue = this.queue; do { if (disposed) { @@ -207,16 +121,17 @@ void drain() { return; } - if (!active) { - - if (errorMode == ErrorMode.BOUNDARY) { - if (errors.get() != null) { - disposed = true; - queue.clear(); - errors.tryTerminateConsumer(downstream); - return; - } + if (errors.get() != null) { + if (errorMode == ErrorMode.IMMEDIATE + || (errorMode == ErrorMode.BOUNDARY && !active)) { + disposed = true; + queue.clear(); + errors.tryTerminateConsumer(downstream); + return; } + } + + if (!active) { boolean d = done; boolean empty = true; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybe.java index 1177d5a06e..1e7090ee7d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybe.java @@ -14,15 +14,14 @@ package io.reactivex.rxjava3.internal.operators.mixed; import java.util.Objects; -import java.util.concurrent.atomic.*; +import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; +import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.util.*; /** @@ -61,8 +60,7 @@ protected void subscribeActual(Observer observer) { } static final class ConcatMapMaybeMainObserver - extends AtomicInteger - implements Observer, Disposable { + extends ConcatMapXMainObserver { private static final long serialVersionUID = -9140123220065488293L; @@ -70,20 +68,8 @@ static final class ConcatMapMaybeMainObserver final Function> mapper; - final AtomicThrowable errors; - final ConcatMapMaybeObserver inner; - final SimplePlainQueue queue; - - final ErrorMode errorMode; - - Disposable upstream; - - volatile boolean done; - - volatile boolean cancelled; - R item; volatile int state; @@ -98,60 +84,20 @@ static final class ConcatMapMaybeMainObserver ConcatMapMaybeMainObserver(Observer downstream, Function> mapper, int prefetch, ErrorMode errorMode) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.errorMode = errorMode; - this.errors = new AtomicThrowable(); this.inner = new ConcatMapMaybeObserver<>(this); - this.queue = new SpscLinkedArrayQueue<>(prefetch); - } - - @Override - public void onSubscribe(Disposable d) { - if (DisposableHelper.validate(upstream, d)) { - upstream = d; - downstream.onSubscribe(this); - } - } - - @Override - public void onNext(T t) { - queue.offer(t); - drain(); - } - - @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - inner.dispose(); - } - done = true; - drain(); - } } @Override - public void onComplete() { - done = true; - drain(); - } - - @Override - public void dispose() { - cancelled = true; - upstream.dispose(); - inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - item = null; - } + void onSubscribeDownstream() { + downstream.onSubscribe(this); } @Override - public boolean isDisposed() { - return cancelled; + void clearValue() { + item = null; } void innerSuccess(R item) { @@ -175,6 +121,12 @@ void innerError(Throwable ex) { } } + @Override + void disposeInner() { + inner.dispose(); + } + + @Override void drain() { if (getAndIncrement() != 0) { return; @@ -183,13 +135,13 @@ void drain() { int missed = 1; Observer downstream = this.downstream; ErrorMode errorMode = this.errorMode; - SimplePlainQueue queue = this.queue; + SimpleQueue queue = this.queue; AtomicThrowable errors = this.errors; for (;;) { for (;;) { - if (cancelled) { + if (disposed) { queue.clear(); item = null; break; @@ -209,7 +161,18 @@ void drain() { if (s == STATE_INACTIVE) { boolean d = done; - T v = queue.poll(); + T v; + + try { + v = queue.poll(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + disposed = true; + upstream.dispose(); + errors.tryAddThrowableOrReport(ex); + errors.tryTerminateConsumer(downstream); + return; + } boolean empty = v == null; if (d && empty) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingle.java index 70d913cd42..3ad2b53cbc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingle.java @@ -14,15 +14,14 @@ package io.reactivex.rxjava3.internal.operators.mixed; import java.util.Objects; -import java.util.concurrent.atomic.*; +import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; +import io.reactivex.rxjava3.internal.fuseable.SimpleQueue; import io.reactivex.rxjava3.internal.util.*; /** @@ -61,8 +60,7 @@ protected void subscribeActual(Observer observer) { } static final class ConcatMapSingleMainObserver - extends AtomicInteger - implements Observer, Disposable { + extends ConcatMapXMainObserver { private static final long serialVersionUID = -9140123220065488293L; @@ -70,20 +68,8 @@ static final class ConcatMapSingleMainObserver final Function> mapper; - final AtomicThrowable errors; - final ConcatMapSingleObserver inner; - final SimplePlainQueue queue; - - final ErrorMode errorMode; - - Disposable upstream; - - volatile boolean done; - - volatile boolean cancelled; - R item; volatile int state; @@ -98,78 +84,44 @@ static final class ConcatMapSingleMainObserver ConcatMapSingleMainObserver(Observer downstream, Function> mapper, int prefetch, ErrorMode errorMode) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.errorMode = errorMode; - this.errors = new AtomicThrowable(); this.inner = new ConcatMapSingleObserver<>(this); - this.queue = new SpscLinkedArrayQueue<>(prefetch); } - @Override - public void onSubscribe(Disposable d) { - if (DisposableHelper.validate(upstream, d)) { - upstream = d; - downstream.onSubscribe(this); - } - } - - @Override - public void onNext(T t) { - queue.offer(t); + void innerSuccess(R item) { + this.item = item; + this.state = STATE_RESULT_VALUE; drain(); } - @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - inner.dispose(); + void innerError(Throwable ex) { + if (errors.tryAddThrowableOrReport(ex)) { + if (errorMode != ErrorMode.END) { + upstream.dispose(); } - done = true; + this.state = STATE_INACTIVE; drain(); } } @Override - public void onComplete() { - done = true; - drain(); - } - - @Override - public void dispose() { - cancelled = true; - upstream.dispose(); + void disposeInner() { inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - item = null; - } } @Override - public boolean isDisposed() { - return cancelled; + void onSubscribeDownstream() { + downstream.onSubscribe(this); } - void innerSuccess(R item) { - this.item = item; - this.state = STATE_RESULT_VALUE; - drain(); - } - - void innerError(Throwable ex) { - if (errors.tryAddThrowableOrReport(ex)) { - if (errorMode != ErrorMode.END) { - upstream.dispose(); - } - this.state = STATE_INACTIVE; - drain(); - } + @Override + void clearValue() { + item = null; } + @Override void drain() { if (getAndIncrement() != 0) { return; @@ -178,13 +130,13 @@ void drain() { int missed = 1; Observer downstream = this.downstream; ErrorMode errorMode = this.errorMode; - SimplePlainQueue queue = this.queue; + SimpleQueue queue = this.queue; AtomicThrowable errors = this.errors; for (;;) { for (;;) { - if (cancelled) { + if (disposed) { queue.clear(); item = null; break; @@ -204,7 +156,18 @@ void drain() { if (s == STATE_INACTIVE) { boolean d = done; - T v = queue.poll(); + T v; + + try { + v = queue.poll(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + disposed = true; + upstream.dispose(); + errors.tryAddThrowableOrReport(ex); + errors.tryTerminateConsumer(downstream); + return; + } boolean empty = v == null; if (d && empty) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription.java b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription.java index b50b4e13f9..155f9ebb48 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription.java @@ -115,7 +115,7 @@ public final void complete(T v) { lazySet(FUSED_READY); Subscriber a = downstream; - a.onNext(v); + a.onNext(null); if (get() != CANCELLED) { a.onComplete(); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletableTest.java index 28c90619bb..6a67cb9fee 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletableTest.java @@ -28,7 +28,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.subjects.CompletableSubject; import io.reactivex.rxjava3.testsupport.*; @@ -66,6 +66,14 @@ public void simpleLongPrefetch() { .assertResult(); } + @Test + public void simpleLongPrefetchHidden() { + Flowable.range(1, 1024).hide() + .concatMapCompletable(Functions.justFunction(Completable.complete()), 32) + .test() + .assertResult(); + } + @Test public void mainError() { Flowable.error(new TestException()) @@ -431,4 +439,54 @@ public Completable apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Flowable.range(1, 5).hide() + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicSyncFused() { + Flowable.range(1, 5) + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicAsyncFused() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectFlowableFusion() + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Flowable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.flowableStripBoundary()) + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertFailure(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybeTest.java index 8a60f6c367..595740a452 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybeTest.java @@ -19,11 +19,11 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; @@ -31,7 +31,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.internal.util.ErrorMode; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; @@ -54,15 +54,19 @@ public MaybeSource apply(Integer v) } @Test - public void simpleLong() { + public void simpleLongPrefetch() { Flowable.range(1, 1024) - .concatMapMaybe(new Function>() { - @Override - public MaybeSource apply(Integer v) - throws Exception { - return Maybe.just(v); - } - }, 32) + .concatMapMaybe(Maybe::just, 32) + .test() + .assertValueCount(1024) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void simpleLongPrefetchHidden() { + Flowable.range(1, 1024).hide() + .concatMapMaybe(Maybe::just, 32) .test() .assertValueCount(1024) .assertNoErrors() @@ -464,4 +468,54 @@ public Maybe apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Flowable.range(1, 5).hide() + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicSyncFused() { + Flowable.range(1, 5) + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicAsyncFused() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectFlowableFusion() + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Flowable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.flowableStripBoundary()) + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingleTest.java index a2e5d73c09..73426eda72 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingleTest.java @@ -30,7 +30,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.internal.util.ErrorMode; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.subjects.SingleSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -52,15 +52,19 @@ public SingleSource apply(Integer v) } @Test - public void simpleLong() { + public void simpleLongPrefetch() { Flowable.range(1, 1024) - .concatMapSingle(new Function>() { - @Override - public SingleSource apply(Integer v) - throws Exception { - return Single.just(v); - } - }, 32) + .concatMapSingle(Single::just, 32) + .test() + .assertValueCount(1024) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void simpleLongPrefetchHidden() { + Flowable.range(1, 1024).hide() + .concatMapSingle(Single::just, 32) .test() .assertValueCount(1024) .assertNoErrors() @@ -382,4 +386,54 @@ public Single apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Flowable.range(1, 5).hide() + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicSyncFused() { + Flowable.range(1, 5) + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicAsyncFused() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectFlowableFusion() + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Flowable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.flowableStripBoundary()) + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletableTest.java index 540e0828af..ae0ce25d91 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletableTest.java @@ -474,4 +474,54 @@ public Completable apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Observable.range(1, 5).hide() + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicSyncFused() { + Observable.range(1, 5) + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicAsyncFused() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectObservableFusion() + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Observable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.observableStripBoundary()) + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertFailure(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybeTest.java index 323cbc3270..120e027538 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybeTest.java @@ -485,4 +485,54 @@ public Maybe apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Observable.range(1, 5).hide() + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicSyncFused() { + Observable.range(1, 5) + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicAsyncFused() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectObservableFusion() + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Observable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.observableStripBoundary()) + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingleTest.java index 90f15ac250..76bf708e55 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingleTest.java @@ -425,4 +425,54 @@ public Single apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Observable.range(1, 5).hide() + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicSyncFused() { + Observable.range(1, 5) + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicAsyncFused() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectObservableFusion() + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Observable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.observableStripBoundary()) + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java b/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java index 1963a38459..9a19f84a7a 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java @@ -3018,6 +3018,12 @@ public static void checkInvalidParallelSubscribers(ParallelFlowable sourc } } + /** + * Creates a fuseable Observable that does not emit anything but rejects + * fusion requests. + * @param the element type + * @return the new Observable + */ public static Observable rejectObservableFusion() { return new Observable() { @Override @@ -3066,6 +3072,12 @@ public boolean isDisposed() { }; } + /** + * Creates a fuseable Flowable that does not emit anything but rejects + * fusion requests. + * @param the element type + * @return the new Observable + */ public static Flowable rejectFlowableFusion() { return new Flowable() { @Override From 171c84677739d63ef778b8893db845ac7abc81e6 Mon Sep 17 00:00:00 2001 From: SergejIsbrecht <4427685+SergejIsbrecht@users.noreply.github.com> Date: Thu, 28 Jan 2021 16:29:04 +0100 Subject: [PATCH 115/521] 3.x: Introduce property rx3.scheduler.use-nanotime (#7169) Issue-Id: #7154 Co-authored-by: Sergej Isbrecht --- .../io/reactivex/rxjava3/core/Scheduler.java | 42 ++++++++++++++++--- .../rxjava3/schedulers/Schedulers.java | 2 + .../reactivex/rxjava3/core/SchedulerTest.java | 38 +++++++++++++++++ 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Scheduler.java b/src/main/java/io/reactivex/rxjava3/core/Scheduler.java index bd121c06c4..3c6a6ad751 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Scheduler.java +++ b/src/main/java/io/reactivex/rxjava3/core/Scheduler.java @@ -60,8 +60,9 @@ * interface which can grant access to the original or hooked {@code Runnable}, thus, a repeated {@code RxJavaPlugins.onSchedule} * can detect the earlier hook and not apply a new one over again. *

    - * The default implementation of {@link #now(TimeUnit)} and {@link Worker#now(TimeUnit)} methods to return current - * {@link System#currentTimeMillis()} value in the desired time unit. Custom {@code Scheduler} implementations can override this + * The default implementation of {@link #now(TimeUnit)} and {@link Worker#now(TimeUnit)} methods to return current {@link System#currentTimeMillis()} + * value in the desired time unit, unless {@code rx3.scheduler.use-nanotime} (boolean) is set. When the property is set to + * {@code true}, the method uses {@link System#nanoTime()} as its basis instead. Custom {@code Scheduler} implementations can override this * to provide specialized time accounting (such as virtual time to be advanced programmatically). * Note that operators requiring a {@code Scheduler} may rely on either of the {@code now()} calls provided by * {@code Scheduler} or {@code Worker} respectively, therefore, it is recommended they represent a logically @@ -88,6 +89,34 @@ * All methods on the {@code Scheduler} and {@code Worker} classes should be thread safe. */ public abstract class Scheduler { + /** + * Value representing whether to use {@link System#nanoTime()}, or default as clock for {@link #now(TimeUnit)} + * and {@link Scheduler.Worker#now(TimeUnit)} + *

    + * Associated system parameter: + *

      + *
    • {@code rx3.scheduler.use-nanotime}, boolean, default {@code false} + *
    + */ + static boolean IS_DRIFT_USE_NANOTIME = Boolean.getBoolean("rx3.scheduler.use-nanotime"); + + /** + * Returns the current clock time depending on state of {@link Scheduler#IS_DRIFT_USE_NANOTIME} in given {@code unit} + *

    + * By default {@link System#currentTimeMillis()} will be used as the clock. When the property is set + * {@link System#nanoTime()} will be used. + *

    + * @param unit the time unit + * @return the 'current time' in given unit + * @throws NullPointerException if {@code unit} is {@code null} + */ + static long computeNow(TimeUnit unit) { + if(!IS_DRIFT_USE_NANOTIME) { + return unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + return unit.convert(System.nanoTime(), TimeUnit.NANOSECONDS); + } + /** * The tolerance for a clock drift in nanoseconds where the periodic scheduler will rebase. *

    @@ -156,7 +185,7 @@ public static long clockDriftTolerance() { * @since 2.0 */ public long now(@NonNull TimeUnit unit) { - return unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS); + return computeNow(unit); } /** @@ -362,8 +391,9 @@ public S when(@NonNull Function - * The default implementation of the {@link #now(TimeUnit)} method returns current - * {@link System#currentTimeMillis()} value in the desired time unit. Custom {@code Worker} implementations can override this + * The default implementation of the {@link #now(TimeUnit)} method returns current {@link System#currentTimeMillis()} + * value in the desired time unit, unless {@code rx3.scheduler.use-nanotime} (boolean) is set. When the property is set to + * {@code true}, the method uses {@link System#nanoTime()} as its basis instead. Custom {@code Worker} implementations can override this * to provide specialized time accounting (such as virtual time to be advanced programmatically). * Note that operators requiring a scheduler may rely on either of the {@code now()} calls provided by * {@code Scheduler} or {@code Worker} respectively, therefore, it is recommended they represent a logically @@ -482,7 +512,7 @@ public Disposable schedulePeriodically(@NonNull Runnable run, final long initial * @since 2.0 */ public long now(@NonNull TimeUnit unit) { - return unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS); + return computeNow(unit); } /** diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java index 5456ea1924..9e6e92295e 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java @@ -40,6 +40,8 @@ *

  • {@code rx3.single-priority} (int): sets the thread priority of the {@link #single()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
  • *
  • {@code rx3.purge-enabled} (boolean): enables periodic purging of all {@code Scheduler}'s backing thread pools, default is {@code false}
  • *
  • {@code rx3.purge-period-seconds} (int): specifies the periodic purge interval of all {@code Scheduler}'s backing thread pools, default is 1 second
  • + *
  • {@code rx3.scheduler.use-nanotime} (boolean): {@code true} instructs {@code Scheduler} to use {@link System#nanoTime()} for {@link Scheduler#now(TimeUnit)}, + * instead of default {@link System#currentTimeMillis()} ({@code false})
  • *
*/ public final class Schedulers { diff --git a/src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java b/src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java index a8e661fe75..e7c095f152 100644 --- a/src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java @@ -16,10 +16,48 @@ package io.reactivex.rxjava3.core; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.After; import org.junit.Test; +import java.util.concurrent.TimeUnit; + public class SchedulerTest { + private static final String DRIFT_USE_NANOTIME = "rx3.scheduler.use-nanotime"; + + @After + public void cleanup() { + // reset value to default in order to not influence other tests + Scheduler.IS_DRIFT_USE_NANOTIME = false; + } + + @Test + public void driftUseNanoTimeNotSetByDefault() { + assertFalse(Scheduler.IS_DRIFT_USE_NANOTIME); + assertFalse(Boolean.getBoolean(DRIFT_USE_NANOTIME)); + } + + @Test + public void computeNow_currentTimeMillis() { + TimeUnit unit = TimeUnit.MILLISECONDS; + assertTrue(isInRange(System.currentTimeMillis(), Scheduler.computeNow(unit), unit, 250, TimeUnit.MILLISECONDS)); + } + + @Test + public void computeNow_nanoTime() { + TimeUnit unit = TimeUnit.NANOSECONDS; + Scheduler.IS_DRIFT_USE_NANOTIME = true; + + assertFalse(isInRange(System.currentTimeMillis(), Scheduler.computeNow(unit), unit, 250, TimeUnit.MILLISECONDS)); + assertTrue(isInRange(System.nanoTime(), Scheduler.computeNow(unit), TimeUnit.NANOSECONDS, 250, TimeUnit.MILLISECONDS)); + } + + private boolean isInRange(long start, long stop, TimeUnit source, long maxDiff, TimeUnit diffUnit) { + long diff = Math.abs(stop - start); + return diffUnit.convert(diff, source) <= maxDiff; + } @Test public void clockDriftCalculation() { From 8dd6f21fbe7fde96b62e29d7b035fa4e7f55e52e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 29 Jan 2021 08:56:24 +0100 Subject: [PATCH 116/521] Bump build-info-extractor-gradle from 4.19.0 to 4.20.0 (#7171) Bumps build-info-extractor-gradle from 4.19.0 to 4.20.0. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1fb259d600..a9caec8d0c 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.2" ext.licenseVersion = "0.15.0" ext.bintrayVersion = "1.8.5" - ext.jfrogExtractorVersion = "4.19.0" + ext.jfrogExtractorVersion = "4.20.0" ext.bndVersion = "5.2.0" ext.checkstyleVersion = "8.26" From 4d325c6b73a47908d268bb3d79c40195ff7dcd82 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Mon, 1 Feb 2021 09:47:57 +0100 Subject: [PATCH 117/521] 3.x: Workaround for FutureTask.toString + JDK 11 build (#7173) --- .github/workflows/gradle_jdk11.yml | 33 +++++++++++++ .../schedulers/AbstractDirectTask.java | 20 ++++++++ .../ScheduledDirectPeriodicTask.java | 2 +- .../schedulers/ScheduledRunnable.java | 24 +++++++++- .../schedulers/AbstractDirectTaskTest.java | 27 +++++++++++ .../schedulers/ScheduledRunnableTest.java | 25 ++++++++++ .../schedulers/AbstractSchedulerTests.java | 48 +++++++++++++++++++ 7 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/gradle_jdk11.yml diff --git a/.github/workflows/gradle_jdk11.yml b/.github/workflows/gradle_jdk11.yml new file mode 100644 index 0000000000..f81dc9de27 --- /dev/null +++ b/.github/workflows/gradle_jdk11.yml @@ -0,0 +1,33 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: JDK 11 + +on: + push: + branches: [ 3.x ] + pull_request: + branches: [ 3.x ] + +jobs: + build: + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build PR + run: ./gradlew -PreleaseMode=pr build --stacktrace + #- name: Upload to Codecov + # uses: codecov/codecov-action@v1 \ No newline at end of file diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTask.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTask.java index a1c4bc2ce1..7ae69a3ea5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTask.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTask.java @@ -83,4 +83,24 @@ public final void setFuture(Future future) { public Runnable getWrappedRunnable() { return runnable; } + + @Override + public String toString() { + String status; + Future f = get(); + if (f == FINISHED) { + status = "Finished"; + } else if (f == DISPOSED) { + status = "Disposed"; + } else { + Thread r = runner; + if (r != null) { + status = "Running on " + runner; + } else { + status = "Waiting"; + } + } + + return getClass().getSimpleName() + "[" + status + "]"; + } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java index 4be6e4b558..a3b90bdf76 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java @@ -39,8 +39,8 @@ public void run() { runner = null; } catch (Throwable ex) { // Exceptions.throwIfFatal(ex); nowhere to go - runner = null; dispose(); + runner = null; RxJavaPlugins.onError(ex); throw ex; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java index d437985890..063c48163b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java @@ -69,7 +69,6 @@ public void run() { throw e; } } finally { - lazySet(THREAD_INDEX, null); Object o = get(PARENT_INDEX); if (o != PARENT_DISPOSED && compareAndSet(PARENT_INDEX, o, DONE) && o != null) { ((DisposableContainer)o).delete(this); @@ -81,6 +80,7 @@ public void run() { break; } } + lazySet(THREAD_INDEX, null); } } @@ -137,4 +137,26 @@ public boolean isDisposed() { Object o = get(PARENT_INDEX); return o == PARENT_DISPOSED || o == DONE; } + + @Override + public String toString() { + String state; + Object o = get(FUTURE_INDEX); + if (o == DONE) { + state = "Finished"; + } else if (o == SYNC_DISPOSED) { + state = "Disposed(Sync)"; + } else if (o == ASYNC_DISPOSED) { + state = "Disposed(Async)"; + } else { + o = get(THREAD_INDEX); + if (o == null) { + state = "Waiting"; + } else { + state = "Running on " + o; + } + } + + return getClass().getSimpleName() + "[" + state + "]"; + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTaskTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTaskTest.java index 85507fb7f1..dfd3ffeae9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTaskTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTaskTest.java @@ -241,4 +241,31 @@ public void run() { TestHelper.race(r1, r2); } } + + static class TestDirectTask extends AbstractDirectTask { + private static final long serialVersionUID = 587679821055711738L; + + TestDirectTask() { + super(Functions.EMPTY_RUNNABLE); + } + } + + @Test + public void toStringStates() { + TestDirectTask task = new TestDirectTask(); + + assertEquals("TestDirectTask[Waiting]", task.toString()); + + task.runner = Thread.currentThread(); + + assertEquals("TestDirectTask[Running on " + Thread.currentThread() + "]", task.toString()); + + task.dispose(); + + assertEquals("TestDirectTask[Disposed]", task.toString()); + + task.set(AbstractDirectTask.FINISHED); + + assertEquals("TestDirectTask[Finished]", task.toString()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java index f0577e41ca..4225a71c05 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java @@ -399,4 +399,29 @@ public void withParentIsDisposed() { assertFalse(set.remove(run)); } + + @Test + public void toStringStates() { + CompositeDisposable set = new CompositeDisposable(); + ScheduledRunnable task = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, set); + + assertEquals("ScheduledRunnable[Waiting]", task.toString()); + + task.set(ScheduledRunnable.THREAD_INDEX, Thread.currentThread()); + + assertEquals("ScheduledRunnable[Running on " + Thread.currentThread() + "]", task.toString()); + + task.dispose(); + + assertEquals("ScheduledRunnable[Disposed(Sync)]", task.toString()); + + task.set(ScheduledRunnable.FUTURE_INDEX, ScheduledRunnable.DONE); + + assertEquals("ScheduledRunnable[Finished]", task.toString()); + + task = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, set); + task.dispose(); + + assertEquals("ScheduledRunnable[Disposed(Async)]", task.toString()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerTests.java b/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerTests.java index 34d8ac1b39..6c32a09898 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerTests.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerTests.java @@ -27,6 +27,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Scheduler.Worker; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.SequentialDisposable; @@ -771,4 +772,51 @@ public void schedulePeriodicallyDirectNullRunnable() { assertEquals("run is null", npe.getMessage()); } } + + void schedulePrint(Function onSchedule) { + CountDownLatch waitForBody = new CountDownLatch(1); + CountDownLatch waitForPrint = new CountDownLatch(1); + + try { + Disposable d = onSchedule.apply(() -> { + waitForBody.countDown(); + try { + waitForPrint.await(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + }); + + waitForBody.await(); + + assertNotEquals("", d.toString()); + } catch (Throwable ex) { + throw new AssertionError(ex); + } finally { + waitForPrint.countDown(); + } + } + + @Test + public void scheduleDirectPrint() { + if (getScheduler() instanceof TrampolineScheduler) { + // no concurrency with Trampoline + return; + } + schedulePrint(r -> getScheduler().scheduleDirect(r)); + } + + @Test + public void schedulePrint() { + if (getScheduler() instanceof TrampolineScheduler) { + // no concurrency with Trampoline + return; + } + Worker worker = getScheduler().createWorker(); + try { + schedulePrint(worker::schedule); + } finally { + worker.dispose(); + } + } } From d7805f867589addbfbbad497806934c6fe347266 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Tue, 2 Feb 2021 12:36:35 +0100 Subject: [PATCH 118/521] 3.x: Allow reruns of release actions by enabling bintray.override (#7176) --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index a9caec8d0c..5708d4d1be 100644 --- a/build.gradle +++ b/build.gradle @@ -349,6 +349,7 @@ if (rootProject.hasProperty("releaseMode")) { key = rootProject.bintrayKey configurations = ["archives"] publish = true + override = true // Allows re-running the GHA upon release hangs pkg { repo = "RxJava" name = "RxJava" From 0cb8978c1d21363eac53299272295567efb13a55 Mon Sep 17 00:00:00 2001 From: Wayne Yang Date: Wed, 3 Feb 2021 02:02:29 +0800 Subject: [PATCH 119/521] Fix the typos (#7178) Co-authored-by: Wayne_Yang --- .../java/io/reactivex/rxjava3/schedulers/Schedulers.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java index 9e6e92295e..83fee3be34 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java @@ -367,7 +367,7 @@ public static Scheduler single() { * because such circumstances prevent RxJava from progressing flow-related activities correctly. * If the {@link Executor#execute(Runnable)} or {@link ExecutorService#submit(Callable)} throws, * the {@code RejectedExecutionException} is routed to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-reladed problems, it is recommended + * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-related problems, it is recommended * all flows using the returned {@code Scheduler} to be canceled/disposed before the underlying * {@code Executor} is shut down. To avoid problems due to the {@code Executor} having a bounded-queue, * it is recommended to rephrase the flow to utilize backpressure as the means to limit outstanding work. @@ -441,7 +441,7 @@ public static Scheduler from(@NonNull Executor executor) { * because such circumstances prevent RxJava from progressing flow-related activities correctly. * If the {@link Executor#execute(Runnable)} or {@link ExecutorService#submit(Callable)} throws, * the {@code RejectedExecutionException} is routed to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-reladed problems, it is recommended + * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-related problems, it is recommended * all flows using the returned {@code Scheduler} to be canceled/disposed before the underlying * {@code Executor} is shut down. To avoid problems due to the {@code Executor} having a bounded-queue, * it is recommended to rephrase the flow to utilize backpressure as the means to limit outstanding work. @@ -521,7 +521,7 @@ public static Scheduler from(@NonNull Executor executor, boolean interruptibleWo * because such circumstances prevent RxJava from progressing flow-related activities correctly. * If the {@link Executor#execute(Runnable)} or {@link ExecutorService#submit(Callable)} throws, * the {@code RejectedExecutionException} is routed to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-reladed problems, it is recommended + * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-related problems, it is recommended * all flows using the returned {@code Scheduler} to be canceled/disposed before the underlying * {@code Executor} is shut down. To avoid problems due to the {@code Executor} having a bounded-queue, * it is recommended to rephrase the flow to utilize backpressure as the means to limit outstanding work. From 5fb0ace5f6af6b20cb7f7fcb83c502002b438d10 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 5 Feb 2021 17:48:08 +0100 Subject: [PATCH 120/521] 3.x: Release to Sonatype directly (#7181) * 3.x: Release to Sonatype directly * Update build.gradle * Update gradle.properties * Turn RS into compile dependency in POM * Fix the POM of the snapshot/artifactory upload --- .github/workflows/gradle_release.yml | 14 ++- build.gradle | 149 ++++++--------------------- gradle.properties | 24 ++++- gradle/javadoc_cleanup.gradle | 1 - 4 files changed, 66 insertions(+), 122 deletions(-) diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 6e5af8f48f..6641017cc9 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -17,11 +17,11 @@ jobs: env: # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions # ------------------------------------------------------------------------------ - bintrayUser: ${{ secrets.BINTRAY_USER }} - bintrayKey: ${{ secrets.BINTRAY_KEY }} - sonatypeUsername: ${{ secrets.SONATYPE_USER }} - sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }} JAVADOCS_TOKEN: ${{ secrets.JAVADOCS_TOKEN }} + ORG_GRADLE_PROJECT_SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_USER }} + ORG_GRADLE_PROJECT_SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + ORG_GRADLE_PROJECT_SIGNING_PRIVATE_KEY: ${{ secrets.SIGNING_PRIVATE_KEY }} + ORG_GRADLE_PROJECT_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} # ------------------------------------------------------------------------------ CI_BUILD_NUMBER: ${{ github.run_number }} steps: @@ -43,8 +43,12 @@ jobs: - name: Extract version tag run: echo "BUILD_TAG=${GITHUB_REF:10}" >> $GITHUB_ENV - name: Build and Release - run: ./gradlew -PreleaseMode=full -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" build --stacktrace + run: ./gradlew -PreleaseMode=full build --stacktrace --no-daemon - name: Upload to Codecov uses: codecov/codecov-action@v1 + - name: Upload release + run: ./gradlew -PreleaseMode=full javadocCleanup uploadArchives --no-daemon --no-parallel + - name: Publish release + run: ./gradlew -PreleaseMode=full closeAndReleaseRepository --no-daemon --no-parallel - name: Push Javadocs run: ./push_javadoc.sh diff --git a/build.gradle b/build.gradle index 5708d4d1be..6c256f24ae 100644 --- a/build.gradle +++ b/build.gradle @@ -13,15 +13,14 @@ buildscript { ext.jacocoVersion = "0.8.4" ext.animalSnifferVersion = "1.5.2" ext.licenseVersion = "0.15.0" - ext.bintrayVersion = "1.8.5" ext.jfrogExtractorVersion = "4.20.0" ext.bndVersion = "5.2.0" ext.checkstyleVersion = "8.26" + ext.vanniktechPublishPlugin = "0.13.0" // -------------------------------------- repositories { - jcenter() mavenCentral() maven { url "https://plugins.gradle.org/m2/" @@ -31,28 +30,27 @@ buildscript { classpath "ru.vyarus:gradle-animalsniffer-plugin:$animalSnifferVersion" classpath "gradle.plugin.com.hierynomus.gradle.plugins:license-gradle-plugin:$licenseVersion" classpath "me.champeau.gradle:jmh-gradle-plugin:$jmhGradleVersion" - classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$bintrayVersion" classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$jfrogExtractorVersion" classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:$bndVersion" + classpath "com.vanniktech:gradle-maven-publish-plugin:$vanniktechPublishPlugin" } } group = "io.reactivex.rxjava3" ext.githubProjectName = "rxjava" -version = project.properties["release.version"] - def releaseTag = System.getenv("BUILD_TAG"); if (releaseTag != null && !releaseTag.isEmpty()) { if (releaseTag.startsWith("v")) { releaseTag = releaseTag.substring(1); } - version = releaseTag; - project.properties.put("release.version", releaseTag); + project.setProperty("VERSION_NAME" , releaseTag); println("Releasing with version " + version); } +version = project.properties["VERSION_NAME"] + description = "RxJava: Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM." apply plugin: "java-library" @@ -62,7 +60,6 @@ apply plugin: "ru.vyarus.animalsniffer" apply plugin: "maven" apply plugin: "me.champeau.gradle.jmh" apply plugin: "com.github.hierynomus.license" -apply plugin: "com.jfrog.bintray" apply plugin: "com.jfrog.artifactory" apply plugin: "eclipse" @@ -116,21 +113,7 @@ animalsniffer { annotation = "io.reactivex.rxjava3.internal.util.SuppressAnimalSniffer" } -task sourcesJar(type: Jar, dependsOn: classes) { - classifier = "sources" - from sourceSets.main.allSource -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = "javadoc" - from javadoc.destinationDir -} - -artifacts { - archives jar - archives sourcesJar - archives javadocJar -} +apply plugin: 'maven' apply plugin: 'biz.aQute.bnd.builder' @@ -154,61 +137,6 @@ license { excludes(["**/*.md", "**/*.txt"]) } -apply plugin: "maven-publish" - -install { - repositories.mavenInstaller.pom.project { - name "RxJava" - description "Reactive Extensions for Java" - url "https://github.com/ReactiveX/RxJava" - licenses { - license { - name "The Apache Software License, Version 2.0" - url "http://www.apache.org/licenses/LICENSE-2.0.txt" - distribution "repo" - } - } - developers { - developer { - id "akarnokd" - name "David Karnok" - email "akarnokd@gmail.com" - } - } - scm { - connection "scm:git:git@github.com:ReactiveX/RxJava.git" - url "scm:git:git@github.com:ReactiveX/RxJava.git" - developerConnection "scm:git:git@github.com:ReactiveX/RxJava.git" - } - issueManagement { - system "github" - url "https://github.com/ReactiveX/RxJava/issues" - } - } -} - -publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact (sourcesJar) { - classifier = "sources" - } - } - } -} - -// Reactive-Streams as compile dependency -publishing.publications.all { - pom.withXml { - asNode().dependencies."*".findAll() { - it.scope.text() == "runtime" && project.configurations.compile.allDependencies.find { dep -> - dep.name == it.artifactId.text() - } - }.each { it.scope*.value = "compile"} - } -} - jmh { jmhVersion = jmhLibVersion humanOutputFile = null @@ -312,12 +240,28 @@ checkstyle { toolVersion = checkstyleVersion } +apply from: file("gradle/javadoc_cleanup.gradle") + +def fixPom() { + // Reactive-Streams as compile dependency + publishing.publications.all { + pom.withXml { + asNode().dependencies."*".findAll() { + it.scope.text() == "runtime" && project.configurations.compile.allDependencies.find { dep -> + dep.name == it.artifactId.text() + } + }.each { it.scope*.value = "compile"} + } + } +} + if (rootProject.hasProperty("releaseMode")) { if ("branch".equals(rootProject.releaseMode)) { // From https://github.com/ReactiveX/RxAndroid/blob/2.x/rxandroid/build.gradle#L94 println("ReleaseMode: " + rootProject.releaseMode); + artifactory { contextUrl = "https://oss.jfrog.org/" @@ -332,48 +276,23 @@ if (rootProject.hasProperty("releaseMode")) { defaults { publishConfigs("archives") } + + fixPom() } } build.finalizedBy(artifactoryPublish) } - if ("full".equals(rootProject.releaseMode)) { - // based on https://github.com/bintray/gradle-bintray-plugin - def rver = version; - - println("ReleaseMode: " + rootProject.releaseMode + " version " + rver); - - bintray { - user = rootProject.bintrayUser - key = rootProject.bintrayKey - configurations = ["archives"] - publish = true - override = true // Allows re-running the GHA upon release hangs - pkg { - repo = "RxJava" - name = "RxJava" - userOrg = "reactivex" - labels = ["rxjava", "reactivex"] - licenses = ["Apache-2.0"] - vcsUrl = "https://github.com/ReactiveX/RxJava.git" - version { - name = rver - gpg { - sign = true - } - mavenCentralSync { - sync = true - user = rootProject.sonatypeUsername - password = rootProject.sonatypePassword - close = "1" - } - } - } + if ("full".equals(rootProject.releaseMode)) { + apply plugin: "com.vanniktech.maven.publish" + + fixPom() + + signing { + if (project.hasProperty('SIGNING_PRIVATE_KEY') && project.hasProperty('SIGNING_PASSWORD')) { + useInMemoryPgpKeys(project.getProperty('SIGNING_PRIVATE_KEY'), project.getProperty('SIGNING_PASSWORD')) } - - build.finalizedBy(bintrayUpload) - } + } + } } - -apply from: file("gradle/javadoc_cleanup.gradle") diff --git a/gradle.properties b/gradle.properties index 820b2a5bc1..be63fe3d85 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,24 @@ release.scope=patch -release.version=3.0.0-SNAPSHOT +VERSION_NAME=3.0.0-SNAPSHOT + +GROUP=io.reactivex.rxjava3 +POM_ARTIFACT_ID=rxjava +POM_NAME=RxJava +POM_PACKAGING=jar + +POM_DESCRIPTION=Reactive Extensions for Java +POM_INCEPTION_YEAR=2013 + +POM_URL=https://github.com/akarnokd/RxJavaExtensions +POM_SCM_URL=https://github.com/ReactiveX/RxJava +POM_SCM_CONNECTION=scm:git:git://github.com/ReactiveX/RxJava.git +POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/ReactiveX/RxJava.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=akarnokd +POM_DEVELOPER_NAME=David Karnok +POM_DEVELOPER_URL=https://github.com/akarnokd/ +POM_DEVELOPER_EMAIL=akarnokd@gmail.com diff --git a/gradle/javadoc_cleanup.gradle b/gradle/javadoc_cleanup.gradle index 64f6437288..f6811fdcaf 100644 --- a/gradle/javadoc_cleanup.gradle +++ b/gradle/javadoc_cleanup.gradle @@ -57,5 +57,4 @@ def fixJavadocFile(file) { file.setText(fileContents, 'UTF-8'); } -javadocJar.dependsOn javadocCleanup build.dependsOn javadocCleanup \ No newline at end of file From 27cd0f7e86ffde69689fd857abb6258322f37869 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 5 Feb 2021 20:00:22 +0100 Subject: [PATCH 121/521] Update build.gradle --- build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.gradle b/build.gradle index 6c256f24ae..4a2b4949e2 100644 --- a/build.gradle +++ b/build.gradle @@ -294,5 +294,10 @@ if (rootProject.hasProperty("releaseMode")) { useInMemoryPgpKeys(project.getProperty('SIGNING_PRIVATE_KEY'), project.getProperty('SIGNING_PASSWORD')) } } + mavenPublish { + nexus { + stagingProfile = "io.reactivex" + } + } } } From 4e86963d9710d4f8fcbba0451e250237f4db15f8 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 5 Feb 2021 20:27:48 +0100 Subject: [PATCH 122/521] Update gradle_release.yml --- .github/workflows/gradle_release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 6641017cc9..9bca1fb9ec 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -47,8 +47,8 @@ jobs: - name: Upload to Codecov uses: codecov/codecov-action@v1 - name: Upload release - run: ./gradlew -PreleaseMode=full javadocCleanup uploadArchives --no-daemon --no-parallel + run: ./gradlew -PreleaseMode=full javadocCleanup uploadArchives --no-daemon --no-parallel --stacktrace - name: Publish release - run: ./gradlew -PreleaseMode=full closeAndReleaseRepository --no-daemon --no-parallel + run: ./gradlew -PreleaseMode=full closeAndReleaseRepository --no-daemon --no-parallel --stacktrace - name: Push Javadocs run: ./push_javadoc.sh From adc3a5be7ab276297a2907f3e2f1581fb5b4179f Mon Sep 17 00:00:00 2001 From: David Karnok Date: Sat, 13 Feb 2021 09:20:06 +0100 Subject: [PATCH 123/521] 3.x: Upgrade to Gradle 6.8.2 (#7184) --- gradle/wrapper/gradle-wrapper.jar | Bin 55616 -> 58702 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 29 ++++++++++------------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016b3885f6930543d57b744ea8c220a1a..cc4fdc293d0e50b0ad9b65c16e7ddd1db2f6025b 100644 GIT binary patch delta 16535 zcmZ9zbyyr-lRgZCTOhc*ySoH;cXxO94DJ#b+}+(FxVr{|dypU*+~LbU`|kes`Fj57 zyY8yfeVy*U&eSRCZ-Sbgglb@bL`kJuD(i%VfWU)-fM5ZAqre6!L1F?a*_h28Ox@k% z)ux=5zF-P1b$GIsh22W}rhGA$wY4AMj)Kul`ohep<{7-Ia88yvi6?!4@QO*mP1?8% z^+-G1h=Bla=)vYr;y%0F`7k?YyaR;riRpp3>1dAn4tcrPo2W>F8o&vIoo8FT(bXb?GlmSb7V9@<6RmZzUyg~x=I4k!GQX(!lDs)h5@qh6pkwH=O@3LDKNm1i;WQ8o$Fl=C^mx!!2RpT&LbaQ5~-gj zk}V-#Uq1+j(|;TD?e?fpp}ORH^Fq!uFQ{?+R=-AAXl>dQHNRxA%eOvJm2_4jRrfpH z5-aw5XpBp(8nzoT7~-#u+*s{L@q<(~8X0g_k%xjtgn)pDhk$?(g|LNWtR{hhfS~+K zG5zN~69PBXF|=_%h}_p27^B$eqeB|SWFatETD2Oq;%Vn$m>?Zn)|n^BYMi`It%~RE z{?zseJ_NVFBivK1vbQd!dzAq}2e$&>Wo6B}`={5MckUhxc|L^S-q?bQA7!N=FxZWT zU=VP`Gg4To%<=zBf<;qVDNMDbkkc&;M*Z23z5%huy5rEWEer-UUAsxdlvL`%T?_}| z(AC(*xAH|wk8S#%l@lNw>O44BZp257X zHvrr{{odBrGrE6ZV); zj8iGg2`q{Cm5o=D;JE|EG^sx`O)a|Vsgst~3Ake^OY!6;?G&szhN9ov0-!PbvBcU5 zGRjaV&=KpDs4zqyN`T#AmhHfP#k*wGhXF?Dga*x|Bj`& zHV~0hpwX|JkNK!dAqe;o8Ea%7b%IeQD~k(41Q0J{%pt1LS1Ggcq3FOT= z5A|Vo_JTwHTm_Y#V?{dbMum`oDTd}5=vi-t>w&h{Z8|8w&TVt0^eE-i3>R&hl&SM_ zmq)Meerq`|97S(0OKH~x2bnWXD<9`-`tCM{=8}{PSRq_%t`k~5fPh}{h3YIkjBTGneZ+JF+OuXd^<)_ZuX5$u&ZP+pP<2g_}pc)~MKJVi9<{(FJ?Nr^j) z=vL&X+rs>>ym1r>$ddJHuRN}3R53kb3p*4jpEpZzzA*8+3P^Zm_{$%#!r=GQC(O@C zx6Lk~7MUL^QcV)@DgnE*4-XV`3c`9c&QcG>RRmvV%AHUPa?0%()8%asP!noiK|7#1;^qznQT z0~b;d`W|`=o_E4xvzJ%-6v|@%kGFdG2L#9-_6miL%AA`Q8UkV!?(cf~&k72JLx7X8 zv@-Q{@Bp3R5(7&$x6}zVF+a8(xRIt{)nsT>+Jf4+pyjHxT1sjigKcbRQ&rGv`O^=% z9loFMTS2`MJnyO-KNl${u=ILJh5e4pedY`0;4eN1B{>+214bTnrh^ygc0ClRkGF-6 z^KM>p6MJ-DjzMz}f}!mS!&hQLdMYMBZn`5Ft}T)22E31R0j608`P&({6Sv z+~0D8pDl^uBMtG_h6A3r60>3 ze}0-}HvlSJitaX&`j_DjiW^0DaQ|}DHmI7NLj)$z@t4@n`b%CaxbCFQaar%#KMbFrP8;UV*=UXv2t~N7${I78|hP9xX|r*{0)ZBS-A2?pnEp z5{%38c<{72i%oG5F zBn@<(E_yi9g#uyMnN0S#v~L6&+}+@3~P5v<;rEzy3qM((!S^E7A$!`9*Z zfXHq{x|C#{_u}V_a3rgg{+P${gr=ns+3nmp7N*3$9I`A)xCG=A&A zk)vJy%fy1XNE<$2gK24($*r7zv|jZX)Cs&uID;Ff>s4pn&mdgKDt8oUo#5NiSA)&e zJ4iE)n<|_?dQ#*Q@65>|bKEX#^E_AO@K|ufg}Vxmu;OF$c;lKXEaaj*j#yz`L)}N4 z7`o+@_lsZgv4de;{vM}N<&38%r!Vzbcm11k4Keo+>iUiF?hz3GnEb7mTyS3bsTfEg z{lk+$yF=lE(k<$qGn=dX;d3Di>#8R3#qeA{5c+~3qq1%VjOdZv{)bd5jroreFdBBbJ#1)lyIhM5VZs&!Pcn5PR2S# z=^0_9q~0cs$>}}R&gvTxD)MaWj`V7B0z1~8qhjtKm}`Y~#bXcn!m-JZ7H@n7E8l%j zuSN6NIX__j?Xk_ZA`0VxOyNX<7f$G+m_p4e*zNKonge<-rut`Usij{fL)mOusi|$U zG_o_^vj(A89K0u3WqcXp5zrI^AV?;CtmPSO5tiQ?Io$v79p?$~+?+i;NYf5nDND9A+Xjmwo|s55SQS$L9~oncx`VWnLO|nBSK6IuerhlQz zwuQ>taA1U{x7}WC)8#rZke-dv7{a2#t2m)1`e*N@kb5${9SJvk21PuQAlo!osvVYo z*AA*9nWA8WYM6BTBaiE#Wsp*ug2Ni;mUP#+IfgQB%!hX-a;LhvHF~Uiw$=FPa8M+Q zbNf%N{comPbCObF8bT2$?fkH+i>L&@2A|M|ni2YeC028z<6$xMKt<;E(nAaKQ|x;N zC(5?n?3KK3q!h)jC#br?MSQ5~ROH_ujB;*1$-pNF2n=Ef z2(thDLBRw6dm~q?i{N9R?fIT)<*Qs=K4PwazZ%VvU@pCaFOWbq6^$`8cv-V*)=9!(~wffqAT0h85(jmhvt3`g!XYq7_pu(SpG zuFo4gz9bs{%})Pe%lop^TI8cg`F#@A=oJtIti85@I0G|4O1So9HM3OjX)lBAVSCYo zNc!rGzKXlPl|}C$?p8lKLiJ$;h3}y3K7d;xwj+16he&AiL^Os-U>abIdB9_^y`TH# zUS%N|z%vlSK_Z${z_JJto+}*4ZW3T+L?1i2$?x40Lis=+@)hM>3k9gH=m>P)CjkH- zrC&k8K<=vx2<|=O02Ls95dJH}J5x|O_z!h2Mn7;@BsJ_0{iHX_YkJdxzuluV*J~nv zZ+(RJ4=@zh^dfdJ9r~Aijm&+v5&I~Xpsfz4n0#e6%-Bk+Wn>UEAW9~lP78vslB;y~ zo1df|t7RsgDAXTT3*RqV<8tcwsXu_45jEVD7L)kuEBJ1qbUd)Eq-P496DbYJ-}BPO zXUZH{e_^Y0XEjZv=quW?TQ;N5JIKV6)dCoj75Gnk5ClN3>>=6re8pbedzbQtGSq7K zGS2*5XXa)F(uorON)mI(=YL`){fdAVXTtXR z?E>gtZZ#A~Wd{?Dh9T=cl@_C|pv$1#asILv1iP+hRKnFAZ)$A5PGi!~sPoXGhR()w z1HEsJtC>BKv>V0f6kr-PbMwil)~(80oiUwtVp(1yoW=XY642$zO00%CSjbM9Hw3~O zN{JssnFCFubzZ++sSh(;EyKsbeW~AV%|fD3h|W2=o>_m1xEg zS9JqIRzw!}X(6J|KG z9-ip9vJlnYdhKBhdc%p#m2DlLL6OW&Dmg0wd4-HxE=9wreebMg&URh&AI%XfWxo<% zTTsB>FK5HKq1$D>O=WW_LG?CzSi#~CA<- zK36RlA;PKAM?0TEf|`sPMp={ELiS6~jYefrI5~=W(mM~EG%)G7oz1DPkV-D58=U=? z>)PhLkx#h7)KFO|W~(XoErM-q##xTUbMp#Qy`e0QL5)aN+Vq_D}m#bjQA)?xQHbUF?>&b> zuiSSvN~gMti(Eo02wSosQnU^i4_LYr-&X zlj%ECr}SkjnA@NUOeSbPL2Np;qvFuYi~>C?<15|-ngY6(2gpwBR7V7+ou@-#=Z&~y zTY=GwE0CR+Y?}`Y2%9L2=FKk9Kk2whbTRSKtBU(Eo~D|o-O}0bFtL?!)y-4o=6d9Q z7EjP$WN{eyMfL53F13MF0~4>;#Cp(@U?a5=Dk7)h(39O}LY9vzi0nbvO%Il_(^ztc zo<&!Fb{9w`PplGJJ58Y0Y|0hqQouVl$XSONKyQmDFJ-CVayp#XYeVVBx|wep9f3+D zvQ4n!gOP{IyZ6JFhNun1$$o%*lY%g3Dz~Z_9-BdMR0b9$Y6rtlQ4^6&(&yc~I1iGo zS2$+!`m^OQ(Z#hke@*Su;D1+v+}2_`&#Q9~ECl**ts zd5);~Z&Y$GY?ngLCZ{N{FS|F49GF0g>0B3-AW>=bKBO%sbO|~TDgQ#DKcRzT5vLtZ zWi;OezJA%rP0L9~x_OMzPuKp!DXOE&(q^0^(}FqzqPTc*_~}(nO*F_?Tt8Q13Buex zQUspuM`!1e-_IhP9V}qyyG&Z-F{fq3c!dvJ4C3rxKB7k_S`SX75X@T8(5SbVQYx%t zCeZ}=>{c)@#SZrel(*pUOSWPr);$ex1I((16?Lz_*$JZrUmPO^*zQjI829Sb6a_x0)g36Wod$piD+WsTlnct7G#;>kCev7^LwzYL1n5)bF?A1y8or;AjG?4Vs zK2_1BkfMEqdD_ww5ie=v5MCpL{TrJNy8)DLx%r z&#XmHhq&O>tyfXJP99TItlVcYe}t>+7)ER@@>LM71QqZ1`tB|JYxf2mld0LT>F-6% zeyR4r9(H^slfuHPIK=E@zN~FH{!t|KOAR})zUFHy*C<1tU_SpC{;DonK{@?!$0AMw zqR!8h>aWX7Iuqh|o*UgBjVYgi;jd%BrR`F;(n*&~{V|a&Ipx($01mxGRR|IcbIlmP z1euEoX;?Gwm@nW97Ig!xY>C_-Pyn#uTqwTanQ~9CqF3(rCSY#@6-gNCFn3U#kmN{T zBmjJ^yR}JP>$vm{rzJz0(;RC|E5l}}IEU*P@5--R^aH<9j{#jsy{Za$t3Y>SgXPRv z;RB~xVJzrmmnWs^K859zwNclqytTpP!@*T!= zH3q9AcVI0dzC(PYg^8upVyP@yF}vlvreE4JcV%YNtUSF)J>trpjeRiIK)>b>1L-Z~ z8qrLt3(X&N`hx3e{5>B)rBO4QH1qTo$6pUv9(}qulWyoho-`6k#*}Rg?;d5l!v%IGJJVBekDVFlZ#etwfuSd$ z3Xf;KI`WL6Yo!llE#z5~U!+((O6HoJhjXT$fO`RrQ`??n9(ZzA(6UZEYcxWBQe2mmB|vYmQa4ZmP(5j#WEsOVNR2R9-EI9hUJfdBpie1 z;2+S%rpd?wDNNCI6O~^fUyj}IhT^bEK2pCtST6P|u6xV85Zl)8 z)-;%p$lE5`W&eJBp#O@P$Pul71x@DB$#CHR5BXT2W|`4%q@Q`xK?n>|wQyh-ru% z;F9*X++b7s7>P`1b*d!UX&Go%wd01Fbqya{(PjIF+=k43+@Q(3Ih*hJ+8HXc@ziXN z?`_1~T50UeYrJxQc4aE%p)?{r{=}HaQ1NI1sp-uFY*#S1Zn>BO_oAIU6xI=X2_eY; zyfm!YTG`#=SQX-p_YZkEYADZy-yE_2Znfy|O9G+61G@;}+V$V1Fck0m*{EBUU+@`*D>9RUFH^nE zxL%5K-x@%Mu5rs-V|pakt$o3FZ@3HwBWJ==Koc%L;QT5UV*_fw+?+qy~5L?@(IK~C3%Bpg^*dCPoO`VD;`j<(SQx=cYuEzJ3Kx9<4tk#9;6m~nFNpj+xdr`sp_liiuQ<%+_icThV{&~Licp|OR9`4yfb0$o7fGOyYqHYE!+r8=2#3HT za~SrGY&Pzj2)9k!Ff74qEn!^Ss%G4@ji+fZlCY9MetCHQZu}9bn92F~ctoQFG_oEwBkwH;L_&wCv)vIBgz2qdfj0G8Nawv#o%MPpxBlw(p1krpHS7RR z`$Yz*{t)EqY)fb@e5dgyY7_+b{ntJi^k)LUc@;Md3x&@Cb6@Lk)++)X0)qU%_rc6) zKpo!zOmD1@_ogvM5agnY7>-T0o`XBf9(~x5m>8QQIw@HgbV=^{r);ujjFZMmo3tF|(LT4oR>XL!ZRy=E4jC5@IbMLd>Z`&`u4=;+d zZ^wm^kTruMN2XAWPRX0y-w3j^F?kZ=fY>Eegh`(Vqr!^WElPad;-uRn!Q_|5(+n(o zN2QyD$48&=5V{qlc#LLea&KI4j0TFoTXv(@n zcXtv#>@z7mYUTCT5~_Ch5VCcLW-p*!9{lp2^ugI?GXGX9vn#aOtv&c6<^zN$0mAQv zk_E^}VF*tXkeJ%iPzGp>@^7*%A&5}#9iS`8J%)W5`Mj)Ss-wD$I}hSHji7EQIB4*b zh(FN^J0^gc%%mZUDNY!DPBvIR}ooqwwyh7X`mXLGVvE#bf9EqQCS;r zN6ckX>nGa>mD;=VL*#o=qk6#S^< z6W3B0EXNXzVuRUm1%)WC)|epi%nijOwwYyzXtmI-1|v^QYL}W2eg{IQVTya`>+zUn z)tUgTF$Ke#F@I9q>kL@?^g`upf?27t0ur+4Zq{+Yk}$@D=~w|U#;IT~7~?TMn4Nwe zD#4;%eIJd1b~d^_0mRPcb_sdL)N7E$ce5!mselG7fY7H6hI>^V06l_2 zL=IRa3;-En6dxYhlAO32lVz6Zyjq6Ws4w2e@mRDFXm zGReM}&?fI0F%D$29} zHP4JZ&oif!F0S4zU-Np0X^d4mnt$TtO0vGQTj}#cLufwTf}v1Z9w>nG~1 zV2ueg9Vu7TpDJ_A`fhu{7wOO~lbh|OL(9$8{WoeF-oHm0M*Bdw^PqFv#3(lv5LM^z z)f}5)Ele!-tg%;JHL){?B~g?V@k1lsE5$B*$K!hrBu@imygQpofyWcGCQ*-H@(1yx z|Kd#8Pd{LrJlQTL_?P+MbnN=rC%{Fw+mM1$@~ra9t4I z!&xVy1ImDP3ZY*8&n7~a*ScZPXT%b^us5?}mn71iJnHNj#+^Y~$k+)>-_x}M@eH_Q z?(Xn35{fdhp;`P0VyRtxt%sno6UikEmn)Za#NM#*!lJ+0=F_xX3(LG?fM2+mHbsIh z4X1$8Y=YGYQ{@UaSCMbJs%8LfD_Mqm@{m#FI_e_is-78poq$y!?A#UE`9q1}MtZXk zfI)9_>lm>GdN7!yL&*d)+t;I~;MlT)N~feGA|));Lt!qfrpUzw&>BedE|8f@I9|XU z>bD{-vhFbMl;UegpuF3b_9f{AKKho?Vh@^vU4nG*2LnM4H zEd&#WdK_UPsLe0cH0X!VX2)^+DJl0fa3Ygq?DPtwi)*5{hXd*^00D7iI`f*k?f3 z*wu(njYNj~q+YSm_sL~Wrp3~mi9-8?ej^mCG_%FVg29kinD?>3{h*E@eM1G35QXP- zQ=WUY5M?!`yJRnsiMlZ(d>GlqueV8#kW!x5FI@Ysw@Y>XQ61@S_99orI1jrJy5~bn zMd&R3qRDQ=D0PPrwosTw5BE+K$`!!B@%bmfy)3-!$yZpUqa7J9KC!`F7{)ZTR5X9s z+DIzSHzc_Ccz9J&3T_buevQV|Mdr&=B627E5I5e?yK*_J`u)!q%B)lo>tyLhW2WsS z5qp*VfX>fj)5 zV`*;x-_iNhlr7~Y72MJMW={qNqFo8eUg*pwl#&B+j3Qi$=mqFoGb@B`qDfQCu7sA{ zXA<9`aBB2;Y9qfr63c)&+qKb*V9PcC*^Rv82Vv(q+mF|`E2MrzVmz5*$|13c!6IZ- zi>{Jl#xYAMyqXgope3uF@Q(Y)l$0SWvLn&;!=@Yl3ep%>;_0BU_huPOnLIiXQeR6(?-dlLs{{utZJyF`F3`@R`*ClesEZAEnPqlDY;}SVS1R z7fby*m$Rzak^8=49GrF#{d4BI4!m=1sNHF|x>@VCljIu!RISg?TnR06R3B_G;@vS7 zSzb~moI}WGpY{~>T-U}ATdZ{$w71ey4?WMTKO%C4|h;X1fykFoJNyujJ_)Xbo zz|6sjU5A`rGd$)-&_E7(76{RmIErVZ8N&Sxn=2w3YVBCrtCz`ctAVe$gWcrt62v4M z6`kE-X$JojsE{$9#mZ`9hOW-Pf_qedGCqv!GzI=X4-xbG}5`%Gc?a0-${Tdx5A`@3y^MQbR*gn;zv=n^q_bYw^bG$>79N|uRn#;X~E;^ z7EwMtcx{QLkpBNi+z#1et&!=CR)jC#{i#vvuQNf&ebg5QdgB-7%dD2h5 z)N|MBd~<0(`4*>Bt+pZf$H!iLdIv4pd-|1+uf^~L2Y_R-B_CP&%7-JuM&um7$RE|n zYQXBmEH_uOi!5_Taz=Z9Q}C0C<*A6;FSf#7Bb)TLTJr8O4f+&>b^+a5QY&=bMtgcB z`M(eN@m6=ssk&9O>R(Phg%$Ufu!O~ld7e%!R$f~|co+=+lxq$K!tgxmq^C>S9?@+c zmV0j2xB$oJtgo?c2ftROCPn3QU(=FEmnO<`%*`(?~Se3Ol9tDni?7 zKRSqT#TsTm(r}m(E?HJuR4gW5gBWB+I$R`*E!O(R%#5@ zJ1w@>CpDL?YmB z!+|#vAAGs(3-qQyr{ae{KaO==8Vty}2k6Uf&RGX>^qE-JKJmaFE{4*iizD5{wJj#3N z@Pfbia)x5aaaUT{F~PZ`8mjj_Qk+0s5dkR9A>McrQrWg7-l*0X-BBd$o@e`8^{A0FPfY!tF}}#lf%(Y{n->BAA337N`XFrE~5JR6UU5j zQ7X-yet0g{ny>A+4AOFOvz=ov*$?tR4OA{g?c+@ygFE5+th)K|L)~})WyX^k%POGy zZAaD}H}$8zdh|SpmQ`y>G<0*v>kgxQRxvC8Q#q5*Ukvc=77xm595Bm|%N{D?+9(yk z%dPNMcvfI1B~EU{AI;p%qAiY2kq=zz=98mkZO{r7FS4z}dQ=H@Y^~2s46WEm)`&pm zy(!GDY};Y2EqJar>nvwQMp&KPO=;k-cYJ{mDuhMZ%xHv{V@q<=O5%DRF{ZZAEfg}S zNz}$Cb72ELtfrd%c3qZ4Nt3b9J;kLxR9I{S!bmvx*!~NEaF#!+9C+W;bX>2_b3)!@ zh*Vv}TG1N=;Zbewti+J?c_$La(4~5uB!?h+Y9;G=?qKalaoQjeG(%@iCN+Rt6uXe8 zyYW4;Sbm7vKf*3jfLY#;UXSz_@%&u}sUym2#81N68lVy$uATR($xx+y;+ZsfS+ zEH=DDvllZ_+_u0b3vr3q z1BF9VWF1*>M|r{_KxKpC6^OBOh}Csmt7kS$K=n=SgO5GJ65LWhE|~RE9LA zxHF%nkP>rMt%y?hxgN%W-3b{kYTZW&^~vUYt%cTCS51#8#X12s6WrB~T64@dmgz8K zabeR@_}?tJ%%9n+W0&9Y874MNldAg55i;fG7TxLJQs2uKDQ+v|`pQKrZh3_Y7hyaK z<#q}k={;4-<H-*c%C4Py4Sxwd zDp?R8BTDRj*VrBsQGIgimHy@LThIAW86fgU?FrHkWVz|<{P=hwnbFfN|9T&ibpz-zFcg(LczapPVmtrXF8I6{ZO|w>n zP8tw%NKE@LtezVuMSkU1zTzrO&YYE=AS~-=3gOy&=;1s30Pg;bKzLeswIOo3kil43 z51m=p66(J zlwL2r#!dF^TC2j|96t>C_YCiG#ssB2DN~iB5Rc0BqzKsYA2D;N`#py*a81Jo$ z7)<;?ny++*P!4pbjKCk`a-JnjH5T&;o|>ZX8|>410%{IC!XK+8(CxZtY`D{ZL;xA$ zzS7Lt_oT?B`_cE!eplg*LZE8cmPxu}UeoxhK0X@gyIcm=r~kUJ zJqyqTcPpSVqmjD68vmqM)GCFD9hXOSvMS19Axg6hf zk{!Bw{aLveknL@H0Kl4@syTr0$9E-B$ZZyEpx+Z!@i$BSOAU+rWGBbw&-Sf-8g$sWa_9j%-(UCzgV5~Z9H|c!VW3q3xUO?GQLEc5R^#7{vXX|M}^HoQZ7qb9#UGy81z8-?!LA0$_%eq&x(EXY)|H|>weX(z)&xD2Uu z8{ug2{@PN<2baC_6DBob^=kin<%B~UE0cfp%we^+ho~>``4&d?YOmFe{2{Y3 zg;0*x=(8=`Rq$`emRZ0VQYA@q{2S95E%0j>cRpF`6GDO+(VKUU05QM*AOZ2Ybz=)K zcQ8;Qu^&93wxMYoO-m199v+e8I*Y?9w2-u7ZFRlTi2Af}w!b_l zc14C)-#?J%W^HP$xvFb>b>zdC!|EA*vz;m?FiBBDjPq%0+CFue)oD&~fHl(e5!fZU zJ-8suZULRA?~J5N+ol@Nb4EImc2;kBU%H|~+MS;&c2!!*k5^=i0&(st-5WfNEnZ;X zi5)MgdK}?sDUHc%(4+Gt#GHV+$Kg8fK3CFWM}`4|qD0Ja$dM4=9oPNy#m}qchA8r! zr^cGz*O17HZmS?F5l?7;2}cI#6)OHoCuvmf8F56r(t;>@%200F6GcP=FzW zL`bXJGbeub&dShGz#KI>6Za%B-Ea96z)8I^Ps?$5UU)M2@OJzC9%5@uF2|BiRl+zS zq$edug*g%A&(G)$Z)bew{xu#5ljnYTJ@~tQNm2{QW*G7n*M_C^PthCk_ADG6&$DcJ zZi?Zm-f{&q-DyPqLzY6&0bd^%5KRP}@P}9Tg=YHvyaB;uLRZ5+Gl>*qE3Lb3_dl zXI7c$^=Vqp)Wz1K8*@?hDZb2M;nQv4Gi1l3E%zImmYb;~*+mJ7X!FAS4SyH028J#2 zRuB!#R@AanO*eu)SjhQo=-6yJF%!v6>ax6lk{Mr9`-g0CwW0f#c;vizFS~M`z!@yQ zIy%^6KBM!};NfoT4-f}Vu+D&%&&&H^V}yva4p}du{;b3#b3f~B>JFwG&bjPVyi#Cy z=5FTs=xdfr8qxS=LG&eo?Uyfj>^-3g)hM*=oRwbLiQe8KBr5#0#?$*v(@k*^MUG*s zikul)knv~+KGgB$Oq}6^tQuhn<=7cR1t3}_`|%RR6o_Rleqii+1(EqNWKg=k!D|N6 zJQJ%LcWnWm2g8<>uqwaf3X%;^T-bbn)yC;3Tx(X|Em?2TJVNk#D3%i#eo6VnDZ}%# zR}Y-B(QWLB(K-^(7Mw8E;VEpUcA-1wr25I%aAK42`_J(&Arbqcg;xPl)C?N$bSUS) zK%agqnAH#v_y8rqVjY9(hHgRB9E1Xb)-f-p^cC({KhMi6Un;>y)0kwbn?aTPz3O#P z8p)FVS^aJzivH*lrGZfvX3sro$Y!?_tckux z70r$aORx?t;L(+(ui$Y&x}rxAaTug>$VM0ISy?1&Jy6dotuvC1Mv6e8P8?I?WVb?` z6T#}tGEKT5)G-aGp%hwPasorcNM}=)V{(%U-JZjHfwA93%W>9WM6IEsY&JfakIOSJ zIg8)9p9wMD_p-P%WZ!rG`LV~g0!#0)4?u8P02y_&7u5h^=D<#w7yj-OQB#hJUZrvH={xrLh17RaF{e+d2OSbYY z3*9AgW~5b8Wz%#UK-fk4Iw)J#sZsK%vv(awe(pV;dD*sN{kdnkx@9tGxecHn`$29& z*p{jn+$?5iGyA>F+bHktL+9RK)&y)RRfM77f%&KoECV-gQ5kMm$isya5rE0HTS_4q z7*bum1uWV2mj<<*+*Gedp=(wti9K>RPYN2k$`0O&`K3q844a((t<*e-D-JEMSD5#_ z(&KY=2-sV_B9RF7U3-Cvp7z-5-!X1V=OrTyon5hMKYU5buKBfR)gFb*0eNr`Y0Dmq zKv^$6ql6aZ9qr2!OT(6;x>%(;&_k7y-kR)ka=+HVO0}uDGhD8k_K|?&%wFJI}R;O`cklo*lxj=`|yGhttzyB=IFvx&q{QEQL+ zvYvTr98=HFwaw4f72F6TD4YOCxSA~l;0sZ|=p!jDF#wsQj6K5&p{Nl1ssZ8K1|TXI z?uP*cg(38u0bs`<__+GSHs~I&3mdi@;pls69^4&LnzTN|Pd!5Bxh0lbwCSQtpt~NnV>oB6!3t! zL^-x8%cOqUyx86ZYV3%jXiD<=!Esq_i4i{#|IG6UIM&(kgSr_?Q}Ceq740^1jUMVp^dm&Yr!sa{j1bSW=ZK$fTb4Q| zKS)0U9nzV`F*U<(OA+eg#14fv@%*w^kJ}L>ntz807HYzg%Zm`-4)TEgMaiG~{;8L^hFJLn+MDIEebIka9DOIDrP13&`lWkA^rP(y zkZRk3Uj%RsC9~gVP?&VhhoX8SKD1>AsW& z>5$Q@Z-H~l=j0rc_@!4w;}TCnhkR~CqtJCv;;!K5s#rOd{^c1@WBJe+`I_t6K<|g| z5Jzj{O0`1Ag_=oC+1;xyv@bTus0F0eoY8PrIj>K)@`ppS-nwbyF=kX)R%Lx{)QEz;*8^w@&F3GGU*io054f9jY`f#8{WX7e7SH`qmK}`LF^-F=I+e zm0h_FJVcOYK#B4SnXuKY9IOkSU*WaPS1+sDb!cvTMz6*V)5eDrZ2#441A{aL9i!?J zcOyp{N@qQW`dX|F;D~GVWx`96t-x`T*FDDHN@0w*i zYP{jfBLwQiZ6>xhBo>Xg6`%9Xugh-Xq1=8%)cpaaQ4{O!NH$o@E40Gn!dpe88|K3Z z_Y;Dstv!p6^ZjUEiKh>UW&^n|U;lqC(3Ru7Al3<7!hbc){%xWCpQ9w00t%Ewf%Ugf z8Xpw1iU#t9MMM67%6RyHlz&^pKx`8@g#T(9`yZ>n=aOI-g#R)8zddB2%1JcBe>y+@ z<_#47cAIhjYY^P0{|q7nWlf+F{;T5uUxqGd|1pFIl}%xTo+j`CE+qd;-QZ&X*Ns3r zllTA=(tqd;Jkq}uJ;0jguSfs_PYMGV=>I}Skiir^0H5<8quePH!hcm){Og|3T>lsW znNdNnQ)q<$H~aB7ko><#NpP0Xe+=P~|8Fh?v^S1T_^;UW|Bm^u2WI-^KcnD464R^z zam|0kcsb;MrcyqQ5BQ_~4<$T<0+Le11-(tv1739hLkR&iP5*)UT124w8G3-F)juM5 zMgm}B`yU7gQk&%ke0KwZt*JopbA+Io*-rohcaVw=!(WjeVBrqpoD%?m+(E8$h5%x( zzb8D9gFPh(Wu6`|=LcGdBm|MV;D8+dik1QYi03w_f3;|!rFneFk-vo}L?EOEZU9o) zUnK>|YJm-K|KCu_4QCH_N!7nK1y z$so}sTfj@^Kg`^cB;Yv*B$`DB68Z53@R1J+{$UP4E&hi=T^0Z!m;QxZ|6C|(86N;& z@mFL4Z7%Zz9;*Jif^xxUP|y+@$Y2E@AYc0rmAxVZ2ygfc$w6>GSphqPAhLdPkp5qI zKKU0i|D7uuXzC|E0Bsg@{L>0>I0sT*wFI;;fX+wB{_7c{QT^*JA}oT0$7rxsw{>jWwr$(CHL*R>GqL%^nPg(yp4hf0w(Z=x^S!sedb_%6ueJ8>bGpu- zK4gE=!rLT>yjqw?mVPQf5 zX)Y2R70ivs6xp<-Rof`nMFPqQYA>;lG)fwyWH~oFAb*AJ`vKkkSfp%N;Sbwby|%dg z8T}b8Wb>3UDuNbN!LXFU{&v3pbm9NFe`WPs7}6O|m?mO3Cj`~mVeu`7=D4pj1`^V$j%II2Y2Z38#sJz8&P(2` zjWTte&|ACL*V{O3EAU(0Bt1_^5W*A+ua!<1e=mw01vYM>Y=_8Pb&ToFs;x~1|J`f7 zY?AfR)Y)PFCC+XaQ}TvpL0`heiV~}#`+d+TVE&1)%ivJyHOQd@GtJ1-y??B|eb3eE zC#eCdewcY=(FEZ~P7aqxMfy~GoGIq8f23&%GcFbJ)9q|FndHj4REFq{xKW*a^7y5t zd6?4Iefg!zkuHJ4% zOHwMayunN-G{&guwqoPv`hi-n)Q(bIk2R!0(>1lJLMaEHS9PXZj@Gnd7bdQpCwv+A z(V-tbc+ES%uZIxVOEaBjv{qw!jg9Cb9y&pRM-vv`rXh1U%GYk4`ll^4j*zn2FqA%d=A9qhSB`SEnJuTg#bv zyJ(g);;1KM6PMgd6ZT61aakbWse! z21a|sW*uz@$$fE=jeO5&BR;C1}M+mUOzX5{@4C9$5tvaygH|<>=JGuDttX|c*Xgv^;8wE%QhO4T>1AboCFT}l;{ey-3eF;)44K!L3pQ~_naGR!jO+UdE>`85q0kq!+6fX-<{wI+ zRUF_kRRle+a`^DLuklYo#4fOwLV_Ry21T5a46gpS^ii1xm(XZeo%^Iioi5Wt5~uh~ z1U)aVWJjooE7YsX?w<;1Z{TxnARr*3Ae_wtSv^P~AU_E~KuCekrdYtZMI=DB zF07xyux`k`~{KojTikl?ts%y3!_ooUc0Am2@y)KX$=NU+nx~Cirvojs!O=PSwZ>%=?E9*I$ zWGnu+#-uUsbN%b52g>x0Q_!=%pCl(hTha#Lv`ZZHEd34)1aRH>pk&=J2LMU|4?iMn zpl)iOTWsI?KglDkZhldH%Bz0rU)*y_zGMd0(EEQ%bADB1eyLA#Yuts|c9&&3(Plel ziZ#4SDwMGl&7l~hyxr)kzrV}!@vL@`9;DB_E-Gs{pjm#HFK%usV0V*^*l zL4zA})ioWHYdWJ7*TSzKN(R)@+9B#%jlGhDSp?JKE4E2q;O9}*k0$FYwoN8a7TdEP zc&ayN&gF8gSjrTTDuPweCpvFTwPwrl(u$T&D;nkSCOlGQhhXD3brsT=;-B+w&HI)g zZOr6-T5CHYueMLGV_!74W~W<6`#3VN)+wvZXDAd3@b4h5-ZYxaH2`v(Ykoh;eC1i+ z8yu-Rk|k8j9oUI_3~%rBhrdosb|?{-L*U844FJ*6kq)ZPl-ki9(5nTpyw;f79`76X znmx{BqgZ(^>q-b-)4E896$g`GML!y|emZAsl=G+F{tQ_wDcTT%2Bx9i6bdf2{K)2q zzKo+Z+X@hs?nlF8-~#xwep^rISLMG@7!(jM9><^tHP9cL^ui zr-q$(!w%cwpI?p1MpCXL4e!RKnyi?c%W)RV)6zFsOvrw(lK?1bIh^QG_2i8gOf_ci z@4j|UREHe3!tyH}%sKk?R&N?;WhwDq2EtOOl_9*#`1l!oQy9!ZIt9uoKk&;v;jJk- zecx0v>&voWxZ_>QP@pHBI5OWS18hwqX}`2atyR;aj<3n^6v%1Psbnbl25CaN`OI&* zuNBM_`bN!TvI3Zlb<;28CY15!%w#G^9m4FnEy79p%bdoDyr4GIP4>Wyo%D~D`6w($ z2$L0md99SK9QS!U(&JYTN|p9NO2eCn8SpmIv*u6~$E?s=JynZGsv3f}a3_yex`L<) z?|83DUcwG%Da@tWML!!@2`Je(tn%LK$5~F@;jQNB!vU1L$dB4&Bn@XT&pnV=9R-S8 zwXj?;(P*bzOCnfv$;YQo^D*(*IvyYj>g8)=Bn30$)^pf(t_P|Pz}0M<9}UFFGkGT! znJEqR(CJo{tSU?-#a9V~qPX@chA{NBt)O{z47h|fb0L$;7=CC`st*o;U(x^ta1@I- zRi#sK+yMN)R;p}?;nQwPZHXGT$-edWe}}hOG#H?S{}Vra+$}qu<(REylE=ZluO#oe zM;^39xovZ|>lW^65l`x+Td%#wxJvD%?;3yJa?RA)->1B1#n7gGNiK45Rw#~L$F60d z$k1;#L6f8QMy#S3PMPgG(-(ei3eRjB$D|U~Vh#AE?<#|&?dc7s~3ETI=NS=1CQD|*ip_V$X z@qw(zMp1(BJ({xLbuEeARSQJ^G7VIoNX4`^3Vk}sExlo1ba6#)8g&t0a}o#t@=RyM zL<_L3Ju9!v#)KY3UxIZ1iT0JA8C3ui63ojfWuY;zpm6HaaIsgcLQK?yKR1HbFfaM33q#Nq$8bvySvYeD$8}$(k9OtkH?sG2xX+zghZ5eiGb=J&=5eRS4Uf7J^gmqRt)Gg zq+%%>DN5&Vlh`&dlOa2iR6992q427gogLZK$It4K>}zUKKgAQT!%#%UdEKX9KEKjA?K7|y!r^p!l7s+u{Z4OE_;-i2?zhcdHxm@*s|-#6WHz>mt?0st61M_1nC zcv!|9{fGxn2Da6yhg4DEb)LOBl-R8(Ri|D=a(AA5SEW_oE_n~G7MdCxDY`476&SlO zzgKG@XwXNH&X>Lu#%QGYEmisghsu|veE8Gk=DCfzF z0uR28B-fCJSBx3nCQtv~a|49VYV<=$Ix-t=@Y-~!9;^?Ps=J!<<+f>7t7jEo?N*6j z+)|_bp*7-@M2&>~c6JN-)L=fGJoPE>IAIQkckiH`malPZBll`8kfF9rHAKP3cS2Li zx+0vZ@O{;YSd?YCL9_BmI-c7oyy~QWAUum^WRkF=}y-)wP+kPmmN6DL2|B_Adt6b)wdHwc_CIvg! zEC~R!p=~*tA!!%orF-9~bC-R1Jgl>8b_*u{yCsHrI@!gcZ8*YJXE>%Lz*SdsO6&p2 z!GKR1ZseDLF}FJtCOsg<|86>|$9pcjz6+8n`9=d5-PK?v%R=EJXf{nDoSExgs<%OY(kwqrbR9G0E7Ffc?M~ zZ#@LpoMp1B)tS;Y#6aGS>@+WYrfDOZ?<=PfdP!@VqBl^$iwd~fk9j3^Hs52Q!^^79 ztFJr2^NTh8!}*M#RYTeXYi@KYg@hO-HQCTjkS~+7p%Voluiog+F||b|U|kkD*AuXsJl6#wib3ua027 z$)3K0iTdp#QyY*9d7E5lymv{C_zUX%?LAL=eluBUH4AzgMvfABwaC!Qw- zDSEU95iiuAUW>0q3r}>%C)2!LjloxJg#7qitqDUe@C3|zELhc63bKUHToa@st6xXy zR-VH`v*|2e+S$XsS=MDT8P7Y0_~$vVjF>pAr1iFYegW#C{Ko9L7p?m*O%`)b%LO@2 z0V@+Gd)JrcQAeyEge?{*-{I(m!xZ!M*;^fuvckpnEnVKmD{Qs24C|g2D$AGtoN6x8 z*Lswn3Qp&h-Jq8uIE?4sBvbMEmdnC!h{*V7YC+XhmcLMBf?306rO;QfSqJPKc06RJ zBIxyh;saRvKM~gS9CH(sFPOKRAKP#5!ZMMUyWaDa+NbwC+Rr`wGyx5y{><}mE8{Qz z`>o-Zf2JYY(iYxkV!&4-k*3`11tXXUq=@5YcBEMcW^v-`UgOxa+cUNV5#*V3NQUQm zB9Zfni7AhUS$}A|MAa+r!Se(&?=W=7Kwo42EC67Y+<44w_2{AskOce$(yf@8N|f}( zt7YkR26^pC<1A!*W5u((Aj)<3wNa-tA=fVfVgQ=SuUzjuzM^A(5W<1KBse`fW1ecY z#qEsxm1nhn$;J4|)uqYPKGxG}k}i6qU5OW!HcnMvM@N=e1C6PlDoWc&W9<+sxoi7- z*a1*EoYw*1)41MSBEJLCQHT#VEMl1kDKpRTk6UFG!J~0uRk>{xM-ea#5&X8P;Hv{> z6+Ve^S2hX-zdbS15vYH(CRWVt-RINQD7vk%Zlw1rnYuxLdEQ(peO?^?${hc1X`~iqnY*<;Jzs2)o4qMBjp%3;~?w^zO;|8|! zx=#~4B2Vvb&G_RISW{qlU1y0>SGW=5GlObbbH1W!#ha z0ZFhLkBwu(2kW(S#KF~VXzn?PUuqeng%Pu&K-GQKphD{chv$c{)_xwJ!_da{^VzeIlP3s8DQ(B=w#W#f?z+tQu^ zq|iezjP=f?nEp!Mb9|aKwdQe`16|QKDvqLx-lhm%Q>3ycGE@X$El|jxsAA2VGf*7VGyv{<@Lb=)##@p$T3Bs~i|`+lUge*^NjWD8P0bOR zFVyTxKEA@D5t}QUKJGyp3s--P(Zd`72!7?pjrA**w#we5@Nw(HEo;b0JKY-GV9HQf z)1_IkWbqf~9LhktNn59fFGSARGz(60JHsbB8ZsGs4-k|(O>Zm6a~W5&bpWP}7%e8~ z{MEYCK>d>1f5(5j$1uIj$X8fZoe2n^`etNWdgI}ruMd%=jKx-jcdN)@=l{n0f_CWY z6ObsTVYWrw{tM4DoM>h(M|~}f$YT8xe)V(@Ikr@pghS8i6omcDf7X;(`16=$o`R16 zrok!%eAcvqmd}9L+S0sHqQ=nNz8kJV^IG8H9b};SYuOWktyw_edEE9ZYfO@gD+!6 z^wTd%C9-FS24~`YOhjjqodC|2jARfWI(p|3xMDoVZhco>-=O$aUfJ$ zGfL6SWU7Vl%u+Elqbz-*qFxeJULFl_^TaZ9bb^n69UNKUS_^|2ri5Bjl6J*jz5GXh zX$0I@%_m`i5ZLM6)VU*9mV^C=>7P4afvY$F?mu3SO@QCmWIq(W?QrqMxum}Vfs=*y z3abRsrU3S03?0_ebS;x%l>X$OJg&*wH>j%}u0YPKh2Qi5-UoMPCVDhi`D z0UVX0JWx&cts#O{;D0}9fzNT&RdXz{$=Y%Zd_$LqW$Fx(Y8caHeo={5^@@WF@y%v% z^8dcp7~8vhAF@LXD8zx+CpBuX zP+C;j_I`0*{O+gU8jqt+A<9iN)KZ&M(Ohy0jN$MN#2Plyt46o$bsS$xHav2D7L{I@ zpddSE?vXzxWIUa>Lhl}gp`fT}FFKgEW_54;U|^)Vl$4kbm;IsrCVjhmi&vcpA^_x; zPu<Gf{}DZO_eSEMWz0pw1^D#V`C309 ze$VH=;YI|ceL4ZX8hy$b@-AKz;45|64pU^3=|L;D#p2k)kFZ|_gFSj&=&A2M7Ji;* zMhBCpuvO>z1{lHGJL$CIrT&yWA(9)(oKIr!3~m>Y7f}km6ZKy!RgQhxrE^$UxT%&1 zrfaq?n-HWc&p~H^HTY$%0gyZ!H*L^8u1M$)AJ0VNga@5E7-;j#-`0_w<|*|BcH#&E zS>Y<*@O571(+p?v3CusMwK!S0jL$K2kEINNi`;eBqQ{j0_yXNgUvr`hsmNv*9C~Z~ z?i3s9w7VJ)QJk>{n=+OGX4@Dqd)}C-F{wbp?C?%mv90ef32*e=faX227j8g-Z8KkI z^`#tknAEP?s1e&^Lcek>pPB5KhKbYXpW3rzY+=Q6UB%5uiHiWrBH99l(@@bpiUxN3 zH$%vtNi>n=0}zr|kF@kZqEZXp&74l}0$+4G%`yyL24JarXa;g~S_JkfNS^P1{%Cg7 z5?TLfzBf?pw(mHX2P8`}m1YDF!M24U1-v+h^-M-IH;+MMnf$KWxXXC(?QRU19$vb7 z!MkG?jrc9NB7dRJizkha@yJcJJS|4ylqsoRZ-DNST;7UDXF7xWZYD4a>1k6o@7i>uimEw8L9T zU?3P=M)}dG{c#_%w}Vzq1YA10&Z)Q7{|RPDX&|15rUjW*QS{>dEU*-Uf(*S>O<2*B z+3z9v$@J?g2OuNhN_2&p-pj=6^Q&iE#W&wWsk#K{oood=lT0{R;HJax`6|qu!YD1* znm6z~Lk!q3(B86!+n`d~%gK?+KA}*Af+@Obe(2@U$k}S_F^$zrlaL7C)C}}43?d(x z#Q%O4SmSMhM4P$Ef))QW5T(mZCg%D|cf~3^R`c`MGyp=kJ)1!hm?b?j&cMqnt0g3( zBqX7gL#b{=sl7!a{V6)>HAB5*@=GWDgDi4gg4q#UoJVHdhBXZI1_Wxbfrlh#IKdmT zf7gQm&B<)RY6q2}U{n8E)KWA(b!pEtE`OmT`V)FYxV~m$HpCk$cmtD%OlcPcDXB;| zahOm7A3&A_FoWrbnIDED$Txr>UznpIK98O2$I*8D@rpDDw~#8hYv?W3n|)mi2Bh008~(Y&4=qDFc8J0|dmK9t4EsKVN0&|5SYcHz}>LxF}5B&^da& z0!E5(76DNoP6!(jLLtKeE29&GvGeVa5;uc#s*@D9$(B*euBl3&QE$22x=2$6jU>u$ zQE#KXYE7}Cd8zzY^9R;PRPoo{)`Ue80@yA2QTJP}iJ4w+39CX>s&#*~K}ZCYDd()fW} zDn~<6273(BtwHEfn|F5~yv2|h_vF5MAs{gtK)>InvtmeQUeZn*pVt1&@ttY>P|oP` zkgnQuuS#kM(@`&?i^a2@gTAN?6V3`Il-6@Ii-Pz_j$L|Z($RLG5zfxh(ef8Z0CyD- zK(wi-`15QR>wB{t`|zX#f%DCGrY$;q=my>aQ>iUC-}1%mR{_acyOq7;9rgEU)Q% zbN1@3{feU1DaGnkp0u5YJ2f3Aei`di*dsws5uMoWC+OWWLd;1m(Ssb=wC{>kOBJWa+vAAxS0ofcT`3 zdsUcdoyb55>e00`OX8)gMfa_LSQ8MA?c&N<1+b$+N3p~?Ajt@fT+2^00$pUzIF*B-8-ZEGUBCWrk4VvGI2c|KYhKM2T7(`xv}Nq#`{l^4nOg< zp2#hxaWlB9AG$2Z(a?EY9APDx2!(3tqrUbIKGf*Y*V^#%&FT9MV$PAHfTjEN%V=qE zDedoqwJ;=F(0UK)r1bg&$8BYTw*40_;O-ubA*x|`KPPWeu>yUTh7PWq51Dj~**S{s z?QLCpI09g_$0s$-j-|x!9IBSr6o1nCmG%A6Iu;_S(&VP=|9tS_n3+qd9^g!b>EX0X z*cLw^3M%V#FVH??HRhOc1gy?oB1@1S(bz!_1s`~Ts)O!9y^3l3&JlM8A2Q*#uFnm^ z8HXLLGd!Z_=q?t&H4hCq-ob~l`6&c$H_DCFquf`##I#~@s3s6b4-^P(4!p8-H5fkO zw*Mh;fn;nI<#Vzuy_c`JJ|J1du|~9$5-3MryxGPSw+JgTZ&#g%1@PeJ7ccs7U_=Z; z^f~AEE|4gt_SpHA{}BtlG%m0UpvN0R08lsN1@L3QNG6CN0Ju*+OGMdhTW4fACPG#$q9GEJ%SM2Gu zK`X-HU3A2JfNr+io0l$02ZNBQTSppPxA@Cupy!a@h0Snm!3cYA3GUaQMGe%4nmzOXgZm*it-E>Mx%(KS7PF zZaMv``j$tBALzakoK#+<{lMpLWI9i9UPuS9JvxC=i&+SeQh(|-sKP!(RABAUuOvbp0 z>7}(Ot{3}ec?h0!HmY_M1IRKcm!p02(V}q?(vuGw6inoJ!wugsX4SZyzb_rE1`lHYWp}`)(kFlu7xC zt0r(kIxH?OuA4&1Xe907kEXR>u&+^6zUv)WJ?o|bXk`e}+TQzE1;wSBhBN}=0F)s} z@^|kbd1?n4W6al0BUkxifnU+1HsIq7fE42-8};taIko3+DS*kE()V(Rj?TP9(!8Mj zav6bR?rfYUnxEvlF+S^W6{=416nZ-;r8oGYfQnnYcM!Cj)7j|SpZfA6zo#%15PI}P-# zffwxz^$so{lYX*^eA#f)&aWsu0CqtFmYXHX372qD9y%~4A)A_Re}4bTjbVZ+y&m|A zqp8C49A);ND{B+}SqF(5|FUJS8)S1AX)x+n^cMS5)IO^uBiZ{y%EjF1wA_4Ho9Q={ z?L}+oxB)g_)4)qP+n(&G1bhHr>j^C(qZbJ7S}LYZ);vOJ%U23 zVJX{oHrIajJ$~rocJY^i0F^lR!Yq@qXj{}AKX|byBlzBUO#P~BJh=`Bvl?9ZK&xq> zjz|47ID95?Gyltqw#AAWhDG^YUn0v`UoPcBYY+l9oMkEa&w^sAc>v}rASK`38WjA6 z*mP9_pa(H24-X3NggR^`)HWVq{u+*^EjD+C_Pdn*%0Kldie=aakt|BNvQcSK1{&*@ zd)E%EwsHV6LZ{Z1S=+oU7Q^AqRjUEncjg1$(;K5pO0p^~65VW?;%qKTicoy8NQUS=5 zVq9;2j(WxDMd^GWMHS>;D3H(E+ASLjA!vN^gGsoBZ<{5&;`&v-hRVV*VFutSCF6YC z)o0e;9?wCjvq=Tus`@2BYko|$#9#q;Q2*d`rU7j%LkV72F~G2I9KrG=HPYH4dWoaJ zu*v1YJz=Bv_L-SV?H+GeX?T6K&*)|{yFG{Cy7;LOo{>gpd~$x0|2_lVrZo9uI=>(G z1%zvUc36rLo;-DM_z6eo?G0CO^?*#GB(OUF3N^#24?WANPc!v}%5Qb%&HokDCnW1* zp9*riXmFFG9zZl%8kQe!4Phjuy(0MNI9BF7Vy+O1{?RWuWrVk`vG3wTKsi_>n7ppI zM^w-W4RxangBvZ<2GN;1CqV~()Sw`wt=CcXY#^sS&$&G!8hxzSj-;`{5nml1;Gm-~ zAzYZ9U{AK+ndsP8X~Pj25W`Kq8MEkF*$HXq{NA*`1Aw178X76$-FpI-bf-~qU_Q+Z zK&^wl9jo5gR`ey>O}D2|rT7qRa@Yh4E(gf}p{67XXT%m$+FE>al;u_|`;n}k~gd0GtQ_Qp8L>^2RL_Il{r zR&A#>1}vDdFV+W16>LH@PZuRN;?Asqq1$q#WZF=@+Np_*GQFwomib`Sq^MQH}eENGKSt|%BAzR{_Vt3m^^P{ z28f(&@mDd!(yA_WJPmYxEYRk}q!xspA-5eVt|aF$%nMeBidd0Hrk3!7<-?$|mHSm( zo}WZSS5uo7^=G0z@eoX{fqQ>KRY5iiKkNKBeSKx0#=+jz=bTJ8)SP(|U1F-`ssz$k zt(KOp&JUJrL$u#yp)P`kXdoH)`cIp84glsi zuB=iJgUPoP=jNo`MWxQxy-Q;M#FSwtO+^YnN!{$M2WU!tFJSKKm1hk zsBz`e-)SKN#t@8u_xzc^kHIW%2s1CRzbA$|SCT|no0tEtILIsSd)(;bcwF>NaZ0+h zel)d#0BW)5D&?a%gEbINbk1)<| zFqdEHHUpj@uHXcBy04V(9gw4EyzCr}vle^^&uz8qcs@BsKkDd@6?|sz%jsF3zP)n3 zR)^~v7i%l<5G#Rhv#`*D-~sZklVOK%WDmk^mDR+mp=C7_)8)4V4`elotvuFFqu?pM%H-FN|WJg9lk zI~+RHiGG^bzftG_qJ}`t_CQ%whj^mJ#1K-XX08-!Fj5Ue68MaGMv?%(z|cA_!^sG| znHabP%Ms#Jeb(njDMu8kF*A-CG6bNn&q+J>oA5_X*Sq?uw!+F9-gGl958-CtP3_+W zg2v!$2cw&w-h!?|PG}c~C_+w15t5L4g}E1!V)%ks5DMEB5`DNsR$sNtO*?Vt`Uw4m zi**n)y(aoV#3Byud=&a1{n*!)JJhVX*l`km7rML z#`HZ6w&yEHuREevWN}Kq*}k(jK=+KJCEdDyyQz4_3Kk3F^(%xGgN6P;g3c@G8I{G6 z*O@nmZJhLmhuvl|(B`#$_i%}(P^!nU9%G0lX;FQxDK{V zcKSOmW5=nixe3@xXRZ!*+F$gr?!~|1< z{*Mj|1!3sLC=i!GBdS|8J7NwlGkM>0eOp-=P0WsQy>b4d;J? zpn+;DEMNw5|7gYv7Z{8paCXH43`6;^Ap`2JvVb{i{dKYdyH@GI0`!4_mdrr-RTLo2 z8Xnkpqra2@XtKrwwqOO!TvG<)um+y3X@dD%1I5<)!78nRfOSJKZaZL&8!qr^T?y>i z2^i={0EG6%{x?X}1|C>|%U_8eNWXvr-1$qlT!B0OH2=J~At(s{_tu4h6yJfWn;Kxq zK7S24aBNcotl9q`+=xH}wk)9lHMj7<%6 Date: Sat, 13 Feb 2021 10:10:37 +0100 Subject: [PATCH 124/521] Bump gradle-maven-publish-plugin from 0.13.0 to 0.14.1 (#7186) Bumps [gradle-maven-publish-plugin](https://github.com/vanniktech/gradle-maven-publish-plugin) from 0.13.0 to 0.14.1. - [Release notes](https://github.com/vanniktech/gradle-maven-publish-plugin/releases) - [Changelog](https://github.com/vanniktech/gradle-maven-publish-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/vanniktech/gradle-maven-publish-plugin/compare/0.13.0...0.14.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4a2b4949e2..0c33158410 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { ext.jfrogExtractorVersion = "4.20.0" ext.bndVersion = "5.2.0" ext.checkstyleVersion = "8.26" - ext.vanniktechPublishPlugin = "0.13.0" + ext.vanniktechPublishPlugin = "0.14.1" // -------------------------------------- From 25dbe1a398add5036b593f295e1b148a110af805 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 15 Feb 2021 09:27:39 +0100 Subject: [PATCH 125/521] Bump gradle-maven-publish-plugin from 0.14.1 to 0.14.2 (#7189) Bumps [gradle-maven-publish-plugin](https://github.com/vanniktech/gradle-maven-publish-plugin) from 0.14.1 to 0.14.2. - [Release notes](https://github.com/vanniktech/gradle-maven-publish-plugin/releases) - [Changelog](https://github.com/vanniktech/gradle-maven-publish-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/vanniktech/gradle-maven-publish-plugin/compare/0.14.1...0.14.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0c33158410..fd0cf75d09 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { ext.jfrogExtractorVersion = "4.20.0" ext.bndVersion = "5.2.0" ext.checkstyleVersion = "8.26" - ext.vanniktechPublishPlugin = "0.14.1" + ext.vanniktechPublishPlugin = "0.14.2" // -------------------------------------- From 73e4b87de44a764596258ec9b38a05e77136ae24 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 15 Feb 2021 09:42:16 +0100 Subject: [PATCH 126/521] Bump junit from 4.13.1 to 4.13.2 (#7188) Bumps [junit](https://github.com/junit-team/junit4) from 4.13.1 to 4.13.2. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.13.1...r4.13.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index fd0cf75d09..ca036a77bb 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { // --------------------------------------- ext.reactiveStreamsVersion = "1.0.3" - ext.junitVersion = "4.13.1" + ext.junitVersion = "4.13.2" ext.testNgVersion = "7.3.0" ext.mockitoVersion = "3.7.7" ext.jmhLibVersion = "1.21" From abecb9aa882dbeffa08e9d7b42040ff6d2fbdebb Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 16 Feb 2021 09:40:53 +0100 Subject: [PATCH 127/521] Bump jmh-gradle-plugin from 0.5.2 to 0.5.3 (#7182) Bumps jmh-gradle-plugin from 0.5.2 to 0.5.3. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ca036a77bb..6322ef9f05 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { ext.testNgVersion = "7.3.0" ext.mockitoVersion = "3.7.7" ext.jmhLibVersion = "1.21" - ext.jmhGradleVersion = "0.5.2" + ext.jmhGradleVersion = "0.5.3" ext.guavaVersion = "30.1-jre" ext.jacocoVersion = "0.8.4" ext.animalSnifferVersion = "1.5.2" From f1ed2ede94728c0b33a477948be7c506ce1fdbb6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Feb 2021 07:43:41 +0100 Subject: [PATCH 128/521] Bump gradle-animalsniffer-plugin from 1.5.2 to 1.5.3 (#7192) Bumps gradle-animalsniffer-plugin from 1.5.2 to 1.5.3. Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6322ef9f05..09b22acfcd 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { ext.jmhGradleVersion = "0.5.3" ext.guavaVersion = "30.1-jre" ext.jacocoVersion = "0.8.4" - ext.animalSnifferVersion = "1.5.2" + ext.animalSnifferVersion = "1.5.3" ext.licenseVersion = "0.15.0" ext.jfrogExtractorVersion = "4.20.0" ext.bndVersion = "5.2.0" From 6f7bbb69056a27aaa64cb0e4f80253a5d4064102 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 23 Feb 2021 08:51:41 +0100 Subject: [PATCH 129/521] Bump biz.aQute.bnd.gradle from 5.2.0 to 5.3.0 (#7194) Bumps [biz.aQute.bnd.gradle](https://github.com/bndtools/bnd) from 5.2.0 to 5.3.0. - [Release notes](https://github.com/bndtools/bnd/releases) - [Changelog](https://github.com/bndtools/bnd/blob/master/docs/ADDING_RELEASE_DOCS.md) - [Commits](https://github.com/bndtools/bnd/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 09b22acfcd..c50b569982 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { ext.animalSnifferVersion = "1.5.3" ext.licenseVersion = "0.15.0" ext.jfrogExtractorVersion = "4.20.0" - ext.bndVersion = "5.2.0" + ext.bndVersion = "5.3.0" ext.checkstyleVersion = "8.26" ext.vanniktechPublishPlugin = "0.14.2" From 09b2b1b4c9428616b2634a67bea0ee5a09201007 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 23 Feb 2021 17:15:09 +0100 Subject: [PATCH 130/521] Bump mockito-core from 3.7.7 to 3.8.0 (#7193) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.7.7 to 3.8.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.7.7...v3.8.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c50b569982..444d81b60a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext.reactiveStreamsVersion = "1.0.3" ext.junitVersion = "4.13.2" ext.testNgVersion = "7.3.0" - ext.mockitoVersion = "3.7.7" + ext.mockitoVersion = "3.8.0" ext.jmhLibVersion = "1.21" ext.jmhGradleVersion = "0.5.3" ext.guavaVersion = "30.1-jre" From 22c5e0bfe0ca9a68cd726d23fb696fe56a059a84 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 24 Feb 2021 17:40:46 +0100 Subject: [PATCH 131/521] 3.x: Add onSubscribe hook to ParallelFlowable operators (#7191) --- .../io/reactivex/rxjava3/core/Scheduler.java | 4 +- .../operators/parallel/ParallelCollect.java | 2 + .../operators/parallel/ParallelConcatMap.java | 3 + .../parallel/ParallelDoOnNextTry.java | 2 + .../operators/parallel/ParallelFilter.java | 2 + .../operators/parallel/ParallelFilterTry.java | 2 + .../operators/parallel/ParallelFlatMap.java | 3 + .../parallel/ParallelFlatMapIterable.java | 3 + .../operators/parallel/ParallelFromArray.java | 3 + .../parallel/ParallelFromPublisher.java | 3 + .../operators/parallel/ParallelMap.java | 2 + .../operators/parallel/ParallelMapTry.java | 2 + .../operators/parallel/ParallelPeek.java | 2 + .../operators/parallel/ParallelReduce.java | 2 + .../operators/parallel/ParallelRunOn.java | 4 +- .../rxjava3/plugins/RxJavaPlugins.java | 48 +++++++++++++++ .../rxjava3/plugins/RxJavaPluginsTest.java | 60 +++++++++++++++++++ 17 files changed, 144 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Scheduler.java b/src/main/java/io/reactivex/rxjava3/core/Scheduler.java index 3c6a6ad751..d4c6a39ce4 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Scheduler.java +++ b/src/main/java/io/reactivex/rxjava3/core/Scheduler.java @@ -91,7 +91,7 @@ public abstract class Scheduler { /** * Value representing whether to use {@link System#nanoTime()}, or default as clock for {@link #now(TimeUnit)} - * and {@link Scheduler.Worker#now(TimeUnit)} + * and {@link Scheduler.Worker#now(TimeUnit)}. *

* Associated system parameter: *

- * @param onSubscribe the consumer called when a {@link CompletableObserver} subscribes. - * @param onError the consumer called when this emits an {@code onError} event - * @param onComplete the runnable called just before when the current {@code Completable} completes normally - * @param onAfterTerminate the runnable called after this {@code Completable} completes normally - * @param onDispose the {@link Runnable} called when the downstream disposes the subscription + * @param onSubscribe the {@link Consumer} called when a {@link CompletableObserver} subscribes. + * @param onError the {@code Consumer} called when this emits an {@code onError} event + * @param onComplete the {@link Action} called just before when the current {@code Completable} completes normally + * @param onTerminate the {@code Action} called just before this {@code Completable} terminates + * @param onAfterTerminate the {@code Action} called after this {@code Completable} completes normally + * @param onDispose the {@code Action} called when the downstream disposes the subscription * @return the new {@code Completable} instance * @throws NullPointerException if {@code onSubscribe}, {@code onError}, {@code onComplete} * {@code onTerminate}, {@code onAfterTerminate} or {@code onDispose} is {@code null} diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 98a72b6302..5a6579fb34 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -9723,6 +9723,10 @@ public final Flowable doOnComplete(@NonNull Action onComplete) { *
{@code doOnEach} does not operate by default on a particular {@link Scheduler}.
* * + * @param onNext the {@link Consumer} to invoke when the current {@code Flowable} calls {@code onNext} + * @param onError the {@code Consumer} to invoke when the current {@code Flowable} calls {@code onError} + * @param onComplete the {@link Action} to invoke when the current {@code Flowable} calls {@code onComplete} + * @param onAfterTerminate the {@code Action} to invoke when the current {@code Flowable} calls {@code onAfterTerminate} * @return the new {@code Flowable} instance * @throws NullPointerException if {@code onNext}, {@code onError}, {@code onComplete} or {@code onAfterTerminate} is {@code null} * @see
ReactiveX operators documentation: Do diff --git a/src/main/java/io/reactivex/rxjava3/core/Notification.java b/src/main/java/io/reactivex/rxjava3/core/Notification.java index c376403314..7f5896209f 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Notification.java +++ b/src/main/java/io/reactivex/rxjava3/core/Notification.java @@ -26,7 +26,9 @@ public final class Notification { final Object value; - /** Not meant to be implemented externally. */ + /** Not meant to be implemented externally. + * @param value the value to carry around in the notification, not {@code null} + */ private Notification(@Nullable Object value) { this.value = value; } diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index 2b3288ee99..3e343bf248 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -8608,6 +8608,10 @@ public final Observable doOnComplete(@NonNull Action onComplete) { *
{@code doOnEach} does not operate by default on a particular {@link Scheduler}.
* * + * @param onNext the {@link Consumer} to invoke when the current {@code Observable} calls {@code onNext} + * @param onError the {@code Consumer} to invoke when the current {@code Observable} calls {@code onError} + * @param onComplete the {@link Action} to invoke when the current {@code Observable} calls {@code onComplete} + * @param onAfterTerminate the {@code Action} to invoke when the current {@code Observable} calls {@code onAfterTerminate} * @return the new {@code Observable} instance * @throws NullPointerException if {@code onNext}, {@code onError}, {@code onComplete} or {@code onAfterTerminate} is {@code null} * @see ReactiveX operators documentation: Do diff --git a/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java b/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java index 2af6e522bd..bb68236e79 100644 --- a/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java +++ b/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java @@ -229,7 +229,9 @@ private void appendStackTrace(StringBuilder b, Throwable ex, String prefix) { } abstract static class PrintStreamOrWriter { - /** Prints the specified string as a line on this StreamOrWriter. */ + /** Prints the specified string as a line on this StreamOrWriter. + * @param o string to print + */ abstract void println(Object o); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java index 69c7c0ce8a..11489490c7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java @@ -118,10 +118,11 @@ public static ConnectableFlowable create(Flowable source, } /** - * Creates a OperatorReplay instance to replay values of the given source observable. - * @param source the source observable - * @param bufferFactory the factory to instantiate the appropriate buffer when the observable becomes active - * @return the connectable observable + * Creates a OperatorReplay instance to replay values of the given source {@code Flowable}. + * @param the value type + * @param source the source {@code Flowable} to use + * @param bufferFactory the factory to instantiate the appropriate buffer when the {@code Flowable} becomes active + * @return the {@code ConnectableFlowable} instance */ static ConnectableFlowable create(Flowable source, final Supplier> bufferFactory) { @@ -544,6 +545,7 @@ public void dispose() { } /** * Convenience method to auto-cast the index object. + * @param type to cast index object * @return the current index object */ @SuppressWarnings("unchecked") diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelper.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelper.java index 9b9cd1e285..2ed6301e42 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelper.java @@ -78,6 +78,7 @@ static boolean tryAsCompletable(Object source, * Try subscribing to a {@link MaybeSource} mapped from * a scalar source (which implements {@link Supplier}). * @param the upstream value type + * @param the downstream value type * @param source the source reactive type ({@code Flowable} or {@code Observable}) * possibly implementing {@link Supplier}. * @param mapper the function that turns the scalar upstream value into a @@ -117,6 +118,7 @@ static boolean tryAsMaybe(Object source, * Try subscribing to a {@link SingleSource} mapped from * a scalar source (which implements {@link Supplier}). * @param the upstream value type + * @param the downstream value type * @param source the source reactive type ({@code Flowable} or {@code Observable}) * possibly implementing {@link Supplier}. * @param mapper the function that turns the scalar upstream value into a diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java index 102e2831b2..0e8c122d62 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java @@ -122,6 +122,7 @@ public static ConnectableObservable create(ObservableSource source, /** * Creates a OperatorReplay instance to replay values of the given source observable. + * @param the value type * @param source the source observable * @param bufferFactory the factory to instantiate the appropriate buffer when the observable becomes active * @return the connectable observable @@ -453,6 +454,7 @@ public void dispose() { } /** * Convenience method to auto-cast the index object. + * @param type index to be casted to * @return the index Object or null */ @SuppressWarnings("unchecked") diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrainHelper.java b/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrainHelper.java index 64178c3ee1..0204dfa371 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrainHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrainHelper.java @@ -291,6 +291,7 @@ static boolean isCancelled(BooleanSupplier cancelled) { /** * Drains the queue based on the outstanding requests in post-completed mode (only!). * + * @param the value type * @param n the current request amount * @param actual the target Subscriber to send events to * @param queue the queue to drain if in the post-complete state diff --git a/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java b/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java index 458ff01ebe..265ea894a5 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java +++ b/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java @@ -54,6 +54,9 @@ public abstract class BaseTestConsumer> { */ protected boolean timeout; + /** + * Constructs a {@code BaseTestConsumer} with {@code CountDownLatch} set to 1. + */ public BaseTestConsumer() { this.values = new VolatileSizeArrayList<>(); this.errors = new VolatileSizeArrayList<>(); diff --git a/src/test/java/io/reactivex/rxjava3/exceptions/CompositeExceptionTest.java b/src/test/java/io/reactivex/rxjava3/exceptions/CompositeExceptionTest.java index 3c0735ae78..cc34f62b89 100644 --- a/src/test/java/io/reactivex/rxjava3/exceptions/CompositeExceptionTest.java +++ b/src/test/java/io/reactivex/rxjava3/exceptions/CompositeExceptionTest.java @@ -148,7 +148,7 @@ public void compositeExceptionFromTwoDuplicateComposites() { cex.getCause().printStackTrace(); } - /** + /* * This hijacks the Throwable.printStackTrace() output and puts it in a string, where we can look for * "CIRCULAR REFERENCE" (a String added by Throwable.printEnclosedStackTrace) */ diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTestHelper.java b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTestHelper.java index 38e94f4536..3c899f262e 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTestHelper.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTestHelper.java @@ -28,6 +28,8 @@ private SchedulerTestHelper() { /** * Verifies that the given Scheduler does not deliver handled errors to its executing Thread's * {@link java.lang.Thread.UncaughtExceptionHandler}. + * + * @param scheduler {@link Scheduler} to verify. */ static void handledErrorIsNotDeliveredToThreadHandler(Scheduler scheduler) throws InterruptedException { Thread.UncaughtExceptionHandler originalHandler = Thread.getDefaultUncaughtExceptionHandler(); From e600b532b5a8eddf36b2d5464c327aea09862dab Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 12 Mar 2021 16:13:11 +0100 Subject: [PATCH 143/521] 3.x: CompositeException.printStackTrace to write directly into PS/PW (#7212) --- .../exceptions/CompositeException.java | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java b/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java index bb68236e79..5d2928b28f 100644 --- a/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java +++ b/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java @@ -199,40 +199,41 @@ public void printStackTrace(PrintWriter s) { * Special handling for printing out a {@code CompositeException}. * Loops through all inner exceptions and prints them out. * - * @param s + * @param output * stream to print to */ - private void printStackTrace(PrintStreamOrWriter s) { - StringBuilder b = new StringBuilder(128); - b.append(this).append('\n'); + private void printStackTrace(PrintStreamOrWriter output) { + output.append(this).append("\n"); for (StackTraceElement myStackElement : getStackTrace()) { - b.append("\tat ").append(myStackElement).append('\n'); + output.append("\tat ").append(myStackElement).append("\n"); } int i = 1; for (Throwable ex : exceptions) { - b.append(" ComposedException ").append(i).append(" :\n"); - appendStackTrace(b, ex, "\t"); + output.append(" ComposedException ").append(i).append(" :\n"); + appendStackTrace(output, ex, "\t"); i++; } - s.println(b.toString()); + output.append("\n"); } - private void appendStackTrace(StringBuilder b, Throwable ex, String prefix) { - b.append(prefix).append(ex).append('\n'); + private void appendStackTrace(PrintStreamOrWriter output, Throwable ex, String prefix) { + output.append(prefix).append(ex).append('\n'); for (StackTraceElement stackElement : ex.getStackTrace()) { - b.append("\t\tat ").append(stackElement).append('\n'); + output.append("\t\tat ").append(stackElement).append('\n'); } if (ex.getCause() != null) { - b.append("\tCaused by: "); - appendStackTrace(b, ex.getCause(), ""); + output.append("\tCaused by: "); + appendStackTrace(output, ex.getCause(), ""); } } abstract static class PrintStreamOrWriter { - /** Prints the specified string as a line on this StreamOrWriter. - * @param o string to print + /** + * Prints the object's string representation via the underlying PrintStream or PrintWriter. + * @param o the object to print + * @return this */ - abstract void println(Object o); + abstract PrintStreamOrWriter append(Object o); } /** @@ -246,11 +247,15 @@ static final class WrappedPrintStream extends PrintStreamOrWriter { } @Override - void println(Object o) { - printStream.println(o); + WrappedPrintStream append(Object o) { + printStream.print(o); + return this; } } + /** + * Same abstraction and implementation as in JDK to allow PrintStream and PrintWriter to share implementation. + */ static final class WrappedPrintWriter extends PrintStreamOrWriter { private final PrintWriter printWriter; @@ -259,8 +264,9 @@ static final class WrappedPrintWriter extends PrintStreamOrWriter { } @Override - void println(Object o) { - printWriter.println(o); + WrappedPrintWriter append(Object o) { + printWriter.print(o); + return this; } } From bdf13a1fdb54b79e0ece299535de75c653abc827 Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Sat, 13 Mar 2021 23:50:44 -0800 Subject: [PATCH 144/521] Fix intermittently failing unit tests (#7213) * Fix intermittently failing tests, mostly increasing timeout Fix several unit tests that are intermittently failing. All fixes involve increasing timeouts. Adjusted formatting in several areas within updated tests. * Remove formatting/refactoring from previous commit Superfluous formatting and refactoring was making review impossible. --- .../FlowableConcatMapSchedulerTest.java | 8 +++- .../flowable/FlowableConcatTest.java | 8 +++- .../operators/flowable/FlowableMergeTest.java | 40 +++++++++---------- .../flowable/FlowableSubscribeOnTest.java | 12 +++--- .../ObservableConcatMapSchedulerTest.java | 8 +++- .../observable/ObservableConcatTest.java | 8 +++- .../rxjava3/subjects/UnicastSubjectTest.java | 4 +- 7 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java index 825bb5565f..0f7c772566 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java @@ -355,7 +355,7 @@ public Flowable apply(Integer t) throws Throwable { } @Test - public void issue2890NoStackoverflow() throws InterruptedException { + public void issue2890NoStackoverflow() throws InterruptedException, TimeoutException { final ExecutorService executor = Executors.newFixedThreadPool(2); final Scheduler sch = Schedulers.from(executor); @@ -400,7 +400,11 @@ public void onError(Throwable e) { } }); - executor.awaitTermination(20000, TimeUnit.MILLISECONDS); + long awaitTerminationTimeoutMillis = 100_000; + if (!executor.awaitTermination(awaitTerminationTimeoutMillis, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Completed " + counter.get() + "/" + n + " before timed out after " + + awaitTerminationTimeoutMillis + " milliseconds."); + } assertEquals(n, counter.get()); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java index 1a8b6f4c4d..986dddad01 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java @@ -729,7 +729,7 @@ public void subscribe(Subscriber s) { } @Test - public void issue2890NoStackoverflow() throws InterruptedException { + public void issue2890NoStackoverflow() throws InterruptedException, TimeoutException { final ExecutorService executor = Executors.newFixedThreadPool(2); final Scheduler sch = Schedulers.from(executor); @@ -774,7 +774,11 @@ public void onError(Throwable e) { } }); - executor.awaitTermination(20000, TimeUnit.MILLISECONDS); + long awaitTerminationTimeoutMillis = 100_000; + if (!executor.awaitTermination(awaitTerminationTimeoutMillis, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Completed " + counter.get() + "/" + n + " before timed out after " + + awaitTerminationTimeoutMillis + " milliseconds."); + } assertEquals(n, counter.get()); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeTest.java index a40f7debb0..57b7a4a667 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeTest.java @@ -204,7 +204,7 @@ public void mergeArrayWithThreading() { TestSubscriber ts = new TestSubscriber<>(stringSubscriber); m.subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); verify(stringSubscriber, never()).onError(any(Throwable.class)); @@ -598,7 +598,7 @@ public void run() { TestSubscriber ts = new TestSubscriber<>(); merge.subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertComplete(); List onNextEvents = ts.values(); assertEquals(300, onNextEvents.size()); @@ -645,7 +645,7 @@ public void run() { TestSubscriber ts = new TestSubscriber<>(); merge.subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); ts.assertComplete(); List onNextEvents = ts.values(); @@ -706,7 +706,7 @@ public void onNext(Integer t) { }; Flowable.merge(f1.take(Flowable.bufferSize() * 2), Flowable.just(-99)).subscribe(testSubscriber); - testSubscriber.awaitDone(5, TimeUnit.SECONDS); + testSubscriber.awaitDone(10, TimeUnit.SECONDS); List onNextEvents = testSubscriber.values(); @@ -752,7 +752,7 @@ public void onNext(Integer t) { }; Flowable.merge(f1.take(Flowable.bufferSize() * 2), f2.take(Flowable.bufferSize() * 2)).observeOn(Schedulers.computation()).subscribe(testSubscriber); - testSubscriber.awaitDone(5, TimeUnit.SECONDS); + testSubscriber.awaitDone(10, TimeUnit.SECONDS); if (testSubscriber.errors().size() > 0) { testSubscriber.errors().get(0).printStackTrace(); } @@ -795,7 +795,7 @@ public void onNext(Integer t) { }; Flowable.merge(f1).observeOn(Schedulers.computation()).take(Flowable.bufferSize() * 2).subscribe(testSubscriber); - testSubscriber.awaitDone(5, TimeUnit.SECONDS); + testSubscriber.awaitDone(10, TimeUnit.SECONDS); if (testSubscriber.errors().size() > 0) { testSubscriber.errors().get(0).printStackTrace(); } @@ -850,7 +850,7 @@ public void onNext(Integer t) { }; Flowable.merge(f1).observeOn(Schedulers.computation()).take(Flowable.bufferSize() * 2).subscribe(testSubscriber); - testSubscriber.awaitDone(5, TimeUnit.SECONDS); + testSubscriber.awaitDone(10, TimeUnit.SECONDS); if (testSubscriber.errors().size() > 0) { testSubscriber.errors().get(0).printStackTrace(); } @@ -868,7 +868,7 @@ public void onNext(Integer t) { public void merge1AsyncStreamOf1() { TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(1, 1).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1, ts.values().size()); } @@ -877,7 +877,7 @@ public void merge1AsyncStreamOf1() { public void merge1AsyncStreamOf1000() { TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(1, 1000).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1000, ts.values().size()); } @@ -886,7 +886,7 @@ public void merge1AsyncStreamOf1000() { public void merge10AsyncStreamOf1000() { TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(10, 1000).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(10000, ts.values().size()); } @@ -895,7 +895,7 @@ public void merge10AsyncStreamOf1000() { public void merge1000AsyncStreamOf1000() { TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(1000, 1000).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1000000, ts.values().size()); } @@ -904,7 +904,7 @@ public void merge1000AsyncStreamOf1000() { public void merge2000AsyncStreamOf100() { TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(2000, 100).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(200000, ts.values().size()); } @@ -913,7 +913,7 @@ public void merge2000AsyncStreamOf100() { public void merge100AsyncStreamOf1() { TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(100, 1).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(100, ts.values().size()); } @@ -935,7 +935,7 @@ public Flowable apply(Integer i) { public void merge1SyncStreamOf1() { TestSubscriber ts = new TestSubscriber<>(); mergeNSyncStreamsOfN(1, 1).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1, ts.values().size()); } @@ -944,7 +944,7 @@ public void merge1SyncStreamOf1() { public void merge1SyncStreamOf1000000() { TestSubscriber ts = new TestSubscriber<>(); mergeNSyncStreamsOfN(1, 1000000).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1000000, ts.values().size()); } @@ -953,7 +953,7 @@ public void merge1SyncStreamOf1000000() { public void merge1000SyncStreamOf1000() { TestSubscriber ts = new TestSubscriber<>(); mergeNSyncStreamsOfN(1000, 1000).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1000000, ts.values().size()); } @@ -962,7 +962,7 @@ public void merge1000SyncStreamOf1000() { public void merge10000SyncStreamOf10() { TestSubscriber ts = new TestSubscriber<>(); mergeNSyncStreamsOfN(10000, 10).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(100000, ts.values().size()); } @@ -971,7 +971,7 @@ public void merge10000SyncStreamOf10() { public void merge1000000SyncStreamOf1() { TestSubscriber ts = new TestSubscriber<>(); mergeNSyncStreamsOfN(1000000, 1).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1000000, ts.values().size()); } @@ -1043,7 +1043,7 @@ public void subscribe(Subscriber s) { }); Flowable.merge(os).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(10000, ts.values().size()); } @@ -1196,7 +1196,7 @@ public void run() { latch.countDown(); } }).subscribe(); - boolean a = latch.await(2, TimeUnit.SECONDS); + boolean a = latch.await(10, TimeUnit.SECONDS); if (!a) { for (String s : messages) { System.out.println("DEBUG => " + s); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java index 9cb263e23d..01ee614fd5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java @@ -202,7 +202,7 @@ public void onNext(Integer t) { System.out.println("First schedule: " + t); assertTrue(t.getName().startsWith("Rx")); ts.request(10); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(20, TimeUnit.SECONDS); System.out.println("After reschedule: " + ts.lastThread()); assertEquals(t, ts.lastThread()); } @@ -254,7 +254,7 @@ public void onNext(Integer t) { } }).subscribeOn(Schedulers.newThread()).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(20, TimeUnit.SECONDS); ts.assertNoErrors(); } @@ -330,7 +330,7 @@ public void subscribe(FlowableEmitter s) throws Exception { .subscribeOn(Schedulers.single()) .observeOn(Schedulers.computation()) .test() - .awaitDone(5, TimeUnit.SECONDS) + .awaitDone(20, TimeUnit.SECONDS) .assertNoErrors() .assertComplete(); @@ -355,7 +355,7 @@ public void subscribe(FlowableEmitter s) throws Exception { .subscribeOn(Schedulers.single()) .observeOn(Schedulers.computation()) .test() - .awaitDone(5, TimeUnit.SECONDS) + .awaitDone(20, TimeUnit.SECONDS) .assertValueCount(Flowable.bufferSize()) .assertNoErrors() .assertComplete(); @@ -377,7 +377,7 @@ public void subscribe(FlowableEmitter s) throws Exception { .subscribeOn(Schedulers.single(), false) .observeOn(Schedulers.computation()) .test() - .awaitDone(5, TimeUnit.SECONDS) + .awaitDone(20, TimeUnit.SECONDS) .assertNoErrors() .assertComplete(); @@ -402,7 +402,7 @@ public void subscribe(FlowableEmitter s) throws Exception { .subscribeOn(Schedulers.single(), true) .observeOn(Schedulers.computation()) .test() - .awaitDone(5, TimeUnit.SECONDS) + .awaitDone(20, TimeUnit.SECONDS) .assertValueCount(Flowable.bufferSize()) .assertNoErrors() .assertComplete(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java index e6a50b9afa..f2a6ede314 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java @@ -307,7 +307,7 @@ public Observable apply(Integer t) throws Throwable { } @Test - public void issue2890NoStackoverflow() throws InterruptedException { + public void issue2890NoStackoverflow() throws InterruptedException, TimeoutException { final ExecutorService executor = Executors.newFixedThreadPool(2); final Scheduler sch = Schedulers.from(executor); @@ -352,7 +352,11 @@ public void onError(Throwable e) { } }); - executor.awaitTermination(20000, TimeUnit.MILLISECONDS); + long awaitTerminationTimeout = 100_000; + if (!executor.awaitTermination(awaitTerminationTimeout, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Completed " + counter.get() + "/" + n + " before timed out after " + + awaitTerminationTimeout + " milliseconds."); + } assertEquals(n, counter.get()); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatTest.java index 4b1462e302..d14f803ee9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatTest.java @@ -683,7 +683,7 @@ public void subscribe(Observer observer) { } @Test - public void issue2890NoStackoverflow() throws InterruptedException { + public void issue2890NoStackoverflow() throws InterruptedException, TimeoutException { final ExecutorService executor = Executors.newFixedThreadPool(2); final Scheduler sch = Schedulers.from(executor); @@ -728,7 +728,11 @@ public void onError(Throwable e) { } }); - executor.awaitTermination(20000, TimeUnit.MILLISECONDS); + long awaitTerminationTimeout = 100_000; + if (!executor.awaitTermination(awaitTerminationTimeout, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Completed " + counter.get() + "/" + n + " before timed out after " + + awaitTerminationTimeout + " milliseconds."); + } assertEquals(n, counter.get()); } diff --git a/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java index cf1f5d4a75..85cb20b48c 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java @@ -481,9 +481,7 @@ public void fusedNoConcurrentCleanDueToCancel() { us.onNext(i); } - to - .awaitDone(5, TimeUnit.SECONDS) - ; + to.awaitDone(10, TimeUnit.SECONDS); if (!errors.isEmpty()) { throw new CompositeException(errors); From 269bb0f1e2cb37b2211caabbc1ef1f331864e6f8 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Tue, 16 Mar 2021 02:06:55 -0400 Subject: [PATCH 145/521] Fix POM_URL (#7214) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index be63fe3d85..8da73143fa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ POM_PACKAGING=jar POM_DESCRIPTION=Reactive Extensions for Java POM_INCEPTION_YEAR=2013 -POM_URL=https://github.com/akarnokd/RxJavaExtensions +POM_URL=https://github.com/ReactiveX/RxJava POM_SCM_URL=https://github.com/ReactiveX/RxJava POM_SCM_CONNECTION=scm:git:git://github.com/ReactiveX/RxJava.git POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/ReactiveX/RxJava.git From c38ea99eee667dcf6cee427a880507e256807813 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Mar 2021 08:56:33 +0100 Subject: [PATCH 146/521] Bump guava from 30.1-jre to 30.1.1-jre (#7216) Bumps [guava](https://github.com/google/guava) from 30.1-jre to 30.1.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6420190cca..6e815f7a29 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { mockitoVersion = "3.8.0" jmhLibVersion = "1.21" jmhGradleVersion = "0.5.3" - guavaVersion = "30.1-jre" + guavaVersion = "30.1.1-jre" jacocoVersion = "0.8.4" animalSnifferVersion = "1.5.3" licenseVersion = "0.15.0" From 11b50a5ee28369a7004c1240cfaf7ea081febced Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Fri, 26 Mar 2021 15:07:08 -0700 Subject: [PATCH 147/521] Modernize gradle plugin block, change maven to maven-publish (#7219) * Modernize gradle plugin block, change maven to maven-publish Move gradle plugins to block at top of file per gradle recommendations. Change deprecated maven plugin to maven-publish as in Gradle 7.0 it will be removed. Several other minor updates to gradle file including, removing semi-colons, changing println to use built-in logger. * Updated gauva to correct version --- build.gradle | 121 ++++++++++++++++++--------------------------------- 1 file changed, 42 insertions(+), 79 deletions(-) diff --git a/build.gradle b/build.gradle index 6e815f7a29..6d260864c1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,72 +1,47 @@ -buildscript { +plugins { + id("java-library") + id("checkstyle") + id("eclipse") + id("jacoco") + id("maven-publish") + id("ru.vyarus.animalsniffer") version "1.5.3" + id("me.champeau.gradle.jmh") version "0.5.3" + id("com.github.hierynomus.license") version "0.15.0" + id("com.jfrog.artifactory") version "4.21.0" + id("biz.aQute.bnd.builder") version "5.3.0" + id("com.vanniktech.maven.publish") version "0.14.2" +} - // Dependency versions - // --------------------------------------- - ext { +ext { reactiveStreamsVersion = "1.0.3" junitVersion = "4.13.2" testNgVersion = "7.3.0" mockitoVersion = "3.8.0" jmhLibVersion = "1.21" - jmhGradleVersion = "0.5.3" guavaVersion = "30.1.1-jre" jacocoVersion = "0.8.4" - animalSnifferVersion = "1.5.3" - licenseVersion = "0.15.0" - jfrogExtractorVersion = "4.21.0" - bndVersion = "5.3.0" checkstyleVersion = "8.41" - vanniktechPublishPlugin = "0.14.2" - } - - // -------------------------------------- - - repositories { - mavenCentral() - gradlePluginPortal() - } - dependencies { - classpath "ru.vyarus:gradle-animalsniffer-plugin:$animalSnifferVersion" - classpath "gradle.plugin.com.hierynomus.gradle.plugins:license-gradle-plugin:$licenseVersion" - classpath "me.champeau.gradle:jmh-gradle-plugin:$jmhGradleVersion" - classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$jfrogExtractorVersion" - classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:$bndVersion" - classpath "com.vanniktech:gradle-maven-publish-plugin:$vanniktechPublishPlugin" - } } -group = "io.reactivex.rxjava3" -ext.githubProjectName = "rxjava" - -def releaseTag = System.getenv("BUILD_TAG"); +def releaseTag = System.getenv("BUILD_TAG") if (releaseTag != null && !releaseTag.isEmpty()) { if (releaseTag.startsWith("v")) { - releaseTag = releaseTag.substring(1); + releaseTag = releaseTag.substring(1) } - project.setProperty("VERSION_NAME" , releaseTag); + project.setProperty("VERSION_NAME" , releaseTag) - println("Releasing with version " + version); + logger.info("Releasing with version: {}", version) } +group = "io.reactivex.rxjava3" version = project.properties["VERSION_NAME"] - description = "RxJava: Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM." -apply plugin: "java-library" -apply plugin: "checkstyle" -apply plugin: "jacoco" -apply plugin: "ru.vyarus.animalsniffer" -apply plugin: "maven" -apply plugin: "me.champeau.gradle.jmh" -apply plugin: "com.github.hierynomus.license" -apply plugin: "com.jfrog.artifactory" -apply plugin: "eclipse" - sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 repositories { - mavenCentral() + mavenCentral() } dependencies { @@ -84,7 +59,7 @@ dependencies { } tasks.withType(JavaCompile) { - options.compilerArgs << "-parameters"; + options.compilerArgs << "-parameters" } javadoc { @@ -100,7 +75,7 @@ javadoc { options.addStringOption("top").value = "" options.addStringOption("doctitle").value = "" options.addStringOption("header").value = "" - options.stylesheetFile = new File(projectDir, "gradle/stylesheet.css"); + options.stylesheetFile = rootProject.file("gradle/stylesheet.css") options.links( "https://docs.oracle.com/javase/8/docs/api/", @@ -112,19 +87,16 @@ animalsniffer { annotation = "io.reactivex.rxjava3.internal.util.SuppressAnimalSniffer" } -apply plugin: 'maven' - -apply plugin: 'biz.aQute.bnd.builder' - jar { - bnd ('Bundle-Name': 'rxjava', - 'Bundle-Vendor': 'RxJava Contributors', - 'Bundle-Description': 'Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.', - 'Import-Package': '!org.junit,!junit.framework,!org.mockito.*,!org.testng.*,*', - 'Bundle-DocURL': 'https://github.com/ReactiveX/RxJava', - 'Eclipse-ExtensibleAPI': 'true', - 'Automatic-Module-Name': 'io.reactivex.rxjava3', - 'Export-Package': '!io.reactivex.rxjava3.internal.*, io.reactivex.rxjava3.*' + bnd ( + "Bundle-Name": "rxjava", + "Bundle-Vendor": "RxJava Contributors", + "Bundle-Description": "Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.", + "Import-Package": "!org.junit,!junit.framework,!org.mockito.*,!org.testng.*,*", + "Bundle-DocURL": "https://github.com/ReactiveX/RxJava", + "Eclipse-ExtensibleAPI": "true", + "Automatic-Module-Name": "io.reactivex.rxjava3", + "Export-Package": "!io.reactivex.rxjava3.internal.*, io.reactivex.rxjava3.*" ) } @@ -144,14 +116,13 @@ jmh { jvmArgsAppend = ["-Djmh.separateClasspathJAR=true"] if (project.hasProperty("jmh")) { - include = ".*" + project.jmh + ".*" - println("JMH: " + include); + include = [".*" + project.jmh + ".*"] + logger.info("JMH: {}", include) } - } plugins.withType(EclipsePlugin) { - project.eclipse.classpath.plusConfigurations += [ configurations.jmh ] + project.eclipse.classpath.plusConfigurations += [configurations.jmh] } test { @@ -199,25 +170,21 @@ task testng(type: Test) { check.dependsOn testng jacoco { - toolVersion = jacocoVersion // See http://www.eclemma.org/jacoco/. + toolVersion = jacocoVersion } task GCandMem(dependsOn: "check") doLast { - print("Memory usage before: ") - println(java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) + logger.lifecycle("Memory usage before: {}", java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) System.gc() Thread.sleep(200) - print("Memory usage: ") - println(java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) + logger.lifecycle("Memory usage: {}", java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) } task GCandMem2(dependsOn: "test") doLast { - print("Memory usage before: ") - println(java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) + logger.lifecycle("Memory usage before: {}", java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) System.gc() Thread.sleep(200) - print("Memory usage: ") - println(java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) + logger.lifecycle("Memory usage: {}", java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) } testng.dependsOn GCandMem2 @@ -258,12 +225,10 @@ def fixPom() { } if (rootProject.hasProperty("releaseMode")) { + logger.lifecycle("ReleaseMode: {}", rootProject.releaseMode) if ("branch".equals(rootProject.releaseMode)) { // From https://github.com/ReactiveX/RxAndroid/blob/2.x/rxandroid/build.gradle#L94 - - println("ReleaseMode: " + rootProject.releaseMode); - artifactory { contextUrl = "https://oss.jfrog.org/" @@ -287,13 +252,11 @@ if (rootProject.hasProperty("releaseMode")) { } if ("full".equals(rootProject.releaseMode)) { - apply plugin: "com.vanniktech.maven.publish" - fixPom() signing { - if (project.hasProperty('SIGNING_PRIVATE_KEY') && project.hasProperty('SIGNING_PASSWORD')) { - useInMemoryPgpKeys(project.getProperty('SIGNING_PRIVATE_KEY'), project.getProperty('SIGNING_PASSWORD')) + if (project.hasProperty("SIGNING_PRIVATE_KEY") && project.hasProperty("SIGNING_PASSWORD")) { + useInMemoryPgpKeys(project.getProperty("SIGNING_PRIVATE_KEY"), project.getProperty("SIGNING_PASSWORD")) } } mavenPublish { From cea706672d5f44f9be36c6e3914528ce9be86ced Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Thu, 8 Apr 2021 16:45:06 +0900 Subject: [PATCH 148/521] 3.x: fix typo in FlowableRetryTest.java (#7223) seperate -> separate --- .../rxjava3/internal/operators/flowable/FlowableRetryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryTest.java index adfc4431a8..224ae32d40 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryTest.java @@ -621,7 +621,7 @@ public void run() { } } - /** Observer for listener on seperate thread. */ + /** Observer for listener on separate thread. */ static final class AsyncSubscriber extends DefaultSubscriber { protected CountDownLatch latch = new CountDownLatch(1); From 5a678bbebc18d1b32551615bfdf8b37bf8c2aa1c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 8 Apr 2021 09:45:54 +0200 Subject: [PATCH 149/521] Bump mockito-core from 3.8.0 to 3.9.0 (#7224) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.8.0 to 3.9.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.8.0...v3.9.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6d260864c1..07b570bb3b 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ ext { reactiveStreamsVersion = "1.0.3" junitVersion = "4.13.2" testNgVersion = "7.3.0" - mockitoVersion = "3.8.0" + mockitoVersion = "3.9.0" jmhLibVersion = "1.21" guavaVersion = "30.1.1-jre" jacocoVersion = "0.8.4" From 82f489e1d3ca71ebf0aedc9daf087f4e403db2cb Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Thu, 8 Apr 2021 13:30:57 -0700 Subject: [PATCH 150/521] Remove custom Gradle garbage collection tasks (#7225) Garbage collection between Gradle tasks should no longer be needed now that tasks are run on Github servers. --- build.gradle | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index 07b570bb3b..6ce59f39b6 100644 --- a/build.gradle +++ b/build.gradle @@ -88,7 +88,7 @@ animalsniffer { } jar { - bnd ( + bnd( "Bundle-Name": "rxjava", "Bundle-Vendor": "RxJava Contributors", "Bundle-Description": "Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.", @@ -126,7 +126,6 @@ plugins.withType(EclipsePlugin) { } test { - testLogging { // showing skipped occasionally should prevent CI timeout due to lack of standard output events=["skipped", "failed"] // "started", "passed" @@ -173,22 +172,6 @@ jacoco { toolVersion = jacocoVersion } -task GCandMem(dependsOn: "check") doLast { - logger.lifecycle("Memory usage before: {}", java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) - System.gc() - Thread.sleep(200) - logger.lifecycle("Memory usage: {}", java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) -} - -task GCandMem2(dependsOn: "test") doLast { - logger.lifecycle("Memory usage before: {}", java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) - System.gc() - Thread.sleep(200) - logger.lifecycle("Memory usage: {}", java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) -} - -testng.dependsOn GCandMem2 - jacocoTestReport { reports { xml.enabled = true @@ -196,8 +179,6 @@ jacocoTestReport { } } -jacocoTestReport.dependsOn GCandMem - build.dependsOn jacocoTestReport checkstyle { From e51dbd2c56a150a4d22ea8f431ebaad09b22daa0 Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Thu, 8 Apr 2021 23:33:39 -0700 Subject: [PATCH 151/521] Initial commit for dependabot.yml config file (#7227) --- .github/dependabot.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..def5ba5d90 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "daily" From 2d873459e32f595f1e8bcccd770f5beb20682de1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Apr 2021 10:42:15 +0200 Subject: [PATCH 152/521] Bump actions/cache from v2 to v2.1.4 (#7230) Bumps [actions/cache](https://github.com/actions/cache) from v2 to v2.1.4. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v2...26968a09c0ea4f3e233fdddbafd1166051a095f6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gradle_branch.yml | 2 +- .github/workflows/gradle_jdk11.yml | 2 +- .github/workflows/gradle_pr.yml | 2 +- .github/workflows/gradle_release.yml | 2 +- .github/workflows/gradle_snapshot.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml index 85594b3549..3352f24532 100644 --- a/.github/workflows/gradle_branch.yml +++ b/.github/workflows/gradle_branch.yml @@ -18,7 +18,7 @@ jobs: with: java-version: 1.8 - name: Cache Gradle packages - uses: actions/cache@v2 + uses: actions/cache@v2.1.4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_jdk11.yml b/.github/workflows/gradle_jdk11.yml index f81dc9de27..76db130e5d 100644 --- a/.github/workflows/gradle_jdk11.yml +++ b/.github/workflows/gradle_jdk11.yml @@ -20,7 +20,7 @@ jobs: with: java-version: 11 - name: Cache Gradle packages - uses: actions/cache@v2 + uses: actions/cache@v2.1.4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index 40ee972118..c8d61bedde 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -18,7 +18,7 @@ jobs: with: java-version: 1.8 - name: Cache Gradle packages - uses: actions/cache@v2 + uses: actions/cache@v2.1.4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 9bca1fb9ec..0bee780f33 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -31,7 +31,7 @@ jobs: with: java-version: 1.8 - name: Cache Gradle packages - uses: actions/cache@v2 + uses: actions/cache@v2.1.4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index a6042fe8ec..6a0d64bf38 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -28,7 +28,7 @@ jobs: with: java-version: 1.8 - name: Cache Gradle packages - uses: actions/cache@v2 + uses: actions/cache@v2.1.4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} From 3dbf37e3da505c3e665a4eb7eda2ec7888aad686 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Apr 2021 10:44:28 +0200 Subject: [PATCH 153/521] Bump testng from 7.3.0 to 7.4.0 (#7231) Bumps [testng](https://github.com/cbeust/testng) from 7.3.0 to 7.4.0. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/cbeust/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/compare/7.3.0...7.4.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6ce59f39b6..7358b2609b 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ plugins { ext { reactiveStreamsVersion = "1.0.3" junitVersion = "4.13.2" - testNgVersion = "7.3.0" + testNgVersion = "7.4.0" mockitoVersion = "3.9.0" jmhLibVersion = "1.21" guavaVersion = "30.1.1-jre" From c1420ea41159f3011ec88df638e576bc76e23a3e Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Fri, 9 Apr 2021 11:31:26 -0700 Subject: [PATCH 154/521] Upgrade setup-java GitHub actions to v2 (#7232) There are two breaking changes between v1 and v2. v2 requires a java distribution be specified. 'adopt' for AdoptOpenJDK has been selected. v2 has also dropped support for legacy Java version syntax, so '1.8' must be become '8'. --- .github/workflows/gradle_branch.yml | 9 +++++---- .github/workflows/gradle_jdk11.yml | 7 ++++--- .github/workflows/gradle_pr.yml | 9 +++++---- .github/workflows/gradle_release.yml | 7 ++++--- .github/workflows/gradle_snapshot.yml | 9 +++++---- 5 files changed, 23 insertions(+), 18 deletions(-) diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml index 3352f24532..8b7142aac6 100644 --- a/.github/workflows/gradle_branch.yml +++ b/.github/workflows/gradle_branch.yml @@ -13,10 +13,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 + - name: Set up JDK 8 + uses: actions/setup-java@v2 with: - java-version: 1.8 + distribution: 'adopt' + java-version: '8' - name: Cache Gradle packages uses: actions/cache@v2.1.4 with: @@ -28,4 +29,4 @@ jobs: - name: Build branch without snapshot run: ./gradlew -PreleaseMode=pr build --stacktrace - name: Upload to Codecov - uses: codecov/codecov-action@v1 \ No newline at end of file + uses: codecov/codecov-action@v1 diff --git a/.github/workflows/gradle_jdk11.yml b/.github/workflows/gradle_jdk11.yml index 76db130e5d..c3a5ef5261 100644 --- a/.github/workflows/gradle_jdk11.yml +++ b/.github/workflows/gradle_jdk11.yml @@ -16,9 +16,10 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v2 with: - java-version: 11 + distribution: 'adopt' + java-version: '11' - name: Cache Gradle packages uses: actions/cache@v2.1.4 with: @@ -30,4 +31,4 @@ jobs: - name: Build PR run: ./gradlew -PreleaseMode=pr build --stacktrace #- name: Upload to Codecov - # uses: codecov/codecov-action@v1 \ No newline at end of file + # uses: codecov/codecov-action@v1 diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index c8d61bedde..66392e1c2d 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -13,10 +13,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 + - name: Set up JDK 8 + uses: actions/setup-java@v2 with: - java-version: 1.8 + distribution: 'adopt' + java-version: '8' - name: Cache Gradle packages uses: actions/cache@v2.1.4 with: @@ -28,4 +29,4 @@ jobs: - name: Build PR run: ./gradlew -PreleaseMode=pr build --stacktrace - name: Upload to Codecov - uses: codecov/codecov-action@v1 \ No newline at end of file + uses: codecov/codecov-action@v1 diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 0bee780f33..b34cd2fb64 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -26,10 +26,11 @@ jobs: CI_BUILD_NUMBER: ${{ github.run_number }} steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 + - name: Set up JDK 8 + uses: actions/setup-java@v2 with: - java-version: 1.8 + distribution: 'adopt' + java-version: '8' - name: Cache Gradle packages uses: actions/cache@v2.1.4 with: diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index 6a0d64bf38..a876b0a648 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -23,10 +23,11 @@ jobs: CI_BUILD_NUMBER: ${{ github.run_number }} steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 + - name: Set up JDK 8 + uses: actions/setup-java@v2 with: - java-version: 1.8 + distribution: 'adopt' + java-version: '8' - name: Cache Gradle packages uses: actions/cache@v2.1.4 with: @@ -42,4 +43,4 @@ jobs: - name: Upload to Codecov uses: codecov/codecov-action@v1 - name: Push Javadocs - run: ./push_javadoc.sh \ No newline at end of file + run: ./push_javadoc.sh From 5bb119b1489afe4fbc5bb51b828be95971ee8f1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:34:33 +0200 Subject: [PATCH 155/521] Bump actions/cache from v2.1.4 to v2.1.5 (#7234) Bumps [actions/cache](https://github.com/actions/cache) from v2.1.4 to v2.1.5. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v2.1.4...1a9e2138d905efd099035b49d8b7a3888c653ca8) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gradle_branch.yml | 2 +- .github/workflows/gradle_jdk11.yml | 2 +- .github/workflows/gradle_pr.yml | 2 +- .github/workflows/gradle_release.yml | 2 +- .github/workflows/gradle_snapshot.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml index 8b7142aac6..5b96684b3b 100644 --- a/.github/workflows/gradle_branch.yml +++ b/.github/workflows/gradle_branch.yml @@ -19,7 +19,7 @@ jobs: distribution: 'adopt' java-version: '8' - name: Cache Gradle packages - uses: actions/cache@v2.1.4 + uses: actions/cache@v2.1.5 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_jdk11.yml b/.github/workflows/gradle_jdk11.yml index c3a5ef5261..641de38ae1 100644 --- a/.github/workflows/gradle_jdk11.yml +++ b/.github/workflows/gradle_jdk11.yml @@ -21,7 +21,7 @@ jobs: distribution: 'adopt' java-version: '11' - name: Cache Gradle packages - uses: actions/cache@v2.1.4 + uses: actions/cache@v2.1.5 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index 66392e1c2d..a3c44c9ac1 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -19,7 +19,7 @@ jobs: distribution: 'adopt' java-version: '8' - name: Cache Gradle packages - uses: actions/cache@v2.1.4 + uses: actions/cache@v2.1.5 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index b34cd2fb64..f7fdc49ff8 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -32,7 +32,7 @@ jobs: distribution: 'adopt' java-version: '8' - name: Cache Gradle packages - uses: actions/cache@v2.1.4 + uses: actions/cache@v2.1.5 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index a876b0a648..926de34c1f 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -29,7 +29,7 @@ jobs: distribution: 'adopt' java-version: '8' - name: Cache Gradle packages - uses: actions/cache@v2.1.4 + uses: actions/cache@v2.1.5 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} From bb03bcf43989a2ad863245f43fa9f48c4f733a17 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 16 Apr 2021 08:32:51 +0200 Subject: [PATCH 156/521] 3.x: Action cache clear, limit environmental variables (#7238) * 3.x: Action cache clear, limit environmental variables * No secrets for PRs --- .github/workflows/gradle_branch.yml | 4 ++-- .github/workflows/gradle_jdk11.yml | 4 ++-- .github/workflows/gradle_pr.yml | 4 ++-- .github/workflows/gradle_release.yml | 23 +++++++++++++---------- .github/workflows/gradle_snapshot.yml | 23 ++++++++++++++--------- 5 files changed, 33 insertions(+), 25 deletions(-) diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml index 5b96684b3b..214939c0d9 100644 --- a/.github/workflows/gradle_branch.yml +++ b/.github/workflows/gradle_branch.yml @@ -22,8 +22,8 @@ jobs: uses: actions/cache@v2.1.5 with: path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: ${{ runner.os }}-gradle + key: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }} - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build branch without snapshot diff --git a/.github/workflows/gradle_jdk11.yml b/.github/workflows/gradle_jdk11.yml index 641de38ae1..9bbafd6615 100644 --- a/.github/workflows/gradle_jdk11.yml +++ b/.github/workflows/gradle_jdk11.yml @@ -24,8 +24,8 @@ jobs: uses: actions/cache@v2.1.5 with: path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: ${{ runner.os }}-gradle + key: ${{ runner.os }}-gradle-1-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle-1- - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build PR diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index a3c44c9ac1..5b8310c635 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -22,8 +22,8 @@ jobs: uses: actions/cache@v2.1.5 with: path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: ${{ runner.os }}-gradle + key: ${{ runner.os }}-gradle-1-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle-1- - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build PR diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index f7fdc49ff8..144e20cd27 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -15,14 +15,6 @@ jobs: runs-on: ubuntu-latest env: - # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions - # ------------------------------------------------------------------------------ - JAVADOCS_TOKEN: ${{ secrets.JAVADOCS_TOKEN }} - ORG_GRADLE_PROJECT_SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_USER }} - ORG_GRADLE_PROJECT_SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} - ORG_GRADLE_PROJECT_SIGNING_PRIVATE_KEY: ${{ secrets.SIGNING_PRIVATE_KEY }} - ORG_GRADLE_PROJECT_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - # ------------------------------------------------------------------------------ CI_BUILD_NUMBER: ${{ github.run_number }} steps: - uses: actions/checkout@v2 @@ -35,8 +27,8 @@ jobs: uses: actions/cache@v2.1.5 with: path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: ${{ runner.os }}-gradle + key: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }} - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Grant execute permission for push @@ -49,7 +41,18 @@ jobs: uses: codecov/codecov-action@v1 - name: Upload release run: ./gradlew -PreleaseMode=full javadocCleanup uploadArchives --no-daemon --no-parallel --stacktrace + env: + # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions + # ------------------------------------------------------------------------------ + ORG_GRADLE_PROJECT_SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_USER }} + ORG_GRADLE_PROJECT_SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + ORG_GRADLE_PROJECT_SIGNING_PRIVATE_KEY: ${{ secrets.SIGNING_PRIVATE_KEY }} + ORG_GRADLE_PROJECT_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - name: Publish release run: ./gradlew -PreleaseMode=full closeAndReleaseRepository --no-daemon --no-parallel --stacktrace - name: Push Javadocs run: ./push_javadoc.sh + env: + # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions + # ------------------------------------------------------------------------------ + JAVADOCS_TOKEN: ${{ secrets.JAVADOCS_TOKEN }} diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index 926de34c1f..099f5e9e91 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -12,13 +12,6 @@ jobs: runs-on: ubuntu-latest env: - # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions - # ------------------------------------------------------------------------------ - bintrayUser: ${{ secrets.BINTRAY_USER }} - bintrayKey: ${{ secrets.BINTRAY_KEY }} - sonatypeUsername: ${{ secrets.SONATYPE_USER }} - sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }} - JAVADOCS_TOKEN: ${{ secrets.JAVADOCS_TOKEN }} # ------------------------------------------------------------------------------ CI_BUILD_NUMBER: ${{ github.run_number }} steps: @@ -32,15 +25,27 @@ jobs: uses: actions/cache@v2.1.5 with: path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: ${{ runner.os }}-gradle + key: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }} - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Grant execute permission for push run: chmod +x push_javadoc.sh - name: Build and Snapshot branch run: ./gradlew -PreleaseMode=branch -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" build --stacktrace + env: + # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions + # ------------------------------------------------------------------------------ + bintrayUser: ${{ secrets.BINTRAY_USER }} + bintrayKey: ${{ secrets.BINTRAY_KEY }} + sonatypeUsername: ${{ secrets.SONATYPE_USER }} + sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }} - name: Upload to Codecov uses: codecov/codecov-action@v1 - name: Push Javadocs run: ./push_javadoc.sh + # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions + # ------------------------------------------------------------------------------ + env: + JAVADOCS_TOKEN: ${{ secrets.JAVADOCS_TOKEN }} + From 24113ad45f21e5b422e0cc51973ca5b9abe534c5 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Sat, 17 Apr 2021 18:00:37 +0200 Subject: [PATCH 157/521] Update gradle_release.yml Forgot to add env to the close repo step --- .github/workflows/gradle_release.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 144e20cd27..432d4d3eef 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -50,6 +50,11 @@ jobs: ORG_GRADLE_PROJECT_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - name: Publish release run: ./gradlew -PreleaseMode=full closeAndReleaseRepository --no-daemon --no-parallel --stacktrace + env: + # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions + # ------------------------------------------------------------------------------ + ORG_GRADLE_PROJECT_SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_USER }} + ORG_GRADLE_PROJECT_SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} - name: Push Javadocs run: ./push_javadoc.sh env: From 28a559483b991fe094141cc99e54dd8b9adb8f8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 12:05:02 +0200 Subject: [PATCH 158/521] Bump com.vanniktech.maven.publish from 0.14.2 to 0.15.0 (#7246) Bumps [com.vanniktech.maven.publish](https://github.com/vanniktech/gradle-maven-publish-plugin) from 0.14.2 to 0.15.0. - [Release notes](https://github.com/vanniktech/gradle-maven-publish-plugin/releases) - [Changelog](https://github.com/vanniktech/gradle-maven-publish-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/vanniktech/gradle-maven-publish-plugin/compare/0.14.2...0.15.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7358b2609b..061e15d08b 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ plugins { id("com.github.hierynomus.license") version "0.15.0" id("com.jfrog.artifactory") version "4.21.0" id("biz.aQute.bnd.builder") version "5.3.0" - id("com.vanniktech.maven.publish") version "0.14.2" + id("com.vanniktech.maven.publish") version "0.15.0" } ext { From 0792e059237ec804f2992ebd665fd0a65020698c Mon Sep 17 00:00:00 2001 From: David Karnok Date: Mon, 26 Apr 2021 12:28:44 +0200 Subject: [PATCH 159/521] Update env names for release plugin v0.15 (#7247) #7246 --- .github/workflows/gradle_release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 432d4d3eef..9683482ad2 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -44,8 +44,8 @@ jobs: env: # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions # ------------------------------------------------------------------------------ - ORG_GRADLE_PROJECT_SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_USER }} - ORG_GRADLE_PROJECT_SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + ORG_GRADLE_PROJECT_mavenCentralRepositoryUsername: ${{ secrets.SONATYPE_USER }} + ORG_GRADLE_PROJECT_mavenCentralRepositoryPassword: ${{ secrets.SONATYPE_PASSWORD }} ORG_GRADLE_PROJECT_SIGNING_PRIVATE_KEY: ${{ secrets.SIGNING_PRIVATE_KEY }} ORG_GRADLE_PROJECT_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - name: Publish release @@ -53,8 +53,8 @@ jobs: env: # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions # ------------------------------------------------------------------------------ - ORG_GRADLE_PROJECT_SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_USER }} - ORG_GRADLE_PROJECT_SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + ORG_GRADLE_PROJECT_mavenCentralRepositoryUsername: ${{ secrets.SONATYPE_USER }} + ORG_GRADLE_PROJECT_mavenCentralRepositoryPassword: ${{ secrets.SONATYPE_PASSWORD }} - name: Push Javadocs run: ./push_javadoc.sh env: From 3ad6bc105bc74aaa9f04026d207755f9cc40ae28 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Mon, 3 May 2021 11:51:05 +0200 Subject: [PATCH 160/521] Update gradle_release.yml Rename maven central credential properties --- .github/workflows/gradle_release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 9683482ad2..891fa0859a 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -44,8 +44,8 @@ jobs: env: # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions # ------------------------------------------------------------------------------ - ORG_GRADLE_PROJECT_mavenCentralRepositoryUsername: ${{ secrets.SONATYPE_USER }} - ORG_GRADLE_PROJECT_mavenCentralRepositoryPassword: ${{ secrets.SONATYPE_PASSWORD }} + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USER }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} ORG_GRADLE_PROJECT_SIGNING_PRIVATE_KEY: ${{ secrets.SIGNING_PRIVATE_KEY }} ORG_GRADLE_PROJECT_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - name: Publish release @@ -53,8 +53,8 @@ jobs: env: # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions # ------------------------------------------------------------------------------ - ORG_GRADLE_PROJECT_mavenCentralRepositoryUsername: ${{ secrets.SONATYPE_USER }} - ORG_GRADLE_PROJECT_mavenCentralRepositoryPassword: ${{ secrets.SONATYPE_PASSWORD }} + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USER }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} - name: Push Javadocs run: ./push_javadoc.sh env: From 94ed352d49ed102086b466ae268afbd6ae17951b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 May 2021 12:08:09 +0200 Subject: [PATCH 161/521] Bump com.vanniktech.maven.publish from 0.15.0 to 0.15.1 (#7250) Bumps [com.vanniktech.maven.publish](https://github.com/vanniktech/gradle-maven-publish-plugin) from 0.15.0 to 0.15.1. - [Release notes](https://github.com/vanniktech/gradle-maven-publish-plugin/releases) - [Changelog](https://github.com/vanniktech/gradle-maven-publish-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/vanniktech/gradle-maven-publish-plugin/compare/0.15.0...0.15.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 061e15d08b..9c0aa000f5 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ plugins { id("com.github.hierynomus.license") version "0.15.0" id("com.jfrog.artifactory") version "4.21.0" id("biz.aQute.bnd.builder") version "5.3.0" - id("com.vanniktech.maven.publish") version "0.15.0" + id("com.vanniktech.maven.publish") version "0.15.1" } ext { From 56cd564f67a263923500ec590965548644d1bd93 Mon Sep 17 00:00:00 2001 From: akarnokd Date: Mon, 3 May 2021 14:15:38 +0200 Subject: [PATCH 162/521] Try fixing snapshot releases, attempt 1 --- .github/workflows/gradle_snapshot.yml | 10 ++++---- build.gradle | 36 ++++++++++++--------------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index 099f5e9e91..ddd72b67bb 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -32,14 +32,14 @@ jobs: - name: Grant execute permission for push run: chmod +x push_javadoc.sh - name: Build and Snapshot branch - run: ./gradlew -PreleaseMode=branch -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" build --stacktrace + run: ./gradlew -PreleaseMode=branch build --stacktrace --no-daemon + - name: Upload Snapshot + run: ./gradlew -PreleaseMode=branch javadocCleanup uploadArchives --no-daemon --no-parallel --stacktrace env: # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions # ------------------------------------------------------------------------------ - bintrayUser: ${{ secrets.BINTRAY_USER }} - bintrayKey: ${{ secrets.BINTRAY_KEY }} - sonatypeUsername: ${{ secrets.SONATYPE_USER }} - sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }} + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USER }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} - name: Upload to Codecov uses: codecov/codecov-action@v1 - name: Push Javadocs diff --git a/build.gradle b/build.gradle index 9c0aa000f5..da06dfbc85 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,6 @@ plugins { id("ru.vyarus.animalsniffer") version "1.5.3" id("me.champeau.gradle.jmh") version "0.5.3" id("com.github.hierynomus.license") version "0.15.0" - id("com.jfrog.artifactory") version "4.21.0" id("biz.aQute.bnd.builder") version "5.3.0" id("com.vanniktech.maven.publish") version "0.15.1" } @@ -209,27 +208,24 @@ if (rootProject.hasProperty("releaseMode")) { logger.lifecycle("ReleaseMode: {}", rootProject.releaseMode) if ("branch".equals(rootProject.releaseMode)) { - // From https://github.com/ReactiveX/RxAndroid/blob/2.x/rxandroid/build.gradle#L94 - artifactory { - contextUrl = "https://oss.jfrog.org/" - - publish { - repository { - repoKey = "oss-snapshot-local" - - username = rootProject.bintrayUser - password = rootProject.bintrayKey - } - - defaults { - publishConfigs("archives") - } - - fixPom() + + if (version.endsWith("-SNAPSHOT")) { + fixPom() + + publishing { + repositories { + maven { + url = "https://s01.oss.sonatype.org/content/repositories/snapshots/" + } } + } + + mavenPublish { + nexus { + stagingProfile = "io.reactivex" + } + } } - - build.finalizedBy(artifactoryPublish) } if ("full".equals(rootProject.releaseMode)) { From c4163c96549c18c21595ff30bc647c805ef103a9 Mon Sep 17 00:00:00 2001 From: akarnokd Date: Mon, 3 May 2021 14:17:40 +0200 Subject: [PATCH 163/521] Don't do full build yet, saves time with trying the new snapshot push --- .github/workflows/gradle_snapshot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index ddd72b67bb..4db8fbfb8f 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -32,7 +32,7 @@ jobs: - name: Grant execute permission for push run: chmod +x push_javadoc.sh - name: Build and Snapshot branch - run: ./gradlew -PreleaseMode=branch build --stacktrace --no-daemon + run: ./gradlew -PreleaseMode=branch assemble --stacktrace --no-daemon - name: Upload Snapshot run: ./gradlew -PreleaseMode=branch javadocCleanup uploadArchives --no-daemon --no-parallel --stacktrace env: From 4aa1c43b0b620eecb65e91f19c8e56ee5d9bf46a Mon Sep 17 00:00:00 2001 From: akarnokd Date: Mon, 3 May 2021 14:29:37 +0200 Subject: [PATCH 164/521] Re-enable snapshot full build, update README --- .github/workflows/gradle_snapshot.yml | 2 +- README.md | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index 4db8fbfb8f..ddd72b67bb 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -32,7 +32,7 @@ jobs: - name: Grant execute permission for push run: chmod +x push_javadoc.sh - name: Build and Snapshot branch - run: ./gradlew -PreleaseMode=branch assemble --stacktrace --no-daemon + run: ./gradlew -PreleaseMode=branch build --stacktrace --no-daemon - name: Upload Snapshot run: ./gradlew -PreleaseMode=branch javadocCleanup uploadArchives --no-daemon --no-parallel --stacktrace env: diff --git a/README.md b/README.md index b14ae42f84..8e894869f7 100644 --- a/README.md +++ b/README.md @@ -565,7 +565,20 @@ and for Ivy: ### Snapshots -Snapshots are available via https://oss.jfrog.org/libs-snapshot/io/reactivex/rxjava3/rxjava/ +Snapshots after May 1st, 2021 are available via https://oss.sonatype.org/content/repositories/snapshots/io/reactivex/rxjava3/rxjava/ + +```groovy +repositories { + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } +} + +dependencies { + implementation 'io.reactivex.rxjava3:rxjava:3.0.0-SNAPSHOT' +} +``` + +Snapshots before May 1st, 2021 are available via https://oss.jfrog.org/libs-snapshot/io/reactivex/rxjava3/rxjava/ +(Note that due to the Sunset of Bintray, our jfrog access has been severed, hence the new snapshot repo above.) ```groovy repositories { @@ -573,7 +586,7 @@ repositories { } dependencies { - compile 'io.reactivex.rxjava3:rxjava:3.0.0-SNAPSHOT' + implementation 'io.reactivex.rxjava3:rxjava:3.0.0-SNAPSHOT' } ``` From 93e5216ac1f352aea78eda14f933d2bf536a6a50 Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Tue, 4 May 2021 23:28:55 -0700 Subject: [PATCH 165/521] Remove fixPom method from build.gradle (#7253) The pom is generated with the reactive-streams scope set to 'compile' by default. To test that this works, the below gradle tasks were run: ./gradlew -PreleaseMode=full cleanGeneratePomFileForMavenPublication generatePomFileForMavenPublication ./gradlew -PreleaseMode=branch cleanGeneratePomFileForMavenPublication generatePomFileForMavenPublication Both tasks generated a pom file with the reactive-streams set to 'compile' scope. --- build.gradle | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/build.gradle b/build.gradle index da06dfbc85..5d66ba9bfd 100644 --- a/build.gradle +++ b/build.gradle @@ -191,27 +191,12 @@ checkstyle { apply from: file("gradle/javadoc_cleanup.gradle") -def fixPom() { - // Reactive-Streams as compile dependency - publishing.publications.all { - pom.withXml { - asNode().dependencies."*".findAll() { - it.scope.text() == "runtime" && project.configurations.compile.allDependencies.find { dep -> - dep.name == it.artifactId.text() - } - }.each { it.scope*.value = "compile"} - } - } -} - if (rootProject.hasProperty("releaseMode")) { logger.lifecycle("ReleaseMode: {}", rootProject.releaseMode) if ("branch".equals(rootProject.releaseMode)) { if (version.endsWith("-SNAPSHOT")) { - fixPom() - publishing { repositories { maven { @@ -229,8 +214,6 @@ if (rootProject.hasProperty("releaseMode")) { } if ("full".equals(rootProject.releaseMode)) { - fixPom() - signing { if (project.hasProperty("SIGNING_PRIVATE_KEY") && project.hasProperty("SIGNING_PASSWORD")) { useInMemoryPgpKeys(project.getProperty("SIGNING_PRIVATE_KEY"), project.getProperty("SIGNING_PASSWORD")) From 06294de92d5f6ebb4870e64a84055460163630cc Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Wed, 5 May 2021 10:47:48 -0700 Subject: [PATCH 166/521] Update Gradle task dependencies to remove dependsOn 'build' task (#7255) It is generally discouraged to add dependencies on 'build', so 'javadocCleanup' and 'jacocoTestReport' have been removed and put on better tasks. Also, 'jacocoTestReport' needs input from the test results. Updated task dependencies: - Gradle 'javadoc' task is finalized by 'javadocCleanup', and 'javadocCleanup' no longer directly invoked by 'build' - Gradle 'jacocoTestReport' task run by 'check' instead of 'build', and 'jacocoTestReport' depends on 'test' and 'testng' Changes due to dependency updates: - Add 'javadoc' generation task on Github actions that only 'build' - Remove 'javadocCleanup' task from Github action as 'publish...' tasks already dependent on 'javadoc' Miscellaneous tangential cleanup: - Remove "pr" parameter on command line in Github workflow as parameter is no longer functional - Enclose source/target compatibility in Gradle java block --- .github/workflows/gradle_branch.yml | 4 +++- .github/workflows/gradle_jdk11.yml | 6 +++--- .github/workflows/gradle_pr.yml | 6 ++++-- .github/workflows/gradle_release.yml | 2 +- .github/workflows/gradle_snapshot.yml | 3 +-- build.gradle | 19 +++++++++++++------ gradle/javadoc_cleanup.gradle | 1 - 7 files changed, 25 insertions(+), 16 deletions(-) diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml index 214939c0d9..96acfc3a9d 100644 --- a/.github/workflows/gradle_branch.yml +++ b/.github/workflows/gradle_branch.yml @@ -27,6 +27,8 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build branch without snapshot - run: ./gradlew -PreleaseMode=pr build --stacktrace + run: ./gradlew build --stacktrace - name: Upload to Codecov uses: codecov/codecov-action@v1 + - name: Generate Javadocs + run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_jdk11.yml b/.github/workflows/gradle_jdk11.yml index 9bbafd6615..697bcca364 100644 --- a/.github/workflows/gradle_jdk11.yml +++ b/.github/workflows/gradle_jdk11.yml @@ -29,6 +29,6 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build PR - run: ./gradlew -PreleaseMode=pr build --stacktrace - #- name: Upload to Codecov - # uses: codecov/codecov-action@v1 + run: ./gradlew build --stacktrace + - name: Generate Javadocs + run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index 5b8310c635..9d3b33d517 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -1,7 +1,7 @@ # This workflow will build a Java project with Gradle # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle -name: PR +name: Pull Request on: pull_request: @@ -27,6 +27,8 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build PR - run: ./gradlew -PreleaseMode=pr build --stacktrace + run: ./gradlew build --stacktrace - name: Upload to Codecov uses: codecov/codecov-action@v1 + - name: Generate Javadocs + run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 891fa0859a..2050936863 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -40,7 +40,7 @@ jobs: - name: Upload to Codecov uses: codecov/codecov-action@v1 - name: Upload release - run: ./gradlew -PreleaseMode=full javadocCleanup uploadArchives --no-daemon --no-parallel --stacktrace + run: ./gradlew -PreleaseMode=full uploadArchives --no-daemon --no-parallel --stacktrace env: # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions # ------------------------------------------------------------------------------ diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index ddd72b67bb..8d4457a497 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -34,7 +34,7 @@ jobs: - name: Build and Snapshot branch run: ./gradlew -PreleaseMode=branch build --stacktrace --no-daemon - name: Upload Snapshot - run: ./gradlew -PreleaseMode=branch javadocCleanup uploadArchives --no-daemon --no-parallel --stacktrace + run: ./gradlew -PreleaseMode=branch uploadArchives --no-daemon --no-parallel --stacktrace env: # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions # ------------------------------------------------------------------------------ @@ -48,4 +48,3 @@ jobs: # ------------------------------------------------------------------------------ env: JAVADOCS_TOKEN: ${{ secrets.JAVADOCS_TOKEN }} - diff --git a/build.gradle b/build.gradle index 5d66ba9bfd..9a2c3de5cd 100644 --- a/build.gradle +++ b/build.gradle @@ -36,9 +36,6 @@ group = "io.reactivex.rxjava3" version = project.properties["VERSION_NAME"] description = "RxJava: Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM." -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 - repositories { mavenCentral() } @@ -57,10 +54,17 @@ dependencies { testImplementation "com.google.guava:guava:$guavaVersion" } +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + tasks.withType(JavaCompile) { options.compilerArgs << "-parameters" } +apply from: file("gradle/javadoc_cleanup.gradle") + javadoc { failOnError = false exclude "**/internal/**" @@ -80,6 +84,8 @@ javadoc { "https://docs.oracle.com/javase/8/docs/api/", "http://www.reactive-streams.org/reactive-streams-$%7BreactiveStreamsVersion%7D-javadoc/" ) + + finalizedBy javadocCleanup } animalsniffer { @@ -172,13 +178,16 @@ jacoco { } jacocoTestReport { + dependsOn test + dependsOn testng + reports { xml.enabled = true html.enabled = true } } -build.dependsOn jacocoTestReport +check.dependsOn jacocoTestReport checkstyle { configFile = rootProject.file("config/checkstyle/checkstyle.xml") @@ -189,8 +198,6 @@ checkstyle { ] } -apply from: file("gradle/javadoc_cleanup.gradle") - if (rootProject.hasProperty("releaseMode")) { logger.lifecycle("ReleaseMode: {}", rootProject.releaseMode) diff --git a/gradle/javadoc_cleanup.gradle b/gradle/javadoc_cleanup.gradle index f6811fdcaf..f9f365f4a3 100644 --- a/gradle/javadoc_cleanup.gradle +++ b/gradle/javadoc_cleanup.gradle @@ -57,4 +57,3 @@ def fixJavadocFile(file) { file.setText(fileContents, 'UTF-8'); } -build.dependsOn javadocCleanup \ No newline at end of file From 8fb4640688a1229ed2e09d6618bce10fcb622cf2 Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Wed, 5 May 2021 13:54:51 -0700 Subject: [PATCH 167/521] Remove redundant addition of JMH to classpath (#7257) Remove manual addition of JMH classpath in build file. Gradle adds classpath for all sourceSets to Eclipse by default as fixed in Gradle 6.8 by PR 14534. The JMH sourceSet is added by the JMH Plugin. Tested by running 'eclipse' task in Eclipse 2021-03 (4.19.0). Then refreshing, and JMH still on classpath and viewable in Eclipse. Then removed project from Eclipse, ran 'cleanEclipse', and re-imported project to Eclipse. All successful. --- build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build.gradle b/build.gradle index 9a2c3de5cd..1a4e6ace04 100644 --- a/build.gradle +++ b/build.gradle @@ -126,10 +126,6 @@ jmh { } } -plugins.withType(EclipsePlugin) { - project.eclipse.classpath.plusConfigurations += [configurations.jmh] -} - test { testLogging { // showing skipped occasionally should prevent CI timeout due to lack of standard output From ac5e569072dee77402f3bb85fa60ce94f5581987 Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Wed, 5 May 2021 23:51:16 -0700 Subject: [PATCH 168/521] Remove Javadoc not failing on error (#7258) The 'javadoc' task should fail when there is an error. The 'javadocCleanup' task failed for PR 7239 due to a plugin issue 242, while it should have been the 'javadoc' task that failed the build. Also, publishing when javadocs fail is not desired. --- build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1a4e6ace04..d39a630675 100644 --- a/build.gradle +++ b/build.gradle @@ -66,7 +66,6 @@ tasks.withType(JavaCompile) { apply from: file("gradle/javadoc_cleanup.gradle") javadoc { - failOnError = false exclude "**/internal/**" exclude "**/test/**" exclude "**/perf/**" From 0a9b09e62511174edd13194c9e3b2e43c906b1a6 Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Thu, 6 May 2021 22:02:29 -0700 Subject: [PATCH 169/521] 3.x: Gradle wrapper 7 0 (#7259) * Update Gradle Wrapper to 7.0 * Update com.github.hierynomus.license from 0.15.0 to 0.16.1 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d39a630675..b1b07a7e5a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { id("maven-publish") id("ru.vyarus.animalsniffer") version "1.5.3" id("me.champeau.gradle.jmh") version "0.5.3" - id("com.github.hierynomus.license") version "0.15.0" + id("com.github.hierynomus.license") version "0.16.1" id("biz.aQute.bnd.builder") version "5.3.0" id("com.vanniktech.maven.publish") version "0.15.1" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 442d9132ea..f371643eed 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 2f4815d79287d50589a226db5395ab7525bfbcc6 Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Thu, 6 May 2021 22:21:45 -0700 Subject: [PATCH 170/521] 3.x: Move Gradle properties into gradle.properties file (#7260) * Move Gradle properties into gradle.properties file Moved version, group, and description into gradle.properties file. Removed POM_ARTIFACT property as the 'name' property in the settings file is used when building the POM. Removed 'release.scope' property as it does not appear to be used. Removed VERSION_NAME property and references to it, in favor of 'version' property. * Replace POM_ARTIFACT_ID in gradle.properties Added POM_ARTIFACT_ID=rxjava in gradle.properties file --- build.gradle | 8 ++------ gradle.properties | 6 +++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index b1b07a7e5a..b2fd3f888b 100644 --- a/build.gradle +++ b/build.gradle @@ -27,15 +27,11 @@ if (releaseTag != null && !releaseTag.isEmpty()) { if (releaseTag.startsWith("v")) { releaseTag = releaseTag.substring(1) } - project.setProperty("VERSION_NAME" , releaseTag) + project.version = releaseTag - logger.info("Releasing with version: {}", version) + logger.lifecycle("Releasing with version: " + project.version) } -group = "io.reactivex.rxjava3" -version = project.properties["VERSION_NAME"] -description = "RxJava: Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM." - repositories { mavenCentral() } diff --git a/gradle.properties b/gradle.properties index 8da73143fa..e685b8103a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ -release.scope=patch -VERSION_NAME=3.0.0-SNAPSHOT +group=io.reactivex.rxjava3 +version=3.0.0-SNAPSHOT +description=RxJava: Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM. -GROUP=io.reactivex.rxjava3 POM_ARTIFACT_ID=rxjava POM_NAME=RxJava POM_PACKAGING=jar From 177256eecde749dbf120a51e19f8af4555cae3f1 Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Fri, 7 May 2021 01:24:27 -0700 Subject: [PATCH 171/521] Remove toolVersion for jacoco and checkstyle (#7261) Remove versioning of Jacoco and Checkstyle. Instead they will be updated by Gradle when it is updated. Dependabot does not update tool versions. If issues come up, they can be corrected when Gradle is updated. --- build.gradle | 7 ------- 1 file changed, 7 deletions(-) diff --git a/build.gradle b/build.gradle index b2fd3f888b..329e8347fe 100644 --- a/build.gradle +++ b/build.gradle @@ -18,8 +18,6 @@ ext { mockitoVersion = "3.9.0" jmhLibVersion = "1.21" guavaVersion = "30.1.1-jre" - jacocoVersion = "0.8.4" - checkstyleVersion = "8.41" } def releaseTag = System.getenv("BUILD_TAG") @@ -164,10 +162,6 @@ task testng(type: Test) { check.dependsOn testng -jacoco { - toolVersion = jacocoVersion -} - jacocoTestReport { dependsOn test dependsOn testng @@ -182,7 +176,6 @@ check.dependsOn jacocoTestReport checkstyle { configFile = rootProject.file("config/checkstyle/checkstyle.xml") - toolVersion = checkstyleVersion configProperties = [ "checkstyle.suppressions.file": rootProject.file("config/checkstyle/suppressions.xml"), "checkstyle.header.file": rootProject.file("config/license/HEADER_JAVA") From 74ab2485b9b44b541fffad557d106a41b4ff39f5 Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Fri, 7 May 2021 02:12:30 -0700 Subject: [PATCH 172/521] Rename workflow tasks (#7262) Change 'build' tasks name to Build RxJava. Update Javadoc task names. --- .github/workflows/gradle_branch.yml | 4 ++-- .github/workflows/gradle_jdk11.yml | 4 ++-- .github/workflows/gradle_pr.yml | 4 ++-- .github/workflows/gradle_release.yml | 6 +++--- .github/workflows/gradle_snapshot.yml | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml index 96acfc3a9d..f0454433b3 100644 --- a/.github/workflows/gradle_branch.yml +++ b/.github/workflows/gradle_branch.yml @@ -26,9 +26,9 @@ jobs: restore-keys: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }} - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Build branch without snapshot + - name: Build RxJava run: ./gradlew build --stacktrace - name: Upload to Codecov uses: codecov/codecov-action@v1 - - name: Generate Javadocs + - name: Generate Javadoc run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_jdk11.yml b/.github/workflows/gradle_jdk11.yml index 697bcca364..13a89abd91 100644 --- a/.github/workflows/gradle_jdk11.yml +++ b/.github/workflows/gradle_jdk11.yml @@ -28,7 +28,7 @@ jobs: restore-keys: ${{ runner.os }}-gradle-1- - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Build PR + - name: Build RxJava run: ./gradlew build --stacktrace - - name: Generate Javadocs + - name: Generate Javadoc run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index 9d3b33d517..dbf3d6f1ca 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -26,9 +26,9 @@ jobs: restore-keys: ${{ runner.os }}-gradle-1- - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Build PR + - name: Build RxJava run: ./gradlew build --stacktrace - name: Upload to Codecov uses: codecov/codecov-action@v1 - - name: Generate Javadocs + - name: Generate Javadoc run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 2050936863..bbce6f6b43 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -35,8 +35,8 @@ jobs: run: chmod +x push_javadoc.sh - name: Extract version tag run: echo "BUILD_TAG=${GITHUB_REF:10}" >> $GITHUB_ENV - - name: Build and Release - run: ./gradlew -PreleaseMode=full build --stacktrace --no-daemon + - name: Build RxJava + run: ./gradlew build --stacktrace --no-daemon - name: Upload to Codecov uses: codecov/codecov-action@v1 - name: Upload release @@ -55,7 +55,7 @@ jobs: # ------------------------------------------------------------------------------ ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USER }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} - - name: Push Javadocs + - name: Push Javadoc run: ./push_javadoc.sh env: # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index 8d4457a497..d0d0ddea2c 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -31,8 +31,8 @@ jobs: run: chmod +x gradlew - name: Grant execute permission for push run: chmod +x push_javadoc.sh - - name: Build and Snapshot branch - run: ./gradlew -PreleaseMode=branch build --stacktrace --no-daemon + - name: Build RxJava + run: ./gradlew build --stacktrace --no-daemon - name: Upload Snapshot run: ./gradlew -PreleaseMode=branch uploadArchives --no-daemon --no-parallel --stacktrace env: @@ -42,7 +42,7 @@ jobs: ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} - name: Upload to Codecov uses: codecov/codecov-action@v1 - - name: Push Javadocs + - name: Push Javadoc run: ./push_javadoc.sh # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions # ------------------------------------------------------------------------------ From 63fcf838988f33fbad10dc67072c8d6e4a517ac5 Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Fri, 7 May 2021 04:10:20 -0700 Subject: [PATCH 173/521] Updated javadocCleanup to use logger and remove semi-colons (#7264) --- gradle/javadoc_cleanup.gradle | 75 +++++++++++++++++------------------ 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/gradle/javadoc_cleanup.gradle b/gradle/javadoc_cleanup.gradle index f9f365f4a3..a391375af4 100644 --- a/gradle/javadoc_cleanup.gradle +++ b/gradle/javadoc_cleanup.gradle @@ -1,59 +1,58 @@ // remove the excessive whitespaces between method arguments in the javadocs task javadocCleanup(dependsOn: "javadoc") doLast { - fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Flowable.html')); - fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Observable.html')); - fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Single.html')); - fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Maybe.html')); - fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Completable.html')); + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Flowable.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Observable.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Single.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Maybe.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Completable.html')) - fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/flowables/ConnectableFlowable.html')); - fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/observables/ConnectableObservable.html')); + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/flowables/ConnectableFlowable.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/observables/ConnectableObservable.html')) - fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/subjects/ReplaySubject.html')); - fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/processors/ReplayProcessor.html')); - fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/plugins/RxJavaPlugins.html')); + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/subjects/ReplaySubject.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/processors/ReplayProcessor.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/plugins/RxJavaPlugins.html')) - fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/parallel/ParallelFlowable.html')); + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/parallel/ParallelFlowable.html')) } def fixJavadocFile(file) { - println("Cleaning up: " + file); + logger.lifecycle("Cleaning up: " + file) String fileContents = file.getText('UTF-8') // lots of spaces after the previous method argument - fileContents = fileContents.replaceAll(",\\s{4,}", ",\n "); + fileContents = fileContents.replaceAll(",\\s{4,}", ",\n ") // lots of spaces after the @NonNull annotations - fileContents = fileContents.replaceAll("@NonNull\\s{4,}", "@NonNull "); + fileContents = fileContents.replaceAll("@NonNull\\s{4,}", "@NonNull ") // lots of spaces after the @Nullable annotations - fileContents = fileContents.replaceAll("@Nullable\\s{4,}", "@Nullable "); + fileContents = fileContents.replaceAll("@Nullable\\s{4,}", "@Nullable ") // javadoc bug: duplicates the link to @NonNull for some reason - def nonNullText1 = "@NonNull"; - - fileContents = fileContents.replace(nonNullText1 + " " + nonNullText1, nonNullText1); - fileContents = fileContents.replace(nonNullText1 + "\n " + nonNullText1, nonNullText1); - fileContents = fileContents.replace(nonNullText1 + "\r\n " + nonNullText1, nonNullText1); + def nonNullText1 = "@NonNull" - def nonNullText2 = "@NonNull"; - fileContents = fileContents.replace(nonNullText2 + " " + nonNullText2, nonNullText2); - fileContents = fileContents.replace(nonNullText2 + "\n " + nonNullText2, nonNullText2); - fileContents = fileContents.replace(nonNullText2 + "\r\n " + nonNullText2, nonNullText2); + fileContents = fileContents.replace(nonNullText1 + " " + nonNullText1, nonNullText1) + fileContents = fileContents.replace(nonNullText1 + "\n " + nonNullText1, nonNullText1) + fileContents = fileContents.replace(nonNullText1 + "\r\n " + nonNullText1, nonNullText1) + + def nonNullText2 = "@NonNull" + fileContents = fileContents.replace(nonNullText2 + " " + nonNullText2, nonNullText2) + fileContents = fileContents.replace(nonNullText2 + "\n " + nonNullText2, nonNullText2) + fileContents = fileContents.replace(nonNullText2 + "\r\n " + nonNullText2, nonNullText2) // javadoc bug: duplicates the link to @Nullable for some reason - def nullableText1 = "@Nullable"; - - fileContents = fileContents.replace(nullableText1 + " " + nullableText1, nullableText1); - fileContents = fileContents.replace(nullableText1 + "\n " + nullableText1, nullableText1); - fileContents = fileContents.replace(nullableText1 + "\r\n " + nullableText1, nullableText1); - - def nullableText2 = "@Nullable"; - - fileContents = fileContents.replace(nullableText2 + " " + nullableText2, nullableText2); - fileContents = fileContents.replace(nullableText2 + "\n " + nullableText2, nullableText2); - fileContents = fileContents.replace(nullableText2 + "\r\n " + nullableText2, nullableText2); - - file.setText(fileContents, 'UTF-8'); -} + def nullableText1 = "@Nullable" + + fileContents = fileContents.replace(nullableText1 + " " + nullableText1, nullableText1) + fileContents = fileContents.replace(nullableText1 + "\n " + nullableText1, nullableText1) + fileContents = fileContents.replace(nullableText1 + "\r\n " + nullableText1, nullableText1) + def nullableText2 = "@Nullable" + + fileContents = fileContents.replace(nullableText2 + " " + nullableText2, nullableText2) + fileContents = fileContents.replace(nullableText2 + "\n " + nullableText2, nullableText2) + fileContents = fileContents.replace(nullableText2 + "\r\n " + nullableText2, nullableText2) + + file.setText(fileContents, 'UTF-8') +} From a5dad0c66a9fab7685efbcc504d64b3c6d80330f Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Fri, 7 May 2021 06:10:41 -0700 Subject: [PATCH 174/521] 3.x: Consolidate test logging (#7263) * Consolidate test logging Move test logging to consolidated block. Change all 'rootProject' to 'project'. * Only do parallel testing on non-CI machines --- build.gradle | 67 +++++++++++++++++++++------------------------------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/build.gradle b/build.gradle index 329e8347fe..535aefc268 100644 --- a/build.gradle +++ b/build.gradle @@ -71,7 +71,7 @@ javadoc { options.addStringOption("top").value = "" options.addStringOption("doctitle").value = "" options.addStringOption("header").value = "" - options.stylesheetFile = rootProject.file("gradle/stylesheet.css") + options.stylesheetFile = project.file("gradle/stylesheet.css") options.links( "https://docs.oracle.com/javase/8/docs/api/", @@ -99,7 +99,7 @@ jar { } license { - header rootProject.file("config/license/HEADER") + header project.file("config/license/HEADER") ext.year = Calendar.getInstance().get(Calendar.YEAR) skipExistingHeaders true ignoreFailures true @@ -120,51 +120,38 @@ jmh { } test { + maxHeapSize = "1200m" +} + +task testNG(type: Test) { + useTestNG() +} + +check.dependsOn testNG + +tasks.withType(Test) { testLogging { - // showing skipped occasionally should prevent CI timeout due to lack of standard output - events=["skipped", "failed"] // "started", "passed" - // showStandardStreams = true - exceptionFormat="full" + events = ["skipped", "failed"] + exceptionFormat = "full" debug.events = ["skipped", "failed"] - debug.exceptionFormat="full" + debug.exceptionFormat = "full" info.events = ["failed", "skipped"] - info.exceptionFormat="full" - + info.exceptionFormat = "full" + warn.events = ["failed", "skipped"] - warn.exceptionFormat="full" + warn.exceptionFormat = "full" } - maxHeapSize = "1200m" - if (System.getenv("CI") == null) { maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 } } -task testng(type: Test) { - useTestNG() - testLogging { - events=["skipped", "failed"] - exceptionFormat="full" - - debug.events = ["skipped", "failed"] - debug.exceptionFormat="full" - - info.events = ["failed", "skipped"] - info.exceptionFormat="full" - - warn.events = ["failed", "skipped"] - warn.exceptionFormat="full" - } -} - -check.dependsOn testng - jacocoTestReport { dependsOn test - dependsOn testng + dependsOn testNG reports { xml.enabled = true @@ -175,18 +162,18 @@ jacocoTestReport { check.dependsOn jacocoTestReport checkstyle { - configFile = rootProject.file("config/checkstyle/checkstyle.xml") + configFile = project.file("config/checkstyle/checkstyle.xml") configProperties = [ - "checkstyle.suppressions.file": rootProject.file("config/checkstyle/suppressions.xml"), - "checkstyle.header.file": rootProject.file("config/license/HEADER_JAVA") + "checkstyle.suppressions.file": project.file("config/checkstyle/suppressions.xml"), + "checkstyle.header.file": project.file("config/license/HEADER_JAVA") ] } -if (rootProject.hasProperty("releaseMode")) { - logger.lifecycle("ReleaseMode: {}", rootProject.releaseMode) +if (project.hasProperty("releaseMode")) { + logger.lifecycle("ReleaseMode: {}", project.releaseMode) + + if ("branch" == project.releaseMode) { - if ("branch".equals(rootProject.releaseMode)) { - if (version.endsWith("-SNAPSHOT")) { publishing { repositories { @@ -204,7 +191,7 @@ if (rootProject.hasProperty("releaseMode")) { } } - if ("full".equals(rootProject.releaseMode)) { + if ("full" == project.releaseMode) { signing { if (project.hasProperty("SIGNING_PRIVATE_KEY") && project.hasProperty("SIGNING_PASSWORD")) { useInMemoryPgpKeys(project.getProperty("SIGNING_PRIVATE_KEY"), project.getProperty("SIGNING_PASSWORD")) From aeb5e683b6474482ce540f03cea73d22c3af25f8 Mon Sep 17 00:00:00 2001 From: benjamintboyle Date: Sat, 8 May 2021 14:06:32 -0700 Subject: [PATCH 175/521] Fix minor formatting issues in build.gradle (#7265) --- build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 535aefc268..c47fbdd8f7 100644 --- a/build.gradle +++ b/build.gradle @@ -74,8 +74,8 @@ javadoc { options.stylesheetFile = project.file("gradle/stylesheet.css") options.links( - "https://docs.oracle.com/javase/8/docs/api/", - "http://www.reactive-streams.org/reactive-streams-$%7BreactiveStreamsVersion%7D-javadoc/" + "https://docs.oracle.com/javase/8/docs/api/", + "http://www.reactive-streams.org/reactive-streams-$%7BreactiveStreamsVersion%7D-javadoc/" ) finalizedBy javadocCleanup @@ -124,13 +124,13 @@ test { } task testNG(type: Test) { - useTestNG() + useTestNG() } check.dependsOn testNG tasks.withType(Test) { - testLogging { + testLogging { events = ["skipped", "failed"] exceptionFormat = "full" @@ -165,7 +165,7 @@ checkstyle { configFile = project.file("config/checkstyle/checkstyle.xml") configProperties = [ "checkstyle.suppressions.file": project.file("config/checkstyle/suppressions.xml"), - "checkstyle.header.file": project.file("config/license/HEADER_JAVA") + "checkstyle.header.file" : project.file("config/license/HEADER_JAVA") ] } From f92590088decda924da7dd2922059b8825c3cb09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 May 2021 09:14:33 +0200 Subject: [PATCH 176/521] Bump gradle/wrapper-validation-action from 1 to 1.0.3 (#7268) Bumps [gradle/wrapper-validation-action](https://github.com/gradle/wrapper-validation-action) from 1 to 1.0.3. - [Release notes](https://github.com/gradle/wrapper-validation-action/releases) - [Commits](https://github.com/gradle/wrapper-validation-action/compare/v1...v1.0.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gradle-wrapper-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 405a2b3065..b7256f2ec9 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -7,4 +7,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: gradle/wrapper-validation-action@v1 + - uses: gradle/wrapper-validation-action@v1.0.3 From c3f5e3999512e6455c1a8ede6434793f9c56de9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 May 2021 09:41:06 +0200 Subject: [PATCH 177/521] Bump codecov/codecov-action from 1 to 1.5.0 (#7270) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 1 to 1.5.0. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v1...v1.5.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gradle_branch.yml | 2 +- .github/workflows/gradle_pr.yml | 2 +- .github/workflows/gradle_release.yml | 2 +- .github/workflows/gradle_snapshot.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml index f0454433b3..952a2b89c0 100644 --- a/.github/workflows/gradle_branch.yml +++ b/.github/workflows/gradle_branch.yml @@ -29,6 +29,6 @@ jobs: - name: Build RxJava run: ./gradlew build --stacktrace - name: Upload to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v1.5.0 - name: Generate Javadoc run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index dbf3d6f1ca..962203d3df 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -29,6 +29,6 @@ jobs: - name: Build RxJava run: ./gradlew build --stacktrace - name: Upload to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v1.5.0 - name: Generate Javadoc run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index bbce6f6b43..15503e17b6 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -38,7 +38,7 @@ jobs: - name: Build RxJava run: ./gradlew build --stacktrace --no-daemon - name: Upload to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v1.5.0 - name: Upload release run: ./gradlew -PreleaseMode=full uploadArchives --no-daemon --no-parallel --stacktrace env: diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index d0d0ddea2c..6ca347425f 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -41,7 +41,7 @@ jobs: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USER }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} - name: Upload to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v1.5.0 - name: Push Javadoc run: ./push_javadoc.sh # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions From fc11bd33a7a9dd722750f7998e5c94edf292a238 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 May 2021 09:56:29 +0200 Subject: [PATCH 178/521] Bump actions/checkout from 2 to 2.3.4 (#7269) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 2.3.4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v2.3.4) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gradle-wrapper-validation.yml | 2 +- .github/workflows/gradle_branch.yml | 2 +- .github/workflows/gradle_jdk11.yml | 2 +- .github/workflows/gradle_pr.yml | 2 +- .github/workflows/gradle_release.yml | 2 +- .github/workflows/gradle_snapshot.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index b7256f2ec9..792b87079d 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -6,5 +6,5 @@ jobs: name: "Validation" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2.3.4 - uses: gradle/wrapper-validation-action@v1.0.3 diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml index 952a2b89c0..3824624722 100644 --- a/.github/workflows/gradle_branch.yml +++ b/.github/workflows/gradle_branch.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2.3.4 - name: Set up JDK 8 uses: actions/setup-java@v2 with: diff --git a/.github/workflows/gradle_jdk11.yml b/.github/workflows/gradle_jdk11.yml index 13a89abd91..14c6951714 100644 --- a/.github/workflows/gradle_jdk11.yml +++ b/.github/workflows/gradle_jdk11.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2.3.4 - name: Set up JDK 11 uses: actions/setup-java@v2 with: diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index 962203d3df..583b09dab4 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2.3.4 - name: Set up JDK 8 uses: actions/setup-java@v2 with: diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 15503e17b6..10242b730f 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -17,7 +17,7 @@ jobs: env: CI_BUILD_NUMBER: ${{ github.run_number }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2.3.4 - name: Set up JDK 8 uses: actions/setup-java@v2 with: diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index 6ca347425f..934f3bde83 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -15,7 +15,7 @@ jobs: # ------------------------------------------------------------------------------ CI_BUILD_NUMBER: ${{ github.run_number }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2.3.4 - name: Set up JDK 8 uses: actions/setup-java@v2 with: From b7e2f19378dd7e07ae254ed5ce0ede3f9646ce99 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 12 May 2021 14:21:01 +0200 Subject: [PATCH 179/521] 3.x: Fix copy-paste error in Completable javadoc (#7272) --- src/main/java/io/reactivex/rxjava3/core/Completable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Completable.java b/src/main/java/io/reactivex/rxjava3/core/Completable.java index 805cd8f63f..05b3f8b240 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Completable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Completable.java @@ -52,7 +52,7 @@ * Note that as with the {@code Observable} protocol, {@code onError} and {@code onComplete} are mutually exclusive events. *

* Like {@code Observable}, a running {@code Completable} can be stopped through the {@link Disposable} instance - * provided to consumers through {@link SingleObserver#onSubscribe}. + * provided to consumers through {@link CompletableObserver#onSubscribe}. *

* Like an {@code Observable}, a {@code Completable} is lazy, can be either "hot" or "cold", synchronous or * asynchronous. {@code Completable} instances returned by the methods of this class are cold From 3ba64ceb023614836f09961c8a0f3f94cbfcc2ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 May 2021 09:10:10 +0200 Subject: [PATCH 180/521] Bump mockito-core from 3.9.0 to 3.10.0 (#7273) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.9.0 to 3.10.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.9.0...v3.10.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c47fbdd8f7..a7d5418d75 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ ext { reactiveStreamsVersion = "1.0.3" junitVersion = "4.13.2" testNgVersion = "7.4.0" - mockitoVersion = "3.9.0" + mockitoVersion = "3.10.0" jmhLibVersion = "1.21" guavaVersion = "30.1.1-jre" } From 771616c401061f989bed17ce441e60791b3cf7ea Mon Sep 17 00:00:00 2001 From: David Karnok Date: Fri, 14 May 2021 22:08:20 +0200 Subject: [PATCH 181/521] 3.x: Javadoc: Fix wording of *OnSubscribe interfaces (#7274) --- .../java/io/reactivex/rxjava3/core/CompletableOnSubscribe.java | 2 +- .../java/io/reactivex/rxjava3/core/FlowableOnSubscribe.java | 2 +- src/main/java/io/reactivex/rxjava3/core/MaybeOnSubscribe.java | 2 +- .../java/io/reactivex/rxjava3/core/ObservableOnSubscribe.java | 2 +- src/main/java/io/reactivex/rxjava3/core/SingleOnSubscribe.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/CompletableOnSubscribe.java b/src/main/java/io/reactivex/rxjava3/core/CompletableOnSubscribe.java index 0ac107e64b..e73fae2d5c 100644 --- a/src/main/java/io/reactivex/rxjava3/core/CompletableOnSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/core/CompletableOnSubscribe.java @@ -17,7 +17,7 @@ /** * A functional interface that has a {@code subscribe()} method that receives - * an instance of a {@link CompletableEmitter} instance that allows pushing + * a {@link CompletableEmitter} instance that allows pushing * an event in a cancellation-safe manner. */ @FunctionalInterface diff --git a/src/main/java/io/reactivex/rxjava3/core/FlowableOnSubscribe.java b/src/main/java/io/reactivex/rxjava3/core/FlowableOnSubscribe.java index c289a9e30b..6c5263b779 100644 --- a/src/main/java/io/reactivex/rxjava3/core/FlowableOnSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/core/FlowableOnSubscribe.java @@ -17,7 +17,7 @@ /** * A functional interface that has a {@code subscribe()} method that receives - * an instance of a {@link FlowableEmitter} instance that allows pushing + * a {@link FlowableEmitter} instance that allows pushing * events in a backpressure-safe and cancellation-safe manner. * * @param the value type pushed diff --git a/src/main/java/io/reactivex/rxjava3/core/MaybeOnSubscribe.java b/src/main/java/io/reactivex/rxjava3/core/MaybeOnSubscribe.java index 6c235bac84..67994d3d40 100644 --- a/src/main/java/io/reactivex/rxjava3/core/MaybeOnSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/core/MaybeOnSubscribe.java @@ -17,7 +17,7 @@ /** * A functional interface that has a {@code subscribe()} method that receives - * an instance of a {@link MaybeEmitter} instance that allows pushing + * a {@link MaybeEmitter} instance that allows pushing * an event in a cancellation-safe manner. * * @param the value type pushed diff --git a/src/main/java/io/reactivex/rxjava3/core/ObservableOnSubscribe.java b/src/main/java/io/reactivex/rxjava3/core/ObservableOnSubscribe.java index 587ab9372c..056441620a 100644 --- a/src/main/java/io/reactivex/rxjava3/core/ObservableOnSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/core/ObservableOnSubscribe.java @@ -17,7 +17,7 @@ /** * A functional interface that has a {@code subscribe()} method that receives - * an instance of an {@link ObservableEmitter} instance that allows pushing + * an {@link ObservableEmitter} instance that allows pushing * events in a cancellation-safe manner. * * @param the value type pushed diff --git a/src/main/java/io/reactivex/rxjava3/core/SingleOnSubscribe.java b/src/main/java/io/reactivex/rxjava3/core/SingleOnSubscribe.java index 9372cb5bdf..e8b9e89f8b 100644 --- a/src/main/java/io/reactivex/rxjava3/core/SingleOnSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/core/SingleOnSubscribe.java @@ -17,7 +17,7 @@ /** * A functional interface that has a {@code subscribe()} method that receives - * an instance of a {@link SingleEmitter} instance that allows pushing + * a {@link SingleEmitter} instance that allows pushing * an event in a cancellation-safe manner. * * @param the value type pushed From 1d7e8acb3e1fa035a30248a9195a35c7537200b7 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Thu, 27 May 2021 15:38:19 +0900 Subject: [PATCH 182/521] 3.x: fix typo in ObservableRetryTest.java (#7277) seperate -> separate --- .../internal/operators/observable/ObservableRetryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryTest.java index 285299b83d..13f19fb547 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryTest.java @@ -572,7 +572,7 @@ public void run() { } } - /** Observer for listener on seperate thread. */ + /** Observer for listener on separate thread. */ static final class AsyncObserver extends DefaultObserver { protected CountDownLatch latch = new CountDownLatch(1); From 327fa44fc7bc93e87057bf14f7be37b7a3e496ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 May 2021 09:08:49 +0200 Subject: [PATCH 183/521] Bump actions/cache from 2.1.5 to 2.1.6 (#7278) Bumps [actions/cache](https://github.com/actions/cache) from 2.1.5 to 2.1.6. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v2.1.5...v2.1.6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gradle_branch.yml | 2 +- .github/workflows/gradle_jdk11.yml | 2 +- .github/workflows/gradle_pr.yml | 2 +- .github/workflows/gradle_release.yml | 2 +- .github/workflows/gradle_snapshot.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml index 3824624722..a0d99b4ade 100644 --- a/.github/workflows/gradle_branch.yml +++ b/.github/workflows/gradle_branch.yml @@ -19,7 +19,7 @@ jobs: distribution: 'adopt' java-version: '8' - name: Cache Gradle packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_jdk11.yml b/.github/workflows/gradle_jdk11.yml index 14c6951714..9b30f8da56 100644 --- a/.github/workflows/gradle_jdk11.yml +++ b/.github/workflows/gradle_jdk11.yml @@ -21,7 +21,7 @@ jobs: distribution: 'adopt' java-version: '11' - name: Cache Gradle packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-1-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index 583b09dab4..7490665ca4 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -19,7 +19,7 @@ jobs: distribution: 'adopt' java-version: '8' - name: Cache Gradle packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-1-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 10242b730f..9b15018c13 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -24,7 +24,7 @@ jobs: distribution: 'adopt' java-version: '8' - name: Cache Gradle packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/*.gradle') }} diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index 934f3bde83..ca58fa2e5f 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -22,7 +22,7 @@ jobs: distribution: 'adopt' java-version: '8' - name: Cache Gradle packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/*.gradle') }} From 47c599cb7f976f147b53a32482fb853ecf875da7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 May 2021 10:58:59 +0200 Subject: [PATCH 184/521] Bump gradle/wrapper-validation-action from 1.0.3 to 1.0.4 (#7279) Bumps [gradle/wrapper-validation-action](https://github.com/gradle/wrapper-validation-action) from 1.0.3 to 1.0.4. - [Release notes](https://github.com/gradle/wrapper-validation-action/releases) - [Commits](https://github.com/gradle/wrapper-validation-action/compare/v1.0.3...v1.0.4) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gradle-wrapper-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 792b87079d..184acdb502 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -7,4 +7,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2.3.4 - - uses: gradle/wrapper-validation-action@v1.0.3 + - uses: gradle/wrapper-validation-action@v1.0.4 From 0df952e007814da9f2d4566097676590b977c708 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Jun 2021 09:37:53 +0200 Subject: [PATCH 185/521] Bump mockito-core from 3.10.0 to 3.11.0 (#7280) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.10.0 to 3.11.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.10.0...v3.11.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a7d5418d75..1fb0f2ee22 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ ext { reactiveStreamsVersion = "1.0.3" junitVersion = "4.13.2" testNgVersion = "7.4.0" - mockitoVersion = "3.10.0" + mockitoVersion = "3.11.0" jmhLibVersion = "1.21" guavaVersion = "30.1.1-jre" } From e38337779936d053b86001a0d5a98bccb83341f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 08:47:43 +0200 Subject: [PATCH 186/521] Bump codecov/codecov-action from 1.5.0 to 1.5.2 (#7282) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 1.5.0 to 1.5.2. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v1.5.0...v1.5.2) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gradle_branch.yml | 2 +- .github/workflows/gradle_pr.yml | 2 +- .github/workflows/gradle_release.yml | 2 +- .github/workflows/gradle_snapshot.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml index a0d99b4ade..27a45e5ccc 100644 --- a/.github/workflows/gradle_branch.yml +++ b/.github/workflows/gradle_branch.yml @@ -29,6 +29,6 @@ jobs: - name: Build RxJava run: ./gradlew build --stacktrace - name: Upload to Codecov - uses: codecov/codecov-action@v1.5.0 + uses: codecov/codecov-action@v1.5.2 - name: Generate Javadoc run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index 7490665ca4..dbc5418a92 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -29,6 +29,6 @@ jobs: - name: Build RxJava run: ./gradlew build --stacktrace - name: Upload to Codecov - uses: codecov/codecov-action@v1.5.0 + uses: codecov/codecov-action@v1.5.2 - name: Generate Javadoc run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 9b15018c13..d25da22464 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -38,7 +38,7 @@ jobs: - name: Build RxJava run: ./gradlew build --stacktrace --no-daemon - name: Upload to Codecov - uses: codecov/codecov-action@v1.5.0 + uses: codecov/codecov-action@v1.5.2 - name: Upload release run: ./gradlew -PreleaseMode=full uploadArchives --no-daemon --no-parallel --stacktrace env: diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index ca58fa2e5f..76b402cf15 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -41,7 +41,7 @@ jobs: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USER }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} - name: Upload to Codecov - uses: codecov/codecov-action@v1.5.0 + uses: codecov/codecov-action@v1.5.2 - name: Push Javadoc run: ./push_javadoc.sh # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions From 17366435ba5df7f37b97ad50e38748bf3917ff9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 10:13:32 +0200 Subject: [PATCH 187/521] Bump mockito-core from 3.11.0 to 3.11.1 (#7283) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.11.0 to 3.11.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.11.0...v3.11.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1fb0f2ee22..bd5035b4a5 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ ext { reactiveStreamsVersion = "1.0.3" junitVersion = "4.13.2" testNgVersion = "7.4.0" - mockitoVersion = "3.11.0" + mockitoVersion = "3.11.1" jmhLibVersion = "1.21" guavaVersion = "30.1.1-jre" } From df0dd4c9cf5955ae8519545482a36b45519609cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jun 2021 10:28:30 +0200 Subject: [PATCH 188/521] Bump com.vanniktech.maven.publish from 0.15.1 to 0.16.0 (#7285) Bumps [com.vanniktech.maven.publish](https://github.com/vanniktech/gradle-maven-publish-plugin) from 0.15.1 to 0.16.0. - [Release notes](https://github.com/vanniktech/gradle-maven-publish-plugin/releases) - [Changelog](https://github.com/vanniktech/gradle-maven-publish-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/vanniktech/gradle-maven-publish-plugin/compare/0.15.1...0.16.0) --- updated-dependencies: - dependency-name: com.vanniktech.maven.publish dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index bd5035b4a5..9fd4473214 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { id("me.champeau.gradle.jmh") version "0.5.3" id("com.github.hierynomus.license") version "0.16.1" id("biz.aQute.bnd.builder") version "5.3.0" - id("com.vanniktech.maven.publish") version "0.15.1" + id("com.vanniktech.maven.publish") version "0.16.0" } ext { From 578b4f880abd64400a5c64914ef668d20d580983 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Mon, 21 Jun 2021 10:55:45 +0200 Subject: [PATCH 189/521] Rollback the publish plugin --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9fd4473214..bd5035b4a5 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { id("me.champeau.gradle.jmh") version "0.5.3" id("com.github.hierynomus.license") version "0.16.1" id("biz.aQute.bnd.builder") version "5.3.0" - id("com.vanniktech.maven.publish") version "0.16.0" + id("com.vanniktech.maven.publish") version "0.15.1" } ext { From 91a10d46ff993aba749c393035d402d607bd7424 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jun 2021 08:08:53 +0200 Subject: [PATCH 190/521] Bump mockito-core from 3.11.1 to 3.11.2 (#7287) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.11.1 to 3.11.2. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.11.1...v3.11.2) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index bd5035b4a5..dcc510846b 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ ext { reactiveStreamsVersion = "1.0.3" junitVersion = "4.13.2" testNgVersion = "7.4.0" - mockitoVersion = "3.11.1" + mockitoVersion = "3.11.2" jmhLibVersion = "1.21" guavaVersion = "30.1.1-jre" } From 4749ab3879dcfb4afcf84edcade6141a7221c194 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Mon, 5 Jul 2021 13:27:48 +0200 Subject: [PATCH 191/521] 3.x: Update withLatestFrom doc about upstream early complete (#7289) * 3.x: Update withLatestFrom doc about upstream early complete * Fix singular wording --- .../io/reactivex/rxjava3/core/Flowable.java | 17 +++++++++++++++++ .../io/reactivex/rxjava3/core/Observable.java | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 5a6579fb34..a9f7b0a3b6 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -19233,6 +19233,13 @@ public final Flowable> window( /** * Merges the specified {@link Publisher} into the current {@code Flowable} sequence by using the {@code resultSelector} * function only when the current {@code Flowable} (this instance) emits an item. + * + *

Note that this operator doesn't emit anything until the other source has produced at + * least one value. The resulting emission only happens when the current {@code Flowable} emits (and + * not when the other source emits, unlike combineLatest). + * If the other source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before the other source has produced at least one value, the sequence completes + * without emission. *

* * @@ -19277,6 +19284,8 @@ public final Flowable> window( * least one value. The resulting emission only happens when the current {@code Flowable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. * *

*
Backpressure:
@@ -19317,6 +19326,8 @@ public final Flowable> window( * least one value. The resulting emission only happens when the current {@code Flowable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. * *
*
Backpressure:
@@ -19362,6 +19373,8 @@ public final Flowable> window( * least one value. The resulting emission only happens when the current {@code Flowable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. * *
*
Backpressure:
@@ -19411,6 +19424,8 @@ public final Flowable> window( * least one value. The resulting emission only happens when the current {@code Flowable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. * *
*
Backpressure:
@@ -19445,6 +19460,8 @@ public final Flowable> window( * least one value. The resulting emission only happens when the current {@code Flowable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. * *
*
Backpressure:
diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index 3e343bf248..27d46ad8c5 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -16074,6 +16074,13 @@ public final Observable> window( /** * Merges the specified {@link ObservableSource} into the current {@code Observable} sequence by using the {@code resultSelector} * function only when the current {@code Observable} emits an item. + * + *

Note that this operator doesn't emit anything until the other source has produced at + * least one value. The resulting emission only happens when the current {@code Observable} emits (and + * not when the other source emits, unlike combineLatest). + * If the other source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before the other source has produced at least one value, the sequence completes + * without emission. *

* * @@ -16112,6 +16119,8 @@ public final Observable withLatestFrom(@NonNull ObservableSource * *

@@ -16150,6 +16159,8 @@ public final Observable withLatestFrom( * least one value. The resulting emission only happens when the current {@code Observable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. *

* *

@@ -16192,6 +16203,8 @@ public final Observable withLatestFrom( * least one value. The resulting emission only happens when the current {@code Observable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. *

* *

@@ -16238,6 +16251,8 @@ public final Observable withLatestFrom( * least one value. The resulting emission only happens when the current {@code Observable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. *

* *

@@ -16269,6 +16284,8 @@ public final Observable withLatestFrom(@NonNull ObservableSource[] oth * least one value. The resulting emission only happens when the current {@code Observable} emits (and * not when any of the other sources emit, unlike {@code combineLatest}). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. *

* *

From 3ba0ce4779717c41de563a8c655091813d6dc423 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Jul 2021 08:36:50 +0200 Subject: [PATCH 192/521] Bump com.vanniktech.maven.publish from 0.15.1 to 0.17.0 (#7290) Bumps [com.vanniktech.maven.publish](https://github.com/vanniktech/gradle-maven-publish-plugin) from 0.15.1 to 0.17.0. - [Release notes](https://github.com/vanniktech/gradle-maven-publish-plugin/releases) - [Changelog](https://github.com/vanniktech/gradle-maven-publish-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/vanniktech/gradle-maven-publish-plugin/compare/0.15.1...0.17.0) --- updated-dependencies: - dependency-name: com.vanniktech.maven.publish dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index dcc510846b..af6102f1c9 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { id("me.champeau.gradle.jmh") version "0.5.3" id("com.github.hierynomus.license") version "0.16.1" id("biz.aQute.bnd.builder") version "5.3.0" - id("com.vanniktech.maven.publish") version "0.15.1" + id("com.vanniktech.maven.publish") version "0.17.0" } ext { From 828afc786bde58b3d26d8064baaa40b1d2025491 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 7 Jul 2021 08:56:59 +0200 Subject: [PATCH 193/521] Fix 0.17.0 plugin config https://github.com/vanniktech/gradle-maven-publish-plugin/issues/275#issuecomment-864967048 --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index af6102f1c9..367a9b3678 100644 --- a/build.gradle +++ b/build.gradle @@ -172,6 +172,7 @@ checkstyle { if (project.hasProperty("releaseMode")) { logger.lifecycle("ReleaseMode: {}", project.releaseMode) + /* if ("branch" == project.releaseMode) { if (version.endsWith("-SNAPSHOT")) { @@ -190,6 +191,7 @@ if (project.hasProperty("releaseMode")) { } } } + */ if ("full" == project.releaseMode) { signing { @@ -197,10 +199,12 @@ if (project.hasProperty("releaseMode")) { useInMemoryPgpKeys(project.getProperty("SIGNING_PRIVATE_KEY"), project.getProperty("SIGNING_PASSWORD")) } } + /* mavenPublish { nexus { stagingProfile = "io.reactivex" } } + */ } } From 9ecf8ab36362f2e664f31ed7b9938e028b860140 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 7 Jul 2021 09:17:25 +0200 Subject: [PATCH 194/521] Update gradle_snapshot.yml release plugin now uses the "publish" command --- .github/workflows/gradle_snapshot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index 76b402cf15..ed101d8536 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -34,7 +34,7 @@ jobs: - name: Build RxJava run: ./gradlew build --stacktrace --no-daemon - name: Upload Snapshot - run: ./gradlew -PreleaseMode=branch uploadArchives --no-daemon --no-parallel --stacktrace + run: ./gradlew -PreleaseMode=branch publish --no-daemon --no-parallel --stacktrace env: # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions # ------------------------------------------------------------------------------ From c5883dcb390ebb17a484871038e37078e210052a Mon Sep 17 00:00:00 2001 From: David Karnok Date: Wed, 7 Jul 2021 09:23:29 +0200 Subject: [PATCH 195/521] Update gradle_release.yml Release plugin 0.17.0 uses "publish" --- .github/workflows/gradle_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index d25da22464..81962b6028 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -40,7 +40,7 @@ jobs: - name: Upload to Codecov uses: codecov/codecov-action@v1.5.2 - name: Upload release - run: ./gradlew -PreleaseMode=full uploadArchives --no-daemon --no-parallel --stacktrace + run: ./gradlew -PreleaseMode=full publish --no-daemon --no-parallel --stacktrace env: # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions # ------------------------------------------------------------------------------ From 333094355a19a6fb1b9f88d30bb68534504fac3c Mon Sep 17 00:00:00 2001 From: Christos Gkekas Date: Fri, 16 Jul 2021 07:32:35 +0100 Subject: [PATCH 196/521] Removes the purge thread in favor of standard ScheduledThreadPoolExecutor APIs (#7293) The RXSchedulerPurge thread is currently enabled by default and runs every second to call purge() on each executor in the pool. This is causing significant issues for low powered devices (e.g. mobile phones), because it needs to periodically wake up the CPU to perform purging. The RXSchedulerPurge thread could be completely removed in favor of using the standard setRemoveOnCancelPolicy() API on the ScheduledThreadPoolExecutor which became available in Java 7 and offers removal of cancelled tasks at the moment they are cancelled in O(1). --- .../schedulers/SchedulerPoolFactory.java | 99 +------------------ .../rxjava3/schedulers/Schedulers.java | 5 +- .../schedulers/SchedulerPoolFactoryTest.java | 86 ---------------- .../schedulers/ExecutorSchedulerTest.java | 2 +- 4 files changed, 4 insertions(+), 188 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java index 043569334c..515c2f2061 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java @@ -31,86 +31,11 @@ private SchedulerPoolFactory() { static final String PURGE_ENABLED_KEY = "rx3.purge-enabled"; - /** - * Indicates the periodic purging of the ScheduledExecutorService is enabled. - */ public static final boolean PURGE_ENABLED; - static final String PURGE_PERIOD_SECONDS_KEY = "rx3.purge-period-seconds"; - - /** - * Indicates the purge period of the ScheduledExecutorServices created by create(). - */ - public static final int PURGE_PERIOD_SECONDS; - - static final AtomicReference PURGE_THREAD = - new AtomicReference<>(); - - // Upcast to the Map interface here to avoid 8.x compatibility issues. - // See http://stackoverflow.com/a/32955708/61158 - static final Map POOLS = - new ConcurrentHashMap<>(); - - /** - * Starts the purge thread if not already started. - */ - public static void start() { - tryStart(PURGE_ENABLED); - } - - static void tryStart(boolean purgeEnabled) { - if (purgeEnabled) { - for (;;) { - ScheduledExecutorService curr = PURGE_THREAD.get(); - if (curr != null) { - return; - } - ScheduledExecutorService next = Executors.newScheduledThreadPool(1, new RxThreadFactory("RxSchedulerPurge")); - if (PURGE_THREAD.compareAndSet(curr, next)) { - - next.scheduleAtFixedRate(new ScheduledTask(), PURGE_PERIOD_SECONDS, PURGE_PERIOD_SECONDS, TimeUnit.SECONDS); - - return; - } else { - next.shutdownNow(); - } - } - } - } - - /** - * Stops the purge thread. - */ - public static void shutdown() { - ScheduledExecutorService exec = PURGE_THREAD.getAndSet(null); - if (exec != null) { - exec.shutdownNow(); - } - POOLS.clear(); - } - static { SystemPropertyAccessor propertyAccessor = new SystemPropertyAccessor(); PURGE_ENABLED = getBooleanProperty(true, PURGE_ENABLED_KEY, true, true, propertyAccessor); - PURGE_PERIOD_SECONDS = getIntProperty(PURGE_ENABLED, PURGE_PERIOD_SECONDS_KEY, 1, 1, propertyAccessor); - - start(); - } - - static int getIntProperty(boolean enabled, String key, int defaultNotFound, int defaultNotEnabled, Function propertyAccessor) { - if (enabled) { - try { - String value = propertyAccessor.apply(key); - if (value == null) { - return defaultNotFound; - } - return Integer.parseInt(value); - } catch (Throwable ex) { - Exceptions.throwIfFatal(ex); - return defaultNotFound; - } - } - return defaultNotEnabled; } static boolean getBooleanProperty(boolean enabled, String key, boolean defaultNotFound, boolean defaultNotEnabled, Function propertyAccessor) { @@ -142,28 +67,8 @@ public String apply(String t) { * @return the ScheduledExecutorService */ public static ScheduledExecutorService create(ThreadFactory factory) { - final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory); - tryPutIntoPool(PURGE_ENABLED, exec); + final ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1, factory); + exec.setRemoveOnCancelPolicy(PURGE_ENABLED); return exec; } - - static void tryPutIntoPool(boolean purgeEnabled, ScheduledExecutorService exec) { - if (purgeEnabled && exec instanceof ScheduledThreadPoolExecutor) { - ScheduledThreadPoolExecutor e = (ScheduledThreadPoolExecutor) exec; - POOLS.put(e, exec); - } - } - - static final class ScheduledTask implements Runnable { - @Override - public void run() { - for (ScheduledThreadPoolExecutor e : new ArrayList<>(POOLS.keySet())) { - if (e.isShutdown()) { - POOLS.remove(e); - } else { - e.purge(); - } - } - } - } } diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java index 0c0ecbdbe5..f5dbe6ce7c 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java @@ -38,8 +38,7 @@ *
  • {@code rx3.computation-priority} (int): sets the thread priority of the {@link #computation()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
  • *
  • {@code rx3.newthread-priority} (int): sets the thread priority of the {@link #newThread()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
  • *
  • {@code rx3.single-priority} (int): sets the thread priority of the {@link #single()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
  • - *
  • {@code rx3.purge-enabled} (boolean): enables periodic purging of all {@code Scheduler}'s backing thread pools, default is {@code false}
  • - *
  • {@code rx3.purge-period-seconds} (int): specifies the periodic purge interval of all {@code Scheduler}'s backing thread pools, default is 1 second
  • + *
  • {@code rx3.purge-enabled} (boolean): enables purging of all {@code Scheduler}'s backing thread pools, default is {@code true}
  • *
  • {@code rx3.scheduler.use-nanotime} (boolean): {@code true} instructs {@code Scheduler} to use {@link System#nanoTime()} for {@link Scheduler#now(TimeUnit)}, * instead of default {@link System#currentTimeMillis()} ({@code false})
  • * @@ -556,7 +555,6 @@ public static void shutdown() { newThread().shutdown(); single().shutdown(); trampoline().shutdown(); - SchedulerPoolFactory.shutdown(); } /** @@ -569,7 +567,6 @@ public static void start() { newThread().start(); single().start(); trampoline().start(); - SchedulerPoolFactory.start(); } static final class IOTask implements Supplier { diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java index dd5667ffbb..5d93dabc09 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java @@ -30,50 +30,6 @@ public void utilityClass() { TestHelper.checkUtilityClass(SchedulerPoolFactory.class); } - @Test - public void multiStartStop() { - SchedulerPoolFactory.shutdown(); - - SchedulerPoolFactory.shutdown(); - - SchedulerPoolFactory.tryStart(false); - - assertNull(SchedulerPoolFactory.PURGE_THREAD.get()); - - SchedulerPoolFactory.start(); - - // restart schedulers - Schedulers.shutdown(); - - Schedulers.start(); - } - - @Test - public void startRace() throws InterruptedException { - try { - for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - SchedulerPoolFactory.shutdown(); - - Runnable r1 = new Runnable() { - @Override - public void run() { - SchedulerPoolFactory.start(); - } - }; - - TestHelper.race(r1, r1); - } - - } finally { - // restart schedulers - Schedulers.shutdown(); - - Thread.sleep(200); - - Schedulers.start(); - } - } - @Test public void boolPropertiesDisabledReturnsDefaultDisabled() throws Throwable { assertTrue(SchedulerPoolFactory.getBooleanProperty(false, "key", false, true, failingPropertiesAccessor)); @@ -98,30 +54,6 @@ public void boolPropertiesReturnsValue() throws Throwable { assertFalse(SchedulerPoolFactory.getBooleanProperty(true, "false", false, true, Functions.identity())); } - @Test - public void intPropertiesDisabledReturnsDefaultDisabled() throws Throwable { - assertEquals(-1, SchedulerPoolFactory.getIntProperty(false, "key", 0, -1, failingPropertiesAccessor)); - assertEquals(-1, SchedulerPoolFactory.getIntProperty(false, "key", 1, -1, failingPropertiesAccessor)); - } - - @Test - public void intPropertiesEnabledMissingReturnsDefaultMissing() throws Throwable { - assertEquals(-1, SchedulerPoolFactory.getIntProperty(true, "key", -1, 0, missingPropertiesAccessor)); - assertEquals(-1, SchedulerPoolFactory.getIntProperty(true, "key", -1, 1, missingPropertiesAccessor)); - } - - @Test - public void intPropertiesFailureReturnsDefaultMissing() throws Throwable { - assertEquals(-1, SchedulerPoolFactory.getIntProperty(true, "key", -1, 0, failingPropertiesAccessor)); - assertEquals(-1, SchedulerPoolFactory.getIntProperty(true, "key", -1, 1, failingPropertiesAccessor)); - } - - @Test - public void intPropertiesReturnsValue() throws Throwable { - assertEquals(1, SchedulerPoolFactory.getIntProperty(true, "1", 0, 4, Functions.identity())); - assertEquals(2, SchedulerPoolFactory.getIntProperty(true, "2", 3, 5, Functions.identity())); - } - static final Function failingPropertiesAccessor = new Function() { @Override public String apply(String v) throws Throwable { @@ -135,22 +67,4 @@ public String apply(String v) throws Throwable { return null; } }; - - @Test - public void putIntoPoolNoPurge() { - int s = SchedulerPoolFactory.POOLS.size(); - - SchedulerPoolFactory.tryPutIntoPool(false, null); - - assertEquals(s, SchedulerPoolFactory.POOLS.size()); - } - - @Test - public void putIntoPoolNonThreadPool() { - int s = SchedulerPoolFactory.POOLS.size(); - - SchedulerPoolFactory.tryPutIntoPool(true, null); - - assertEquals(s, SchedulerPoolFactory.POOLS.size()); - } } diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java index c4ba94189c..bb3e759884 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java @@ -93,7 +93,7 @@ public void run() { System.out.println("Wait before second GC"); System.out.println("JDK 6 purge is N log N because it removes and shifts one by one"); - int t = (int)(n * Math.log(n) / 100) + SchedulerPoolFactory.PURGE_PERIOD_SECONDS * 1000; + int t = (int)(n * Math.log(n) / 100) + 1000; int sleepStep = 100; while (t > 0) { System.out.printf(" >> Waiting for purge: %.2f s remaining%n", t / 1000d); From a9e0a8ab68ef9e385e4a639bd70769ca42e6f21a Mon Sep 17 00:00:00 2001 From: David Karnok Date: Sat, 17 Jul 2021 09:13:09 +0200 Subject: [PATCH 197/521] 3.x: API promotions for 3.1.0 (#7296) --- src/main/java/io/reactivex/rxjava3/core/Flowable.java | 8 ++++---- .../internal/schedulers/SchedulerPoolFactory.java | 2 -- .../java/io/reactivex/rxjava3/plugins/RxJavaPlugins.java | 8 ++++---- .../io/reactivex/rxjava3/schedulers/TestScheduler.java | 9 ++++----- .../internal/schedulers/SchedulerPoolFactoryTest.java | 1 - 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index a9f7b0a3b6..804a057533 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -12725,15 +12725,15 @@ public final Flowable onBackpressureLatest() { *
    Scheduler:
    *
    {@code onBackpressureReduce} does not operate by default on a particular {@link Scheduler}.
    *
    + *

    History: 3.0.9 - experimental * @param reducer the bi-function to call when there is more than one non-emitted value to downstream, * the first argument of the bi-function is previous item and the second one is currently * emitting from upstream * @return the new {@code Flowable} instance * @throws NullPointerException if {@code reducer} is {@code null} - * @since 3.0.9 - experimental + * @since 3.1.0 * @see #onBackpressureReduce(Supplier, BiFunction) */ - @Experimental @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) @@ -12764,6 +12764,7 @@ public final Flowable onBackpressureReduce(@NonNull BiFunction reduc *

    Scheduler:
    *
    {@code onBackpressureReduce} does not operate by default on a particular {@link Scheduler}.
    *
    + *

    History: 3.0.9 - experimental * @param the aggregate type emitted when the downstream requests more items * @param supplier the factory to call to create new item of type R to pass it as the first argument to {@code reducer}. * It is called when previous returned value by {@code reducer} already sent to @@ -12774,9 +12775,8 @@ public final Flowable onBackpressureReduce(@NonNull BiFunction reduc * @return the new {@code Flowable} instance * @throws NullPointerException if {@code supplier} or {@code reducer} is {@code null} * @see #onBackpressureReduce(BiFunction) - * @since 3.0.9 - experimental + * @since 3.1.0 */ - @Experimental @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java index 515c2f2061..44a824a168 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java @@ -13,9 +13,7 @@ package io.reactivex.rxjava3.internal.schedulers; -import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; diff --git a/src/main/java/io/reactivex/rxjava3/plugins/RxJavaPlugins.java b/src/main/java/io/reactivex/rxjava3/plugins/RxJavaPlugins.java index c5b0797ba3..58cb730bf6 100644 --- a/src/main/java/io/reactivex/rxjava3/plugins/RxJavaPlugins.java +++ b/src/main/java/io/reactivex/rxjava3/plugins/RxJavaPlugins.java @@ -1153,11 +1153,11 @@ public static void setOnParallelAssembly(@Nullable FunctionHistory: 3.0.11 - experimental * @param handler the hook function to set, null allowed - * @since 3.0.11 - experimental + * @since 3.1.0 */ @SuppressWarnings("rawtypes") - @Experimental public static void setOnParallelSubscribe(@Nullable BiFunction handler) { if (lockdown) { throw new IllegalStateException("Plugins can't be changed anymore"); @@ -1167,11 +1167,11 @@ public static void setOnParallelSubscribe(@Nullable BiFunctionHistory: 3.0.11 - experimental * @return the hook function, may be null - * @since 3.0.11 - experimental + * @since 3.1.0 */ @SuppressWarnings("rawtypes") - @Experimental @Nullable public static BiFunction getOnParallelSubscribe() { return onParallelSubscribe; diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java b/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java index a9aa3285f5..33aca58a48 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java @@ -52,12 +52,12 @@ public TestScheduler() { /** * Creates a new TestScheduler with the option to use the * {@link RxJavaPlugins#onSchedule(Runnable)} hook when scheduling tasks. + *

    History: 3.0.10 - experimental * @param useOnScheduleHook if {@code true}, the tasks submitted to this * TestScheduler is wrapped via the * {@link RxJavaPlugins#onSchedule(Runnable)} hook - * @since 3.0.10 - experimental + * @since 3.1.0 */ - @Experimental public TestScheduler(boolean useOnScheduleHook) { this.useOnScheduleHook = useOnScheduleHook; } @@ -78,7 +78,7 @@ public TestScheduler(long delayTime, TimeUnit unit) { * Creates a new TestScheduler with the specified initial virtual time * and with the option to use the * {@link RxJavaPlugins#onSchedule(Runnable)} hook when scheduling tasks. - * + *

    History: 3.0.10 - experimental * @param delayTime * the point in time to move the Scheduler's clock to * @param unit @@ -86,9 +86,8 @@ public TestScheduler(long delayTime, TimeUnit unit) { * @param useOnScheduleHook if {@code true}, the tasks submitted to this * TestScheduler is wrapped via the * {@link RxJavaPlugins#onSchedule(Runnable)} hook - * @since 3.0.10 - experimental + * @since 3.1.0 */ - @Experimental public TestScheduler(long delayTime, TimeUnit unit, boolean useOnScheduleHook) { time = unit.toNanos(delayTime); this.useOnScheduleHook = useOnScheduleHook; diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java index 5d93dabc09..12afa708c4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java @@ -20,7 +20,6 @@ import io.reactivex.rxjava3.core.RxJavaTest; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.TestHelper; public class SchedulerPoolFactoryTest extends RxJavaTest { From 66fd701417563fd21c24c177447c332191931dc4 Mon Sep 17 00:00:00 2001 From: David Karnok Date: Sat, 17 Jul 2021 12:53:55 +0200 Subject: [PATCH 198/521] 3.x: Add subscribe with disposable container (#7298) --- .../reactivex/rxjava3/core/Completable.java | 45 ++- .../io/reactivex/rxjava3/core/Flowable.java | 53 ++- .../java/io/reactivex/rxjava3/core/Maybe.java | 49 ++- .../io/reactivex/rxjava3/core/Observable.java | 49 ++- .../io/reactivex/rxjava3/core/Single.java | 48 ++- .../AbstractDisposableAutoRelease.java | 125 +++++++ .../DisposableAutoReleaseMultiObserver.java | 78 +++++ .../DisposableAutoReleaseObserver.java | 76 ++++ .../DisposableAutoReleaseSubscriber.java | 151 ++++++++ .../observers/CompletableConsumersTest.java | 221 ++++++++++++ .../observers/MaybeConsumersTest.java | 273 +++++++++++++++ .../observers/ObservableConsumersTest.java | 324 +++++++++++++++++ .../observers/SingleConsumersTest.java | 192 ++++++++++ .../subscribers/FlowableConsumersTest.java | 328 ++++++++++++++++++ .../validators/BaseTypeAnnotations.java | 4 +- .../validators/JavadocForAnnotations.java | 2 +- .../rxjava3/validators/JavadocWording.java | 1 + .../ParamValidationCheckerTest.java | 4 +- 18 files changed, 2009 insertions(+), 14 deletions(-) create mode 100644 src/main/java/io/reactivex/rxjava3/internal/observers/AbstractDisposableAutoRelease.java create mode 100644 src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseMultiObserver.java create mode 100644 src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseObserver.java create mode 100644 src/main/java/io/reactivex/rxjava3/internal/subscribers/DisposableAutoReleaseSubscriber.java create mode 100644 src/test/java/io/reactivex/rxjava3/internal/observers/CompletableConsumersTest.java create mode 100644 src/test/java/io/reactivex/rxjava3/internal/observers/MaybeConsumersTest.java create mode 100644 src/test/java/io/reactivex/rxjava3/internal/observers/ObservableConsumersTest.java create mode 100644 src/test/java/io/reactivex/rxjava3/internal/observers/SingleConsumersTest.java create mode 100644 src/test/java/io/reactivex/rxjava3/internal/subscribers/FlowableConsumersTest.java diff --git a/src/main/java/io/reactivex/rxjava3/core/Completable.java b/src/main/java/io/reactivex/rxjava3/core/Completable.java index 05b3f8b240..96eb10c447 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Completable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Completable.java @@ -19,7 +19,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.*; @@ -29,7 +29,7 @@ import io.reactivex.rxjava3.internal.operators.completable.*; import io.reactivex.rxjava3.internal.operators.maybe.*; import io.reactivex.rxjava3.internal.operators.mixed.*; -import io.reactivex.rxjava3.internal.operators.single.*; +import io.reactivex.rxjava3.internal.operators.single.SingleDelayWithCompletable; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -2836,6 +2836,7 @@ public final Completable hide() { *

    {@code subscribe} does not operate by default on a particular {@link Scheduler}.
    *
    * @return the new {@code Disposable} that can be used for disposing the subscription at any time + * @see #subscribe(Action, Consumer, DisposableContainer) */ @SchedulerSupport(SchedulerSupport.NONE) @NonNull @@ -2921,6 +2922,7 @@ public final void subscribe(@NonNull CompletableObserver observer) { * @param onError the {@link Consumer} that is called if this {@code Completable} emits an error * @return the new {@link Disposable} that can be used for disposing the subscription at any time * @throws NullPointerException if {@code onComplete} or {@code onError} is {@code null} + * @see #subscribe(Action, Consumer, DisposableContainer) */ @CheckReturnValue @NonNull @@ -2934,6 +2936,44 @@ public final Disposable subscribe(@NonNull Action onComplete, @NonNull Consumer< return observer; } + /** + * Wraps the given onXXX callbacks into a {@link Disposable} {@link CompletableObserver}, + * adds it to the given {@link DisposableContainer} and ensures, that if the upstream + * terminates or this particular {@code Disposable} is disposed, the {@code CompletableObserver} is removed + * from the given composite. + *

    + * The {@code CompletableObserver} will be removed after the callback for the terminal event has been invoked. + *

    + *
    Scheduler:
    + *
    {@code subscribe} does not operate by default on a particular {@link Scheduler}.
    + *
    + * @param onError the callback for an upstream error + * @param onComplete the callback for an upstream completion + * @param container the {@code DisposableContainer} (such as {@link CompositeDisposable}) to add and remove the + * created {@code Disposable} {@code CompletableObserver} + * @return the {@code Disposable} that allows disposing the particular subscription. + * @throws NullPointerException + * if {@code onComplete}, {@code onError} + * or {@code container} is {@code null} + * @since 3.1.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Disposable subscribe( + @NonNull Action onComplete, + @NonNull Consumer onError, + @NonNull DisposableContainer container) { + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(container, "container is null"); + + DisposableAutoReleaseMultiObserver observer = new DisposableAutoReleaseMultiObserver<>( + container, Functions.emptyConsumer(), onError, onComplete); + container.add(observer); + subscribe(observer); + return observer; + } + /** * Subscribes to this {@code Completable} and calls the given {@link Action} when this {@code Completable} * completes normally. @@ -2950,6 +2990,7 @@ public final Disposable subscribe(@NonNull Action onComplete, @NonNull Consumer< * @param onComplete the {@code Action} called when this {@code Completable} completes normally * @return the new {@link Disposable} that can be used for disposing the subscription at any time * @throws NullPointerException if {@code onComplete} is {@code null} + * @see #subscribe(Action, Consumer, DisposableContainer) */ @CheckReturnValue @NonNull diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 804a057533..d22d8dc4dc 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -20,7 +20,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.flowables.*; import io.reactivex.rxjava3.functions.*; @@ -15699,6 +15699,7 @@ public final Flowable startWithArray(@NonNull T... items) { * * @return the new {@link Disposable} instance that allows cancelling the flow * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) @@ -15727,6 +15728,7 @@ public final Disposable subscribe() { * @throws NullPointerException * if {@code onNext} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @@ -15753,9 +15755,10 @@ public final Disposable subscribe(@NonNull Consumer onNext) { * the {@code Consumer} you have designed to accept any error notification from the * current {@code Flowable} * @return the new {@link Disposable} instance that allows cancelling the flow - * @see ReactiveX operators documentation: Subscribe * @throws NullPointerException * if {@code onNext} or {@code onError} is {@code null} + * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @@ -15788,6 +15791,7 @@ public final Disposable subscribe(@NonNull Consumer onNext, @NonNull * @throws NullPointerException * if {@code onNext}, {@code onError} or {@code onComplete} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @@ -15806,6 +15810,51 @@ public final Disposable subscribe(@NonNull Consumer onNext, @NonNull return ls; } + /** + * Wraps the given onXXX callbacks into a {@link Disposable} {@link Subscriber}, + * adds it to the given {@link DisposableContainer} and ensures, that if the upstream + * terminates or this particular {@code Disposable} is disposed, the {@code Subscriber} is removed + * from the given container. + *

    + * The {@coded Subscriber} will be removed after the callback for the terminal event has been invoked. + *

    + *
    Backpressure:
    + *
    The operator consumes the current {@code Flowable} in an unbounded manner (i.e., no + * backpressure is applied to it).
    + *
    Scheduler:
    + *
    {@code subscribe} does not operate by default on a particular {@link Scheduler}.
    + *
    + * @param onNext the callback for upstream items + * @param onError the callback for an upstream error if any + * @param onComplete the callback for the upstream completion if any + * @param container the {@code DisposableContainer} (such as {@link CompositeDisposable}) to add and remove the + * created {@code Disposable} {@code Subscriber} + * @return the {@code Disposable} that allows disposing the particular subscription. + * @throws NullPointerException + * if {@code onNext}, {@code onError}, + * {@code onComplete} or {@code container} is {@code null} + * @since 3.1.0 + */ + @BackpressureSupport(BackpressureKind.SPECIAL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Disposable subscribe( + @NonNull Consumer onNext, + @NonNull Consumer onError, + @NonNull Action onComplete, + @NonNull DisposableContainer container) { + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(container, "container is null"); + + DisposableAutoReleaseSubscriber subscriber = new DisposableAutoReleaseSubscriber<>( + container, onNext, onError, onComplete); + container.add(subscriber); + subscribe(subscriber); + return subscriber; + } + @BackpressureSupport(BackpressureKind.SPECIAL) @SchedulerSupport(SchedulerSupport.NONE) @Override diff --git a/src/main/java/io/reactivex/rxjava3/core/Maybe.java b/src/main/java/io/reactivex/rxjava3/core/Maybe.java index c0b8421fb1..917a9838a4 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Maybe.java +++ b/src/main/java/io/reactivex/rxjava3/core/Maybe.java @@ -20,7 +20,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.*; @@ -5226,6 +5226,7 @@ public final Flowable startWith(@NonNull Publisher other) { * * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @SchedulerSupport(SchedulerSupport.NONE) @NonNull @@ -5250,6 +5251,7 @@ public final Disposable subscribe() { * @throws NullPointerException * if {@code onSuccess} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @@ -5272,10 +5274,11 @@ public final Disposable subscribe(@NonNull Consumer onSuccess) { * the {@code Consumer} you have designed to accept any error notification from the * {@code Maybe} * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time - * @see ReactiveX operators documentation: Subscribe * @throws NullPointerException * if {@code onSuccess} is {@code null}, or * if {@code onError} is {@code null} + * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @@ -5305,6 +5308,7 @@ public final Disposable subscribe(@NonNull Consumer onSuccess, @NonNu * if {@code onSuccess}, {@code onError} or * {@code onComplete} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @NonNull @@ -5317,6 +5321,47 @@ public final Disposable subscribe(@NonNull Consumer onSuccess, @NonNu return subscribeWith(new MaybeCallbackObserver<>(onSuccess, onError, onComplete)); } + /** + * Wraps the given onXXX callbacks into a {@link Disposable} {@link MaybeObserver}, + * adds it to the given {@link DisposableContainer} and ensures, that if the upstream + * terminates or this particular {@code Disposable} is disposed, the {@code MaybeObserver} is removed + * from the given composite. + *

    + * The {@code MaybeObserver} will be removed after the callback for the terminal event has been invoked. + *

    + *
    Scheduler:
    + *
    {@code subscribe} does not operate by default on a particular {@link Scheduler}.
    + *
    + * @param onSuccess the callback for upstream items + * @param onError the callback for an upstream error + * @param onComplete the callback for an upstream completion without any value or error + * @param container the {@code DisposableContainer} (such as {@link CompositeDisposable}) to add and remove the + * created {@code Disposable} {@code MaybeObserver} + * @return the {@code Disposable} that allows disposing the particular subscription. + * @throws NullPointerException + * if {@code onSuccess}, {@code onError}, + * {@code onComplete} or {@code container} is {@code null} + * @since 3.1.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Disposable subscribe( + @NonNull Consumer onSuccess, + @NonNull Consumer onError, + @NonNull Action onComplete, + @NonNull DisposableContainer container) { + Objects.requireNonNull(onSuccess, "onSuccess is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(container, "container is null"); + + DisposableAutoReleaseMultiObserver observer = new DisposableAutoReleaseMultiObserver<>( + container, onSuccess, onError, onComplete); + container.add(observer); + subscribe(observer); + return observer; + } + @SchedulerSupport(SchedulerSupport.NONE) @Override public final void subscribe(@NonNull MaybeObserver observer) { diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index 27d46ad8c5..18534eecc7 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -20,7 +20,7 @@ import org.reactivestreams.Publisher; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.*; @@ -13025,6 +13025,7 @@ public final Observable startWithArray(@NonNull T... items) { * * @return the new {@link Disposable} instance that can be used to dispose the subscription at any time * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @SchedulerSupport(SchedulerSupport.NONE) @NonNull @@ -13049,6 +13050,7 @@ public final Disposable subscribe() { * @throws NullPointerException * if {@code onNext} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @@ -13071,9 +13073,10 @@ public final Disposable subscribe(@NonNull Consumer onNext) { * the {@code Consumer} you have designed to accept any error notification from the current * {@code Observable} * @return the new {@link Disposable} instance that can be used to dispose the subscription at any time - * @see ReactiveX operators documentation: Subscribe * @throws NullPointerException * if {@code onNext} or {@code onError} is {@code null} + * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @@ -13102,6 +13105,7 @@ public final Disposable subscribe(@NonNull Consumer onNext, @NonNull * @throws NullPointerException * if {@code onNext}, {@code onError} or {@code onComplete} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @@ -13119,6 +13123,47 @@ public final Disposable subscribe(@NonNull Consumer onNext, @NonNull return ls; } + /** + * Wraps the given onXXX callbacks into a {@link Disposable} {@link Observer}, + * adds it to the given {@code DisposableContainer} and ensures, that if the upstream + * terminates or this particular {@code Disposable} is disposed, the {@code Observer} is removed + * from the given container. + *

    + * The {@code Observer} will be removed after the callback for the terminal event has been invoked. + *

    + *
    Scheduler:
    + *
    {@code subscribe} does not operate by default on a particular {@link Scheduler}.
    + *
    + * @param onNext the callback for upstream items + * @param onError the callback for an upstream error if any + * @param onComplete the callback for the upstream completion if any + * @param container the {@code DisposableContainer} (such as {@link CompositeDisposable}) to add and remove the + * created {@code Disposable} {@code Observer} + * @return the {@code Disposable} that allows disposing the particular subscription. + * @throws NullPointerException + * if {@code onNext}, {@code onError}, + * {@code onComplete} or {@code container} is {@code null} + * @since 3.1.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Disposable subscribe( + @NonNull Consumer onNext, + @NonNull Consumer onError, + @NonNull Action onComplete, + @NonNull DisposableContainer container) { + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(container, "container is null"); + + DisposableAutoReleaseObserver observer = new DisposableAutoReleaseObserver<>( + container, onNext, onError, onComplete); + container.add(observer); + subscribe(observer); + return observer; + } + @SchedulerSupport(SchedulerSupport.NONE) @Override public final void subscribe(@NonNull Observer observer) { diff --git a/src/main/java/io/reactivex/rxjava3/core/Single.java b/src/main/java/io/reactivex/rxjava3/core/Single.java index dbe3694913..66e1ffd3d5 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Single.java +++ b/src/main/java/io/reactivex/rxjava3/core/Single.java @@ -20,7 +20,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.*; @@ -4705,6 +4705,7 @@ public final Flowable startWith(@NonNull Publisher other) { * * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, DisposableContainer) */ @SchedulerSupport(SchedulerSupport.NONE) @NonNull @@ -4726,9 +4727,10 @@ public final Disposable subscribe() { * the callback that receives either the success value or the failure {@link Throwable} * (whichever is not {@code null}) * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time - * @see ReactiveX operators documentation: Subscribe * @throws NullPointerException * if {@code onCallback} is {@code null} + * @see #subscribe(Consumer, Consumer, DisposableContainer) + * @see ReactiveX operators documentation: Subscribe */ @CheckReturnValue @NonNull @@ -4760,6 +4762,7 @@ public final Disposable subscribe(@NonNull BiConsumerReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @@ -4784,9 +4787,10 @@ public final Disposable subscribe(@NonNull Consumer onSuccess) { * the {@code Consumer} you have designed to accept any error notification from the * {@code Single} * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time - * @see ReactiveX operators documentation: Subscribe * @throws NullPointerException * if {@code onSuccess} or {@code onError} is {@code null} + * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, DisposableContainer) */ @CheckReturnValue @NonNull @@ -4800,6 +4804,44 @@ public final Disposable subscribe(@NonNull Consumer onSuccess, @NonNu return observer; } + /** + * Wraps the given onXXX callbacks into a {@link Disposable} {@link SingleObserver}, + * adds it to the given {@link DisposableContainer} and ensures, that if the upstream + * terminates or this particular {@code Disposable} is disposed, the {@code SingleObserver} is removed + * from the given container. + *

    + * The {@code SingleObserver} will be removed after the callback for the terminal event has been invoked. + *

    + *
    Scheduler:
    + *
    {@code subscribe} does not operate by default on a particular {@link Scheduler}.
    + *
    + * @param onSuccess the callback for upstream items + * @param onError the callback for an upstream error if any + * @param container the {@code DisposableContainer} (such as {@link CompositeDisposable}) to add and remove the + * created {@code Disposable} {@code SingleObserver} + * @return the {@code Disposable} that allows disposing the particular subscription. + * @throws NullPointerException + * if {@code onSuccess}, {@code onError} + * or {@code container} is {@code null} + * @since 3.1.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Disposable subscribe( + @NonNull Consumer onSuccess, + @NonNull Consumer onError, + @NonNull DisposableContainer container) { + Objects.requireNonNull(onSuccess, "onSuccess is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(container, "container is null"); + + DisposableAutoReleaseMultiObserver observer = new DisposableAutoReleaseMultiObserver<>( + container, onSuccess, onError, Functions.EMPTY_ACTION); + container.add(observer); + subscribe(observer); + return observer; + } + @SchedulerSupport(SchedulerSupport.NONE) @Override public final void subscribe(@NonNull SingleObserver observer) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/AbstractDisposableAutoRelease.java b/src/main/java/io/reactivex/rxjava3/internal/observers/AbstractDisposableAutoRelease.java new file mode 100644 index 0000000000..e5d89a4502 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/AbstractDisposableAutoRelease.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivex.rxjava3.internal.observers; + +import java.util.concurrent.atomic.AtomicReference; + +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Wraps lambda callbacks and when the upstream terminates or the observer gets disposed, + * removes itself from a {@link io.reactivex.rxjava3.disposables.CompositeDisposable}. + *

    History: 0.18.0 @ RxJavaExtensions + * @since 3.1.0 + */ +abstract class AbstractDisposableAutoRelease +extends AtomicReference +implements Disposable, LambdaConsumerIntrospection { + + private static final long serialVersionUID = 8924480688481408726L; + + final AtomicReference composite; + + final Consumer onError; + + final Action onComplete; + + AbstractDisposableAutoRelease( + DisposableContainer composite, + Consumer onError, + Action onComplete + ) { + this.onError = onError; + this.onComplete = onComplete; + this.composite = new AtomicReference<>(composite); + } + + public final void onError(Throwable t) { + if (get() != DisposableHelper.DISPOSED) { + lazySet(DisposableHelper.DISPOSED); + try { + onError.accept(t); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(new CompositeException(t, e)); + } + } else { + RxJavaPlugins.onError(t); + } + removeSelf(); + } + + public final void onComplete() { + if (get() != DisposableHelper.DISPOSED) { + lazySet(DisposableHelper.DISPOSED); + try { + onComplete.run(); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(e); + } + } + removeSelf(); + } + + @Override + public final void dispose() { + DisposableHelper.dispose(this); + removeSelf(); + } + + final void removeSelf() { + DisposableContainer c = composite.getAndSet(null); + if (c != null) { + c.delete(this); + } + } + + @Override + public final boolean isDisposed() { + return DisposableHelper.isDisposed(get()); + } + + public final void onSubscribe(Disposable d) { + DisposableHelper.setOnce(this, d); + } + + @Override + public final boolean hasCustomOnError() { + return onError != Functions.ON_ERROR_MISSING; + } + +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseMultiObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseMultiObserver.java new file mode 100644 index 0000000000..4779ff5723 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseMultiObserver.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivex.rxjava3.internal.observers; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.DisposableContainer; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Wraps lambda callbacks and when the upstream terminates or this (Single | Maybe | Completable) + * observer gets disposed, removes itself from a {@link io.reactivex.rxjava3.disposables.CompositeDisposable}. + *

    History: 0.18.0 @ RxJavaExtensions + * @param the element type consumed + * @since 3.1.0 + */ +public final class DisposableAutoReleaseMultiObserver +extends AbstractDisposableAutoRelease +implements SingleObserver, MaybeObserver, CompletableObserver { + + private static final long serialVersionUID = 8924480688481408726L; + + final Consumer onSuccess; + + public DisposableAutoReleaseMultiObserver( + DisposableContainer composite, + Consumer onSuccess, + Consumer onError, + Action onComplete + ) { + super(composite, onError, onComplete); + this.onSuccess = onSuccess; + } + + @Override + public void onSuccess(T t) { + if (get() != DisposableHelper.DISPOSED) { + lazySet(DisposableHelper.DISPOSED); + try { + onSuccess.accept(t); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(e); + } + } + removeSelf(); + } + +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseObserver.java new file mode 100644 index 0000000000..89435c96ed --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseObserver.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivex.rxjava3.internal.observers; + +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.DisposableContainer; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; + +/** + * Wraps lambda callbacks and when the upstream terminates or this observer gets disposed, + * removes itself from a {@link io.reactivex.rxjava3.disposables.CompositeDisposable}. + *

    History: 0.18.0 @ RxJavaExtensions + * @param the element type consumed + * @since 3.1.0 + */ +public final class DisposableAutoReleaseObserver +extends AbstractDisposableAutoRelease +implements Observer { + + private static final long serialVersionUID = 8924480688481408726L; + + final Consumer onNext; + + public DisposableAutoReleaseObserver( + DisposableContainer composite, + Consumer onNext, + Consumer onError, + Action onComplete + ) { + super(composite, onError, onComplete); + this.onNext = onNext; + } + + @Override + public void onNext(T t) { + if (get() != DisposableHelper.DISPOSED) { + try { + onNext.accept(t); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + get().dispose(); + onError(e); + } + } + } + +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/DisposableAutoReleaseSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/DisposableAutoReleaseSubscriber.java new file mode 100644 index 0000000000..3b9d2d99b3 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/DisposableAutoReleaseSubscriber.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivex.rxjava3.internal.subscribers; + +import java.util.concurrent.atomic.AtomicReference; + +import org.reactivestreams.Subscription; + +import io.reactivex.rxjava3.core.FlowableSubscriber; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Wraps lambda callbacks and when the upstream terminates or this subscriber gets disposed, + * removes itself from a {@link io.reactivex.rxjava3.disposables.CompositeDisposable}. + *

    History: 0.18.0 @ RxJavaExtensions + * @param the element type consumed + * @since 3.1.0 + */ +public final class DisposableAutoReleaseSubscriber +extends AtomicReference +implements FlowableSubscriber, Disposable, LambdaConsumerIntrospection { + + private static final long serialVersionUID = 8924480688481408726L; + + final AtomicReference composite; + + final Consumer onNext; + + final Consumer onError; + + final Action onComplete; + + public DisposableAutoReleaseSubscriber( + DisposableContainer composite, + Consumer onNext, + Consumer onError, + Action onComplete + ) { + this.onNext = onNext; + this.onError = onError; + this.onComplete = onComplete; + this.composite = new AtomicReference<>(composite); + } + + @Override + public void onNext(T t) { + if (get() != SubscriptionHelper.CANCELLED) { + try { + onNext.accept(t); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + get().cancel(); + onError(e); + } + } + } + + @Override + public void onError(Throwable t) { + if (get() != SubscriptionHelper.CANCELLED) { + lazySet(SubscriptionHelper.CANCELLED); + try { + onError.accept(t); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(new CompositeException(t, e)); + } + } else { + RxJavaPlugins.onError(t); + } + removeSelf(); + } + + @Override + public void onComplete() { + if (get() != SubscriptionHelper.CANCELLED) { + lazySet(SubscriptionHelper.CANCELLED); + try { + onComplete.run(); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(e); + } + } + removeSelf(); + } + + @Override + public void dispose() { + SubscriptionHelper.cancel(this); + removeSelf(); + } + + void removeSelf() { + DisposableContainer c = composite.getAndSet(null); + if (c != null) { + c.delete(this); + } + } + + @Override + public boolean isDisposed() { + return SubscriptionHelper.CANCELLED == get(); + } + + @Override + public void onSubscribe(Subscription s) { + if (SubscriptionHelper.setOnce(this, s)) { + s.request(Long.MAX_VALUE); + } + } + + @Override + public boolean hasCustomOnError() { + return onError != Functions.ON_ERROR_MISSING; + } + +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/CompletableConsumersTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/CompletableConsumersTest.java new file mode 100644 index 0000000000..eb17ead3db --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/CompletableConsumersTest.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.CompositeException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subjects.CompletableSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class CompletableConsumersTest implements Consumer, Action { + + final CompositeDisposable composite = new CompositeDisposable(); + + final CompletableSubject processor = CompletableSubject.create(); + + final List events = new ArrayList<>(); + + @Override + public void run() throws Exception { + events.add("OnComplete"); + } + + @Override + public void accept(Object t) throws Exception { + events.add(t); + } + + @Test + public void onErrorNormal() { + + processor.subscribe(this, this, composite); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onComplete(); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList("OnComplete"), events); + + } + + @Test + public void onErrorError() { + + Disposable d = processor.subscribe(this, this, composite); + + assertTrue(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onError(new IOException()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteNormal() { + + processor.subscribe(this, this, composite); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onComplete(); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList("OnComplete"), events); + + } + + @Test + public void onCompleteError() { + + processor.subscribe(this, this, composite); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onError(new IOException()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteDispose() { + + Disposable d = processor.subscribe(this, this, composite); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + assertFalse(d.isDisposed()); + + d.dispose(); + d.dispose(); + + assertTrue(d.isDisposed()); + + assertEquals(0, composite.size()); + + assertFalse(processor.hasObservers()); + } + + @Test + public void onErrorCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + processor.subscribe(this, t -> { + throw new IOException(t); + }, composite); + + processor.onError(new IllegalArgumentException()); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, CompositeException.class); + List inners = TestHelper.compositeList(errors.get(0)); + TestHelper.assertError(inners, 0, IllegalArgumentException.class); + TestHelper.assertError(inners, 1, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onCompleteCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + processor.subscribe(new Action() { + @Override + public void run() throws Exception { + throw new IOException(); + } + }, this, composite); + + processor.onComplete(); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void badSource() { + List errors = TestHelper.trackPluginErrors(); + try { + new Completable() { + @Override + protected void subscribeActual( + CompletableObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onComplete(); + + observer.onSubscribe(Disposable.empty()); + observer.onComplete(); + observer.onError(new IOException()); + } + }.subscribe(this, this, composite); + + assertEquals(Arrays.asList("OnComplete"), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/MaybeConsumersTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/MaybeConsumersTest.java new file mode 100644 index 0000000000..92b32c000e --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/MaybeConsumersTest.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.CompositeException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeConsumersTest implements Consumer, Action { + + final CompositeDisposable composite = new CompositeDisposable(); + + final MaybeSubject processor = MaybeSubject.create(); + + final List events = new ArrayList<>(); + + @Override + public void run() throws Exception { + events.add("OnComplete"); + } + + @Override + public void accept(Object t) throws Exception { + events.add(t); + } + + static Disposable subscribeAutoDispose(Maybe source, CompositeDisposable composite, + Consumer onSuccess, Consumer onError, Action onComplete) { + return source.subscribe(onSuccess, onError, onComplete, composite); + } + + @Test + public void onSuccessNormal() { + + Disposable d = subscribeAutoDispose(processor, composite, this, Functions.ON_ERROR_MISSING, () -> { }); + + assertFalse(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onSuccess(1); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList(1), events); + + } + + @Test + public void onErrorNormal() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onSuccess(1); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList(1), events); + + } + + @Test + public void onErrorError() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onError(new IOException()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteNormal() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onComplete(); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList("OnComplete"), events); + + } + + @Test + public void onCompleteError() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onError(new IOException()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteDispose() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + assertFalse(d.isDisposed()); + + d.dispose(); + d.dispose(); + + assertTrue(d.isDisposed()); + + assertEquals(0, composite.size()); + + assertFalse(processor.hasObservers()); + } + + @Test + public void onSuccessCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, new Consumer() { + @Override + public void accept(Object t) throws Exception { + throw new IOException(); + } + }, this, this); + + processor.onSuccess(1); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onErrorCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, new Consumer() { + @Override + public void accept(Throwable t) throws Exception { + throw new IOException(t); + } + }, this); + + processor.onError(new IllegalArgumentException()); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, CompositeException.class); + List inners = TestHelper.compositeList(errors.get(0)); + TestHelper.assertError(inners, 0, IllegalArgumentException.class); + TestHelper.assertError(inners, 1, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onCompleteCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, this, new Action() { + @Override + public void run() throws Exception { + throw new IOException(); + } + }); + + processor.onComplete(); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void badSource() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose( + new Maybe() { + @Override + protected void subscribeActual( + MaybeObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onComplete(); + + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(2); + observer.onComplete(); + observer.onError(new IOException()); + } + }, composite, this, this, this + ); + + assertEquals(Arrays.asList("OnComplete"), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/ObservableConsumersTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/ObservableConsumersTest.java new file mode 100644 index 0000000000..b5d26340e7 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/ObservableConsumersTest.java @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class ObservableConsumersTest implements Consumer, Action { + + final CompositeDisposable composite = new CompositeDisposable(); + + final PublishSubject processor = PublishSubject.create(); + + final List events = new ArrayList<>(); + + @Override + public void run() throws Exception { + events.add("OnComplete"); + } + + @Override + public void accept(Object t) throws Exception { + events.add(t); + } + + static Disposable subscribeAutoDispose(Observable source, CompositeDisposable composite, + Consumer onNext, Consumer onError, Action onComplete) { + return source.subscribe(onNext, onError, onComplete, composite); + } + + @Test + public void onNextNormal() { + + Disposable d = subscribeAutoDispose(processor, composite, this, Functions.ON_ERROR_MISSING, () -> { }); + + assertFalse(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onErrorNormal() { + + subscribeAutoDispose(processor, composite, this, Functions.ON_ERROR_MISSING, () -> { }); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onErrorError() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onError(new IOException()); + + assertEquals(events.toString(), 1, events.get(0)); + assertTrue(events.toString(), events.get(1) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteNormal() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1, "OnComplete"), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteError() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onError(new IOException()); + + assertEquals(events.toString(), 1, events.get(0)); + assertTrue(events.toString(), events.get(1) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteDispose() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + assertFalse(d.isDisposed()); + + d.dispose(); + d.dispose(); + + assertTrue(d.isDisposed()); + + assertEquals(0, composite.size()); + + assertFalse(processor.hasObservers()); + } + + @Test + public void onNextCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, new Consumer() { + @Override + public void accept(Object t) throws Exception { + throw new IOException(); + } + }, this, this); + + processor.onNext(1); + + assertTrue(errors.toString(), errors.isEmpty()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onNextCrashOnError() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, new Consumer() { + @Override + public void accept(Throwable t) throws Exception { + throw new IOException(t); + } + }, this); + + processor.onError(new IllegalArgumentException()); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, CompositeException.class); + List inners = TestHelper.compositeList(errors.get(0)); + TestHelper.assertError(inners, 0, IllegalArgumentException.class); + TestHelper.assertError(inners, 1, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onNextCrashNoError() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, t -> { + throw new IOException(); + }, Functions.ON_ERROR_MISSING, () -> { }); + + processor.onNext(1); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, OnErrorNotImplementedException.class); + assertTrue(errors.get(0).getCause() instanceof IOException); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onCompleteCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, this, new Action() { + @Override + public void run() throws Exception { + throw new IOException(); + } + }); + + processor.onNext(1); + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void badSource() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose( + new Observable() { + @Override + protected void subscribeActual( + Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onComplete(); + + observer.onSubscribe(Disposable.empty()); + observer.onNext(2); + observer.onComplete(); + observer.onError(new IOException()); + } + }, composite, this, this, this + ); + + assertEquals(Arrays.asList(1, "OnComplete"), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/SingleConsumersTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/SingleConsumersTest.java new file mode 100644 index 0000000000..a840d8f71a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/SingleConsumersTest.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.CompositeException; +import io.reactivex.rxjava3.functions.Consumer; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleConsumersTest implements Consumer { + + final CompositeDisposable composite = new CompositeDisposable(); + + final SingleSubject processor = SingleSubject.create(); + + final List events = new ArrayList<>(); + + @Override + public void accept(Object t) throws Exception { + events.add(t); + } + + static Disposable subscribeAutoDispose(Single source, CompositeDisposable composite, + Consumer onSuccess, Consumer onError) { + return source.subscribe(onSuccess, onError, composite); + } + + @Test + public void onSuccessNormal() { + + Disposable d = subscribeAutoDispose(processor, composite, this, Functions.ON_ERROR_MISSING); + + assertFalse(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onSuccess(1); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList(1), events); + + } + + @Test + public void onErrorNormal() { + + subscribeAutoDispose(processor, composite, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onSuccess(1); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList(1), events); + + } + + @Test + public void onErrorError() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this); + + assertTrue(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onError(new IOException()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onSuccessCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, new Consumer() { + @Override + public void accept(Object t) throws Exception { + throw new IOException(); + } + }, this); + + processor.onSuccess(1); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onErrorCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, new Consumer() { + @Override + public void accept(Throwable t) throws Exception { + throw new IOException(t); + } + }); + + processor.onError(new IllegalArgumentException()); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, CompositeException.class); + List inners = TestHelper.compositeList(errors.get(0)); + TestHelper.assertError(inners, 0, IllegalArgumentException.class); + TestHelper.assertError(inners, 1, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void badSource() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose( + new Single() { + @Override + protected void subscribeActual( + SingleObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(1); + + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(2); + observer.onError(new IOException()); + } + }, composite, this, this + ); + + assertEquals(Arrays.asList(1), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/FlowableConsumersTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/FlowableConsumersTest.java new file mode 100644 index 0000000000..cc7083cde7 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/FlowableConsumersTest.java @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2016-present, RxJava Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See + * the License for the specific language governing permissions and limitations under the License. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.reactivex.rxjava3.internal.subscribers; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.*; + +import org.junit.Test; +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class FlowableConsumersTest implements Consumer, Action { + + final CompositeDisposable composite = new CompositeDisposable(); + + final PublishProcessor processor = PublishProcessor.create(); + + final List events = new ArrayList<>(); + + @Override + public void run() throws Exception { + events.add("OnComplete"); + } + + @Override + public void accept(Object t) throws Exception { + events.add(t); + } + + static Disposable subscribeAutoDispose(Flowable source, CompositeDisposable composite, + Consumer onNext, Consumer onError, Action onComplete) { + return source.subscribe(onNext, onError, onComplete, composite); + } + + @Test + public void onNextNormal() { + + Disposable d = subscribeAutoDispose(processor, composite, this, Functions.ON_ERROR_MISSING, () -> { }); + + assertFalse(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onErrorNormal() { + + subscribeAutoDispose(processor, composite, this, this, () -> { }); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onErrorError() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onError(new IOException()); + + assertEquals(events.toString(), 1, events.get(0)); + assertTrue(events.toString(), events.get(1) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteNormal() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1, "OnComplete"), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteError() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onError(new IOException()); + + assertEquals(events.toString(), 1, events.get(0)); + assertTrue(events.toString(), events.get(1) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteDispose() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + assertFalse(d.isDisposed()); + + d.dispose(); + d.dispose(); + + assertTrue(d.isDisposed()); + + assertEquals(0, composite.size()); + + assertFalse(processor.hasSubscribers()); + } + + @Test + public void onNextCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, new Consumer() { + @Override + public void accept(Object t) throws Exception { + throw new IOException(); + } + }, this, this); + + processor.onNext(1); + + assertTrue(errors.toString(), errors.isEmpty()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onNextCrashOnError() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, new Consumer() { + @Override + public void accept(Throwable t) throws Exception { + throw new IOException(t); + } + }, this); + + processor.onError(new IllegalArgumentException()); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, CompositeException.class); + List inners = TestHelper.compositeList(errors.get(0)); + TestHelper.assertError(inners, 0, IllegalArgumentException.class); + TestHelper.assertError(inners, 1, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onNextCrashNoError() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, new Consumer() { + @Override + public void accept(Object t) throws Exception { + throw new IOException(); + } + }, Functions.ON_ERROR_MISSING, () -> { }); + + processor.onNext(1); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, OnErrorNotImplementedException.class); + assertTrue(errors.get(0).getCause() instanceof IOException); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onCompleteCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, this, new Action() { + @Override + public void run() throws Exception { + throw new IOException(); + } + }); + + processor.onNext(1); + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void badSource() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose( + new Flowable() { + @Override + protected void subscribeActual( + Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onComplete(); + + s.onSubscribe(new BooleanSubscription()); + s.onNext(2); + s.onComplete(); + s.onError(new IOException()); + } + }, composite, this, this, this + ); + + assertEquals(Arrays.asList(1, "OnComplete"), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/validators/BaseTypeAnnotations.java b/src/test/java/io/reactivex/rxjava3/validators/BaseTypeAnnotations.java index d03c0dcade..582df0fa71 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/BaseTypeAnnotations.java +++ b/src/test/java/io/reactivex/rxjava3/validators/BaseTypeAnnotations.java @@ -22,6 +22,7 @@ import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.DisposableContainer; import io.reactivex.rxjava3.flowables.ConnectableFlowable; import io.reactivex.rxjava3.observables.ConnectableObservable; import io.reactivex.rxjava3.parallel.ParallelFlowable; @@ -44,7 +45,8 @@ static void checkCheckReturnValueSupport(Class clazz) { for (Method m : clazz.getMethods()) { if (m.getDeclaringClass() == clazz) { - boolean isSubscribeMethod = "subscribe".equals(m.getName()) && m.getParameterTypes().length == 0; + boolean isSubscribeMethod = "subscribe".equals(m.getName()) && + (m.getParameterTypes().length == 0 || m.getParameterTypes()[m.getParameterCount() - 1] == DisposableContainer.class); boolean isConnectMethod = "connect".equals(m.getName()) && m.getParameterTypes().length == 0; boolean isAnnotationPresent = m.isAnnotationPresent(CheckReturnValue.class); diff --git a/src/test/java/io/reactivex/rxjava3/validators/JavadocForAnnotations.java b/src/test/java/io/reactivex/rxjava3/validators/JavadocForAnnotations.java index c3c814f9ff..1c683783d6 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/JavadocForAnnotations.java +++ b/src/test/java/io/reactivex/rxjava3/validators/JavadocForAnnotations.java @@ -95,7 +95,7 @@ static final void scanFor(StringBuilder sourceCode, String annotation, String in ; int lc = lineNumber(sourceCode, idx); - e.append(" at io.reactivex.").append(baseClassName) + e.append(" at io.reactivex.rxjava3.core.").append(baseClassName) .append(" (").append(baseClassName).append(".java:") .append(lc).append(")").append("\r\n\r\n"); } diff --git a/src/test/java/io/reactivex/rxjava3/validators/JavadocWording.java b/src/test/java/io/reactivex/rxjava3/validators/JavadocWording.java index b09303643c..3ab41589a3 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/JavadocWording.java +++ b/src/test/java/io/reactivex/rxjava3/validators/JavadocWording.java @@ -309,6 +309,7 @@ public void flowableDocRefersToFlowableTypes() throws Exception { && !m.signature.contains("Maybe") && !m.signature.contains("MaybeSource") && !m.signature.contains("Disposable") + && !m.signature.contains("void subscribe") ) { CharSequence subSequence = m.javadoc.subSequence(idx - 6, idx + 11); if (idx < 6 || !subSequence.equals("{@link Disposable")) { diff --git a/src/test/java/io/reactivex/rxjava3/validators/ParamValidationCheckerTest.java b/src/test/java/io/reactivex/rxjava3/validators/ParamValidationCheckerTest.java index 84f99707f2..51c7626ed5 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/ParamValidationCheckerTest.java +++ b/src/test/java/io/reactivex/rxjava3/validators/ParamValidationCheckerTest.java @@ -25,7 +25,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -580,6 +580,8 @@ public void checkParallelFlowable() { defaultValues.put(ParallelFailureHandling.class, ParallelFailureHandling.ERROR); + defaultValues.put(DisposableContainer.class, new CompositeDisposable()); + // JDK 8 types defaultValues.put(Optional.class, Optional.of(1)); From a463d248145d55fc0d0833d192ee11b254012855 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jul 2021 08:13:45 +0200 Subject: [PATCH 199/521] Bump codecov/codecov-action from 1.5.2 to 2.0.1 (#7299) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 1.5.2 to 2.0.1. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v1.5.2...v2.0.1) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gradle_branch.yml | 2 +- .github/workflows/gradle_pr.yml | 2 +- .github/workflows/gradle_release.yml | 2 +- .github/workflows/gradle_snapshot.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml index 27a45e5ccc..2d64e82a80 100644 --- a/.github/workflows/gradle_branch.yml +++ b/.github/workflows/gradle_branch.yml @@ -29,6 +29,6 @@ jobs: - name: Build RxJava run: ./gradlew build --stacktrace - name: Upload to Codecov - uses: codecov/codecov-action@v1.5.2 + uses: codecov/codecov-action@v2.0.1 - name: Generate Javadoc run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml index dbc5418a92..4a5a0c4ba8 100644 --- a/.github/workflows/gradle_pr.yml +++ b/.github/workflows/gradle_pr.yml @@ -29,6 +29,6 @@ jobs: - name: Build RxJava run: ./gradlew build --stacktrace - name: Upload to Codecov - uses: codecov/codecov-action@v1.5.2 + uses: codecov/codecov-action@v2.0.1 - name: Generate Javadoc run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml index 81962b6028..092dd9f659 100644 --- a/.github/workflows/gradle_release.yml +++ b/.github/workflows/gradle_release.yml @@ -38,7 +38,7 @@ jobs: - name: Build RxJava run: ./gradlew build --stacktrace --no-daemon - name: Upload to Codecov - uses: codecov/codecov-action@v1.5.2 + uses: codecov/codecov-action@v2.0.1 - name: Upload release run: ./gradlew -PreleaseMode=full publish --no-daemon --no-parallel --stacktrace env: diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml index ed101d8536..391feb08a5 100644 --- a/.github/workflows/gradle_snapshot.yml +++ b/.github/workflows/gradle_snapshot.yml @@ -41,7 +41,7 @@ jobs: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USER }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} - name: Upload to Codecov - uses: codecov/codecov-action@v1.5.2 + uses: codecov/codecov-action@v2.0.1 - name: Push Javadoc run: ./push_javadoc.sh # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions From bb814d2064e061d27cbdcd1ac64528d96f97feea Mon Sep 17 00:00:00 2001 From: David Karnok Date: Thu, 22 Jul 2021 17:44:45 +0200 Subject: [PATCH 200/521] 3.x: Javadocs fix some wording in Schedulers.java (#7301) --- .../java/io/reactivex/rxjava3/schedulers/Schedulers.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java index f5dbe6ce7c..cc7e693b3d 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java @@ -27,6 +27,8 @@ * The initial and runtime values of the various scheduler types can be overridden via the * {@code RxJavaPlugins.setInit(scheduler name)SchedulerHandler()} and * {@code RxJavaPlugins.set(scheduler name)SchedulerHandler()} respectively. + * Note that overriding any initial {@code Scheduler} via the {@link RxJavaPlugins} + * has to happen before the {@code Schedulers} class is accessed. *

    * Supported system properties ({@code System.getProperty()}): *