Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c181169

Browse files
authoredJan 29, 2020
3.x: Reenable XFlatMapTest.maybeSingle, add missing Single operators (#6893)
1 parent 5f6aafc commit c181169

File tree

13 files changed

+1153
-13
lines changed

13 files changed

+1153
-13
lines changed
 

‎src/main/java/io/reactivex/rxjava3/core/Maybe.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3653,7 +3653,7 @@ public final <R> Maybe<R> flatMap(@NonNull Function<? super T, ? extends MaybeSo
36533653
}
36543654

36553655
/**
3656-
* Maps the {@code onSuccess}, {@code onError} or {@code onComplete} signals of this {@code Maybe} into {@link MaybeSource} and emits that
3656+
* Maps the {@code onSuccess}, {@code onError} or {@code onComplete} signals of the current {@code Maybe} into a {@link MaybeSource} and emits that
36573657
* {@code MaybeSource}'s signals.
36583658
* <p>
36593659
* <img width="640" height="354" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/Maybe.flatMap.mmm.png" alt="">
@@ -3691,7 +3691,7 @@ public final <R> Maybe<R> flatMap(
36913691
* Returns a {@code Maybe} that emits the results of a specified function to the pair of values emitted by the
36923692
* current {@code Maybe} and a specified mapped {@link MaybeSource}.
36933693
* <p>
3694-
* <img width="640" height="390" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/mergeMap.r.png" alt="">
3694+
* <img width="640" height="268" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/Maybe.flatMap.combiner.png" alt="">
36953695
* <dl>
36963696
* <dt><b>Scheduler:</b></dt>
36973697
* <dd>{@code flatMap} does not operate by default on a particular {@link Scheduler}.</dd>

‎src/main/java/io/reactivex/rxjava3/core/Single.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3196,6 +3196,72 @@ public final <R> Single<R> flatMap(@NonNull Function<? super T, ? extends Single
31963196
return RxJavaPlugins.onAssembly(new SingleFlatMap<>(this, mapper));
31973197
}
31983198

3199+
/**
3200+
* Returns a {@code Single} that emits the results of a specified function to the pair of values emitted by the
3201+
* current {@code Single} and a specified mapped {@link SingleSource}.
3202+
* <p>
3203+
* <img width="640" height="268" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/Single.flatMap.combiner.png" alt="">
3204+
* <dl>
3205+
* <dt><b>Scheduler:</b></dt>
3206+
* <dd>{@code flatMap} does not operate by default on a particular {@link Scheduler}.</dd>
3207+
* </dl>
3208+
*
3209+
* @param <U>
3210+
* the type of items emitted by the {@code SingleSource} returned by the {@code mapper} function
3211+
* @param <R>
3212+
* the type of items emitted by the resulting {@code Single}
3213+
* @param mapper
3214+
* a function that returns a {@code SingleSource} for the item emitted by the current {@code Single}
3215+
* @param combiner
3216+
* a function that combines one item emitted by each of the source and collection {@code SingleSource} and
3217+
* returns an item to be emitted by the resulting {@code SingleSource}
3218+
* @return the new {@code Single} instance
3219+
* @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null}
3220+
* @see <a href="http://reactivex.io/documentation/operators/flatmap.html">ReactiveX operators documentation: FlatMap</a>
3221+
* @since 3.0.0
3222+
*/
3223+
@CheckReturnValue
3224+
@NonNull
3225+
@SchedulerSupport(SchedulerSupport.NONE)
3226+
public final <U, R> Single<R> flatMap(@NonNull Function<? super T, ? extends SingleSource<? extends U>> mapper,
3227+
@NonNull BiFunction<? super T, ? super U, ? extends R> combiner) {
3228+
Objects.requireNonNull(mapper, "mapper is null");
3229+
Objects.requireNonNull(combiner, "combiner is null");
3230+
return RxJavaPlugins.onAssembly(new SingleFlatMapBiSelector<>(this, mapper, combiner));
3231+
}
3232+
3233+
/**
3234+
* Maps the {@code onSuccess} or {@code onError} signals of the current {@code Single} into a {@link SingleSource} and emits that
3235+
* {@code SingleSource}'s signals.
3236+
* <p>
3237+
* <img width="640" height="449" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/Single.flatMap.notification.png" alt="">
3238+
* <dl>
3239+
* <dt><b>Scheduler:</b></dt>
3240+
* <dd>{@code flatMap} does not operate by default on a particular {@link Scheduler}.</dd>
3241+
* </dl>
3242+
*
3243+
* @param <R>
3244+
* the result type
3245+
* @param onSuccessMapper
3246+
* a function that returns a {@code SingleSource} to merge for the {@code onSuccess} item emitted by this {@code Single}
3247+
* @param onErrorMapper
3248+
* a function that returns a {@code SingleSource} to merge for an {@code onError} notification from this {@code Single}
3249+
* @return the new {@code Single} instance
3250+
* @throws NullPointerException if {@code onSuccessMapper} or {@code onErrorMapper} is {@code null}
3251+
* @see <a href="http://reactivex.io/documentation/operators/flatmap.html">ReactiveX operators documentation: FlatMap</a>
3252+
* @since 3.0.0
3253+
*/
3254+
@CheckReturnValue
3255+
@NonNull
3256+
@SchedulerSupport(SchedulerSupport.NONE)
3257+
public final <R> Single<R> flatMap(
3258+
@NonNull Function<? super T, ? extends SingleSource<? extends R>> onSuccessMapper,
3259+
@NonNull Function<? super Throwable, ? extends SingleSource<? extends R>> onErrorMapper) {
3260+
Objects.requireNonNull(onSuccessMapper, "onSuccessMapper is null");
3261+
Objects.requireNonNull(onErrorMapper, "onErrorMapper is null");
3262+
return RxJavaPlugins.onAssembly(new SingleFlatMapNotification<>(this, onSuccessMapper, onErrorMapper));
3263+
}
3264+
31993265
/**
32003266
* Returns a {@link Maybe} that is based on applying a specified function to the item emitted by the current {@code Single},
32013267
* where that function returns a {@link MaybeSource}.

‎src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapNotification.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ public void onSuccess(T value) {
109109
return;
110110
}
111111

112-
source.subscribe(new InnerObserver());
112+
if (!isDisposed()) {
113+
source.subscribe(new InnerObserver());
114+
}
113115
}
114116

115117
@Override
@@ -124,7 +126,9 @@ public void onError(Throwable e) {
124126
return;
125127
}
126128

127-
source.subscribe(new InnerObserver());
129+
if (!isDisposed()) {
130+
source.subscribe(new InnerObserver());
131+
}
128132
}
129133

130134
@Override
@@ -139,7 +143,9 @@ public void onComplete() {
139143
return;
140144
}
141145

142-
source.subscribe(new InnerObserver());
146+
if (!isDisposed()) {
147+
source.subscribe(new InnerObserver());
148+
}
143149
}
144150

145151
final class InnerObserver implements MaybeObserver<R> {

‎src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingle.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ public void onSuccess(T value) {
8989
return;
9090
}
9191

92-
ss.subscribe(new FlatMapSingleObserver<R>(this, downstream));
92+
if (!isDisposed()) {
93+
ss.subscribe(new FlatMapSingleObserver<R>(this, downstream));
94+
}
9395
}
9496

9597
@Override

‎src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapObservable.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ public void onSuccess(T t) {
106106
return;
107107
}
108108

109-
o.subscribe(this);
109+
if (!isDisposed()) {
110+
o.subscribe(this);
111+
}
110112
}
111113

112114
}

‎src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapPublisher.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ public void onSuccess(T t) {
116116
return;
117117
}
118118

119-
p.subscribe(this);
119+
if (get() != SubscriptionHelper.CANCELLED) {
120+
p.subscribe(this);
121+
}
120122
}
121123

122124
@Override

‎src/main/java/io/reactivex/rxjava3/internal/operators/mixed/SingleFlatMapObservable.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ public void onSuccess(T t) {
106106
return;
107107
}
108108

109-
o.subscribe(this);
109+
if (!isDisposed()) {
110+
o.subscribe(this);
111+
}
110112
}
111113

112114
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/**
2+
* Copyright (c) 2016-present, RxJava Contributors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
5+
* compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is
10+
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
11+
* the License for the specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package io.reactivex.rxjava3.internal.operators.single;
15+
16+
import java.util.Objects;
17+
import java.util.concurrent.atomic.AtomicReference;
18+
19+
import io.reactivex.rxjava3.core.*;
20+
import io.reactivex.rxjava3.disposables.Disposable;
21+
import io.reactivex.rxjava3.exceptions.Exceptions;
22+
import io.reactivex.rxjava3.functions.*;
23+
import io.reactivex.rxjava3.internal.disposables.DisposableHelper;
24+
25+
/**
26+
* Maps a source item to another SingleSource then calls a BiFunction with the
27+
* original item and the secondary item to generate the final result.
28+
*
29+
* @param <T> the main value type
30+
* @param <U> the second value type
31+
* @param <R> the result value type
32+
* @since 3.0.0
33+
*/
34+
public final class SingleFlatMapBiSelector<T, U, R> extends Single<R> {
35+
36+
final SingleSource<T> source;
37+
38+
final Function<? super T, ? extends SingleSource<? extends U>> mapper;
39+
40+
final BiFunction<? super T, ? super U, ? extends R> resultSelector;
41+
42+
public SingleFlatMapBiSelector(SingleSource<T> source,
43+
Function<? super T, ? extends SingleSource<? extends U>> mapper,
44+
BiFunction<? super T, ? super U, ? extends R> resultSelector) {
45+
this.source = source;
46+
this.mapper = mapper;
47+
this.resultSelector = resultSelector;
48+
}
49+
50+
@Override
51+
protected void subscribeActual(SingleObserver<? super R> observer) {
52+
source.subscribe(new FlatMapBiMainObserver<T, U, R>(observer, mapper, resultSelector));
53+
}
54+
55+
static final class FlatMapBiMainObserver<T, U, R>
56+
implements SingleObserver<T>, Disposable {
57+
58+
final Function<? super T, ? extends SingleSource<? extends U>> mapper;
59+
60+
final InnerObserver<T, U, R> inner;
61+
62+
FlatMapBiMainObserver(SingleObserver<? super R> actual,
63+
Function<? super T, ? extends SingleSource<? extends U>> mapper,
64+
BiFunction<? super T, ? super U, ? extends R> resultSelector) {
65+
this.inner = new InnerObserver<>(actual, resultSelector);
66+
this.mapper = mapper;
67+
}
68+
69+
@Override
70+
public void dispose() {
71+
DisposableHelper.dispose(inner);
72+
}
73+
74+
@Override
75+
public boolean isDisposed() {
76+
return DisposableHelper.isDisposed(inner.get());
77+
}
78+
79+
@Override
80+
public void onSubscribe(Disposable d) {
81+
if (DisposableHelper.setOnce(inner, d)) {
82+
inner.downstream.onSubscribe(this);
83+
}
84+
}
85+
86+
@Override
87+
public void onSuccess(T value) {
88+
SingleSource<? extends U> next;
89+
90+
try {
91+
next = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null MaybeSource");
92+
} catch (Throwable ex) {
93+
Exceptions.throwIfFatal(ex);
94+
inner.downstream.onError(ex);
95+
return;
96+
}
97+
98+
if (DisposableHelper.replace(inner, null)) {
99+
inner.value = value;
100+
next.subscribe(inner);
101+
}
102+
}
103+
104+
@Override
105+
public void onError(Throwable e) {
106+
inner.downstream.onError(e);
107+
}
108+
109+
static final class InnerObserver<T, U, R>
110+
extends AtomicReference<Disposable>
111+
implements SingleObserver<U> {
112+
113+
private static final long serialVersionUID = -2897979525538174559L;
114+
115+
final SingleObserver<? super R> downstream;
116+
117+
final BiFunction<? super T, ? super U, ? extends R> resultSelector;
118+
119+
T value;
120+
121+
InnerObserver(SingleObserver<? super R> actual,
122+
BiFunction<? super T, ? super U, ? extends R> resultSelector) {
123+
this.downstream = actual;
124+
this.resultSelector = resultSelector;
125+
}
126+
127+
@Override
128+
public void onSubscribe(Disposable d) {
129+
DisposableHelper.setOnce(this, d);
130+
}
131+
132+
@Override
133+
public void onSuccess(U value) {
134+
T t = this.value;
135+
this.value = null;
136+
137+
R r;
138+
139+
try {
140+
r = Objects.requireNonNull(resultSelector.apply(t, value), "The resultSelector returned a null value");
141+
} catch (Throwable ex) {
142+
Exceptions.throwIfFatal(ex);
143+
downstream.onError(ex);
144+
return;
145+
}
146+
147+
downstream.onSuccess(r);
148+
}
149+
150+
@Override
151+
public void onError(Throwable e) {
152+
downstream.onError(e);
153+
}
154+
}
155+
}
156+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/**
2+
* Copyright (c) 2016-present, RxJava Contributors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
5+
* compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is
10+
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
11+
* the License for the specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package io.reactivex.rxjava3.internal.operators.single;
15+
16+
import java.util.Objects;
17+
import java.util.concurrent.atomic.AtomicReference;
18+
19+
import io.reactivex.rxjava3.core.*;
20+
import io.reactivex.rxjava3.disposables.Disposable;
21+
import io.reactivex.rxjava3.exceptions.*;
22+
import io.reactivex.rxjava3.functions.*;
23+
import io.reactivex.rxjava3.internal.disposables.DisposableHelper;
24+
25+
/**
26+
* Maps a value into a SingleSource and relays its signal.
27+
*
28+
* @param <T> the source value type
29+
* @param <R> the result value type
30+
* @since 3.0.0
31+
*/
32+
public final class SingleFlatMapNotification<T, R> extends Single<R> {
33+
34+
final SingleSource<T> source;
35+
36+
final Function<? super T, ? extends SingleSource<? extends R>> onSuccessMapper;
37+
38+
final Function<? super Throwable, ? extends SingleSource<? extends R>> onErrorMapper;
39+
40+
public SingleFlatMapNotification(SingleSource<T> source,
41+
Function<? super T, ? extends SingleSource<? extends R>> onSuccessMapper,
42+
Function<? super Throwable, ? extends SingleSource<? extends R>> onErrorMapper) {
43+
this.source = source;
44+
this.onSuccessMapper = onSuccessMapper;
45+
this.onErrorMapper = onErrorMapper;
46+
}
47+
48+
@Override
49+
protected void subscribeActual(SingleObserver<? super R> observer) {
50+
source.subscribe(new FlatMapSingleObserver<>(observer, onSuccessMapper, onErrorMapper));
51+
}
52+
53+
static final class FlatMapSingleObserver<T, R>
54+
extends AtomicReference<Disposable>
55+
implements SingleObserver<T>, Disposable {
56+
57+
private static final long serialVersionUID = 4375739915521278546L;
58+
59+
final SingleObserver<? super R> downstream;
60+
61+
final Function<? super T, ? extends SingleSource<? extends R>> onSuccessMapper;
62+
63+
final Function<? super Throwable, ? extends SingleSource<? extends R>> onErrorMapper;
64+
65+
Disposable upstream;
66+
67+
FlatMapSingleObserver(SingleObserver<? super R> actual,
68+
Function<? super T, ? extends SingleSource<? extends R>> onSuccessMapper,
69+
Function<? super Throwable, ? extends SingleSource<? extends R>> onErrorMapper) {
70+
this.downstream = actual;
71+
this.onSuccessMapper = onSuccessMapper;
72+
this.onErrorMapper = onErrorMapper;
73+
}
74+
75+
@Override
76+
public void dispose() {
77+
DisposableHelper.dispose(this);
78+
upstream.dispose();
79+
}
80+
81+
@Override
82+
public boolean isDisposed() {
83+
return DisposableHelper.isDisposed(get());
84+
}
85+
86+
@Override
87+
public void onSubscribe(Disposable d) {
88+
if (DisposableHelper.validate(this.upstream, d)) {
89+
this.upstream = d;
90+
91+
downstream.onSubscribe(this);
92+
}
93+
}
94+
95+
@Override
96+
public void onSuccess(T value) {
97+
SingleSource<? extends R> source;
98+
99+
try {
100+
source = Objects.requireNonNull(onSuccessMapper.apply(value), "The onSuccessMapper returned a null SingleSource");
101+
} catch (Throwable ex) {
102+
Exceptions.throwIfFatal(ex);
103+
downstream.onError(ex);
104+
return;
105+
}
106+
107+
if (!isDisposed()) {
108+
source.subscribe(new InnerObserver());
109+
}
110+
}
111+
112+
@Override
113+
public void onError(Throwable e) {
114+
SingleSource<? extends R> source;
115+
116+
try {
117+
source = Objects.requireNonNull(onErrorMapper.apply(e), "The onErrorMapper returned a null SingleSource");
118+
} catch (Throwable ex) {
119+
Exceptions.throwIfFatal(ex);
120+
downstream.onError(new CompositeException(e, ex));
121+
return;
122+
}
123+
124+
if (!isDisposed()) {
125+
source.subscribe(new InnerObserver());
126+
}
127+
}
128+
129+
final class InnerObserver implements SingleObserver<R> {
130+
131+
@Override
132+
public void onSubscribe(Disposable d) {
133+
DisposableHelper.setOnce(FlatMapSingleObserver.this, d);
134+
}
135+
136+
@Override
137+
public void onSuccess(R value) {
138+
downstream.onSuccess(value);
139+
}
140+
141+
@Override
142+
public void onError(Throwable e) {
143+
downstream.onError(e);
144+
}
145+
}
146+
}
147+
}

‎src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapPublisher.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ public void onSuccess(S value) {
9292
downstream.onError(e);
9393
return;
9494
}
95-
f.subscribe(this);
95+
if (parent.get() != SubscriptionHelper.CANCELLED) {
96+
f.subscribe(this);
97+
}
9698
}
9799

98100
@Override

‎src/test/java/io/reactivex/rxjava3/core/XFlatMapTest.java

Lines changed: 440 additions & 3 deletions
Large diffs are not rendered by default.
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/**
2+
* Copyright (c) 2016-present, RxJava Contributors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
5+
* compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is
10+
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
11+
* the License for the specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package io.reactivex.rxjava3.internal.operators.single;
15+
16+
import static org.junit.Assert.assertEquals;
17+
18+
import org.junit.Test;
19+
20+
import io.reactivex.rxjava3.core.*;
21+
import io.reactivex.rxjava3.exceptions.TestException;
22+
import io.reactivex.rxjava3.functions.*;
23+
import io.reactivex.rxjava3.observers.TestObserver;
24+
import io.reactivex.rxjava3.subjects.SingleSubject;
25+
import io.reactivex.rxjava3.testsupport.TestHelper;
26+
27+
public class SingleFlatMapBiSelectorTest extends RxJavaTest {
28+
29+
BiFunction<Integer, Integer, String> stringCombine() {
30+
return new BiFunction<Integer, Integer, String>() {
31+
@Override
32+
public String apply(Integer a, Integer b) throws Exception {
33+
return a + ":" + b;
34+
}
35+
};
36+
}
37+
38+
@Test
39+
public void normal() {
40+
Single.just(1)
41+
.flatMap(new Function<Integer, SingleSource<Integer>>() {
42+
@Override
43+
public SingleSource<Integer> apply(Integer v) throws Exception {
44+
return Single.just(2);
45+
}
46+
}, stringCombine())
47+
.test()
48+
.assertResult("1:2");
49+
}
50+
51+
@Test
52+
public void errorWithJust() {
53+
final int[] call = { 0 };
54+
55+
Single.<Integer>error(new TestException())
56+
.flatMap(new Function<Integer, SingleSource<Integer>>() {
57+
@Override
58+
public SingleSource<Integer> apply(Integer v) throws Exception {
59+
call[0]++;
60+
return Single.just(1);
61+
}
62+
}, stringCombine())
63+
.test()
64+
.assertFailure(TestException.class);
65+
66+
assertEquals(0, call[0]);
67+
}
68+
69+
@Test
70+
public void justWithError() {
71+
final int[] call = { 0 };
72+
73+
Single.just(1)
74+
.flatMap(new Function<Integer, SingleSource<Integer>>() {
75+
@Override
76+
public SingleSource<Integer> apply(Integer v) throws Exception {
77+
call[0]++;
78+
return Single.<Integer>error(new TestException());
79+
}
80+
}, stringCombine())
81+
.test()
82+
.assertFailure(TestException.class);
83+
84+
assertEquals(1, call[0]);
85+
}
86+
87+
@Test
88+
public void dispose() {
89+
TestHelper.checkDisposed(SingleSubject.create()
90+
.flatMap(new Function<Object, SingleSource<Integer>>() {
91+
@Override
92+
public SingleSource<Integer> apply(Object v) throws Exception {
93+
return Single.just(1);
94+
}
95+
}, new BiFunction<Object, Integer, Object>() {
96+
@Override
97+
public Object apply(Object a, Integer b) throws Exception {
98+
return b;
99+
}
100+
}));
101+
}
102+
103+
@Test
104+
public void doubleOnSubscribe() {
105+
TestHelper.checkDoubleOnSubscribeSingle(new Function<Single<Object>, SingleSource<Object>>() {
106+
@Override
107+
public SingleSource<Object> apply(Single<Object> v) throws Exception {
108+
return v.flatMap(new Function<Object, SingleSource<Integer>>() {
109+
@Override
110+
public SingleSource<Integer> apply(Object v) throws Exception {
111+
return Single.just(1);
112+
}
113+
}, new BiFunction<Object, Integer, Object>() {
114+
@Override
115+
public Object apply(Object a, Integer b) throws Exception {
116+
return b;
117+
}
118+
});
119+
}
120+
});
121+
}
122+
123+
@Test
124+
public void mapperThrows() {
125+
Single.just(1)
126+
.flatMap(new Function<Integer, SingleSource<Integer>>() {
127+
@Override
128+
public SingleSource<Integer> apply(Integer v) throws Exception {
129+
throw new TestException();
130+
}
131+
}, stringCombine())
132+
.test()
133+
.assertFailure(TestException.class);
134+
}
135+
136+
@Test
137+
public void mapperReturnsNull() {
138+
Single.just(1)
139+
.flatMap(new Function<Integer, SingleSource<Integer>>() {
140+
@Override
141+
public SingleSource<Integer> apply(Integer v) throws Exception {
142+
return null;
143+
}
144+
}, stringCombine())
145+
.test()
146+
.assertFailure(NullPointerException.class);
147+
}
148+
149+
@Test
150+
public void resultSelectorThrows() {
151+
Single.just(1)
152+
.flatMap(new Function<Integer, SingleSource<Integer>>() {
153+
@Override
154+
public SingleSource<Integer> apply(Integer v) throws Exception {
155+
return Single.just(2);
156+
}
157+
}, new BiFunction<Integer, Integer, Object>() {
158+
@Override
159+
public Object apply(Integer a, Integer b) throws Exception {
160+
throw new TestException();
161+
}
162+
})
163+
.test()
164+
.assertFailure(TestException.class);
165+
}
166+
167+
@Test
168+
public void resultSelectorReturnsNull() {
169+
Single.just(1)
170+
.flatMap(new Function<Integer, SingleSource<Integer>>() {
171+
@Override
172+
public SingleSource<Integer> apply(Integer v) throws Exception {
173+
return Single.just(2);
174+
}
175+
}, new BiFunction<Integer, Integer, Object>() {
176+
@Override
177+
public Object apply(Integer a, Integer b) throws Exception {
178+
return null;
179+
}
180+
})
181+
.test()
182+
.assertFailure(NullPointerException.class);
183+
}
184+
185+
@Test
186+
public void mapperCancels() {
187+
final TestObserver<Integer> to = new TestObserver<>();
188+
189+
Single.just(1)
190+
.flatMap(new Function<Integer, SingleSource<Integer>>() {
191+
@Override
192+
public SingleSource<Integer> apply(Integer v) throws Exception {
193+
to.dispose();
194+
return Single.just(2);
195+
}
196+
}, new BiFunction<Integer, Integer, Integer>() {
197+
@Override
198+
public Integer apply(Integer a, Integer b) throws Exception {
199+
throw new IllegalStateException();
200+
}
201+
})
202+
.subscribeWith(to)
203+
.assertEmpty();
204+
}
205+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/**
2+
* Copyright (c) 2016-present, RxJava Contributors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
5+
* compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is
10+
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
11+
* the License for the specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package io.reactivex.rxjava3.internal.operators.single;
15+
16+
import static org.junit.Assert.assertTrue;
17+
18+
import java.io.IOException;
19+
import java.util.List;
20+
21+
import org.junit.Test;
22+
23+
import io.reactivex.rxjava3.core.*;
24+
import io.reactivex.rxjava3.exceptions.*;
25+
import io.reactivex.rxjava3.functions.Function;
26+
import io.reactivex.rxjava3.internal.functions.Functions;
27+
import io.reactivex.rxjava3.testsupport.*;
28+
29+
public class SingleFlatMapNotificationTest extends RxJavaTest {
30+
31+
@Test
32+
public void dispose() {
33+
TestHelper.checkDisposed(Single.just(1)
34+
.flatMap(Functions.justFunction(Single.just(1)),
35+
Functions.justFunction(Single.just(1))));
36+
}
37+
38+
@Test
39+
public void doubleOnSubscribe() {
40+
TestHelper.checkDoubleOnSubscribeSingle(new Function<Single<Integer>, SingleSource<Integer>>() {
41+
@Override
42+
public SingleSource<Integer> apply(Single<Integer> m) throws Exception {
43+
return m
44+
.flatMap(Functions.justFunction(Single.just(1)),
45+
Functions.justFunction(Single.just(1)));
46+
}
47+
});
48+
}
49+
50+
@Test
51+
public void onSuccessNull() {
52+
Single.just(1)
53+
.flatMap(Functions.justFunction((Single<Integer>)null),
54+
Functions.justFunction(Single.just(1)))
55+
.test()
56+
.assertFailure(NullPointerException.class);
57+
}
58+
59+
@Test
60+
public void onErrorNull() {
61+
TestObserverEx<Integer> to = Single.<Integer>error(new TestException())
62+
.flatMap(Functions.justFunction(Single.just(1)),
63+
Functions.justFunction((Single<Integer>)null))
64+
.to(TestHelper.<Integer>testConsumer())
65+
.assertFailure(CompositeException.class);
66+
67+
List<Throwable> ce = TestHelper.compositeList(to.errors().get(0));
68+
69+
TestHelper.assertError(ce, 0, TestException.class);
70+
TestHelper.assertError(ce, 1, NullPointerException.class);
71+
}
72+
73+
@Test
74+
public void onSuccessError() {
75+
Single.just(1)
76+
.flatMap(Functions.justFunction(Single.<Integer>error(new TestException())),
77+
Functions.justFunction((Single<Integer>)null))
78+
.test()
79+
.assertFailure(TestException.class);
80+
}
81+
82+
@Test
83+
public void onSucccessSuccess() {
84+
Single.just(1)
85+
.flatMap(v -> Single.just(2), e -> Single.just(3))
86+
.test()
87+
.assertResult(2);
88+
}
89+
90+
@Test
91+
public void onErrorSuccess() throws Throwable {
92+
TestHelper.withErrorTracking(errors -> {
93+
Single.error(new TestException())
94+
.flatMap(v -> Single.just(2), e -> Single.just(3))
95+
.test()
96+
.assertResult(3);
97+
98+
assertTrue("" + errors, errors.isEmpty());
99+
});
100+
}
101+
102+
@Test
103+
public void onErrorError() throws Throwable {
104+
TestHelper.withErrorTracking(errors -> {
105+
Single.error(new TestException())
106+
.flatMap(v -> Single.just(2), e -> Single.<Integer>error(new IOException()))
107+
.test()
108+
.assertFailure(IOException.class);
109+
110+
assertTrue("" + errors, errors.isEmpty());
111+
});
112+
}
113+
}

0 commit comments

Comments
 (0)
Please sign in to comment.