, rx.Subscription, rx.Producer {
99 |
100 | private static final long serialVersionUID = -6567012932544037069L;
101 |
102 | final rx.Subscriber super T> actual;
103 |
104 | final AtomicLong requested;
105 |
106 | SourceObserver(rx.Subscriber super T> actual) {
107 | this.actual = actual;
108 | this.requested = new AtomicLong();
109 | }
110 |
111 | @Override
112 | public void request(long n) {
113 | if (n > 0L) {
114 | BackpressureHelper.add(requested, n);
115 | }
116 | }
117 |
118 | @Override
119 | public void unsubscribe() {
120 | DisposableHelper.dispose(this);
121 | }
122 |
123 | @Override
124 | public boolean isUnsubscribed() {
125 | return DisposableHelper.isDisposed(get());
126 | }
127 |
128 | @Override
129 | public void onSubscribe(io.reactivex.rxjava3.disposables.Disposable d) {
130 | DisposableHelper.setOnce(this, d);
131 | }
132 |
133 | @Override
134 | public void onNext(T t) {
135 | if (requested.get() != 0) {
136 | actual.onNext(t);
137 | BackpressureHelper.produced(requested, 1);
138 | } else {
139 | unsubscribe();
140 | actual.onError(new rx.exceptions.MissingBackpressureException());
141 | }
142 | }
143 |
144 | @Override
145 | public void onError(Throwable t) {
146 | lazySet(DisposableHelper.DISPOSED);
147 | actual.onError(t);
148 | }
149 |
150 | @Override
151 | public void onComplete() {
152 | lazySet(DisposableHelper.DISPOSED);
153 | actual.onCompleted();
154 | }
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/main/java/hu/akarnokd/rxjava3/interop/SubscriptionHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2020 David Karnok
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package hu.akarnokd.rxjava3.interop;
18 |
19 | import java.util.Objects;
20 | import java.util.concurrent.atomic.*;
21 |
22 | import org.reactivestreams.Subscription;
23 |
24 | import io.reactivex.rxjava3.exceptions.ProtocolViolationException;
25 | import io.reactivex.rxjava3.plugins.RxJavaPlugins;
26 |
27 | /**
28 | * Utility methods to validate Subscriptions in the various onSubscribe calls.
29 | */
30 | enum SubscriptionHelper implements Subscription {
31 | /**
32 | * Represents a cancelled Subscription.
33 | * Don't leak this instance!
34 | */
35 | CANCELLED
36 | ;
37 |
38 | @Override
39 | public void request(long n) {
40 | // deliberately ignored
41 | }
42 |
43 | @Override
44 | public void cancel() {
45 | // deliberately ignored
46 | }
47 |
48 | /**
49 | * Verifies that current is null, next is not null, otherwise signals errors
50 | * to the RxJavaPlugins and returns false.
51 | * @param current the current Subscription, expected to be null
52 | * @param next the next Subscription, expected to be non-null
53 | * @return true if the validation succeeded
54 | */
55 | public static boolean validate(Subscription current, Subscription next) {
56 | if (next == null) {
57 | RxJavaPlugins.onError(new NullPointerException("next is null"));
58 | return false;
59 | }
60 | if (current != null) {
61 | next.cancel();
62 | reportSubscriptionSet();
63 | return false;
64 | }
65 | return true;
66 | }
67 |
68 | /**
69 | * Reports that the subscription is already set to the RxJavaPlugins error handler,
70 | * which is an indication of a onSubscribe management bug.
71 | */
72 | public static void reportSubscriptionSet() {
73 | RxJavaPlugins.onError(new ProtocolViolationException("Subscription already set!"));
74 | }
75 |
76 | /**
77 | * Validates that the n is positive.
78 | * @param n the request amount
79 | * @return false if n is non-positive.
80 | */
81 | public static boolean validate(long n) {
82 | if (n <= 0) {
83 | RxJavaPlugins.onError(new IllegalArgumentException("n > 0 required but it was " + n));
84 | return false;
85 | }
86 | return true;
87 | }
88 |
89 |
90 | /**
91 | * Atomically sets the subscription on the field if it is still null.
92 | *
If the field is not null and doesn't contain the {@link #CANCELLED}
93 | * instance, the {@link #reportSubscriptionSet()} is called.
94 | * @param field the target field
95 | * @param s the new subscription to set
96 | * @return true if the operation succeeded, false if the target field was not null.
97 | */
98 | public static boolean setOnce(AtomicReference field, Subscription s) {
99 | Objects.requireNonNull(s, "s is null");
100 | if (!field.compareAndSet(null, s)) {
101 | s.cancel();
102 | if (field.get() != CANCELLED) {
103 | reportSubscriptionSet();
104 | }
105 | return false;
106 | }
107 | return true;
108 | }
109 |
110 | /**
111 | * Atomically swaps in the common cancelled subscription instance
112 | * and cancels the previous subscription if any.
113 | * @param field the target field to dispose the contents of
114 | * @return true if the swap from the non-cancelled instance to the
115 | * common cancelled instance happened in the caller's thread (allows
116 | * further one-time actions).
117 | */
118 | public static boolean cancel(AtomicReference field) {
119 | Subscription current = field.get();
120 | if (current != CANCELLED) {
121 | current = field.getAndSet(CANCELLED);
122 | if (current != CANCELLED) {
123 | if (current != null) {
124 | current.cancel();
125 | }
126 | return true;
127 | }
128 | }
129 | return false;
130 | }
131 |
132 | /**
133 | * Atomically sets the new Subscription on the field and requests any accumulated amount
134 | * from the requested field.
135 | * @param field the target field for the new Subscription
136 | * @param requested the current requested amount
137 | * @param s the new Subscription, not null (verified)
138 | * @return true if the Subscription was set the first time
139 | */
140 | public static boolean deferredSetOnce(AtomicReference field, AtomicLong requested,
141 | Subscription s) {
142 | if (SubscriptionHelper.setOnce(field, s)) {
143 | long r = requested.getAndSet(0L);
144 | if (r != 0L) {
145 | s.request(r);
146 | }
147 | return true;
148 | }
149 | return false;
150 | }
151 |
152 | /**
153 | * Atomically requests from the Subscription in the field if not null, otherwise accumulates
154 | * the request amount in the requested field to be requested once the field is set to non-null.
155 | * @param field the target field that may already contain a Subscription
156 | * @param requested the current requested amount
157 | * @param n the request amount, positive (verified)
158 | */
159 | public static void deferredRequest(AtomicReference field, AtomicLong requested, long n) {
160 | Subscription s = field.get();
161 | if (s != null) {
162 | s.request(n);
163 | } else {
164 | if (SubscriptionHelper.validate(n)) {
165 | BackpressureHelper.add(requested, n);
166 |
167 | s = field.get();
168 | if (s != null) {
169 | long r = requested.getAndSet(0L);
170 | if (r != 0L) {
171 | s.request(r);
172 | }
173 | }
174 | }
175 | }
176 | }
177 |
178 | /**
179 | * Atomically sets the subscription on the field if it is still null and issues a positive request
180 | * to the given {@link Subscription}.
181 | *
182 | * If the field is not null and doesn't contain the {@link #CANCELLED}
183 | * instance, the {@link #reportSubscriptionSet()} is called.
184 | * @param field the target field
185 | * @param s the new subscription to set
186 | * @param request the amount to request, positive (not verified)
187 | * @return true if the operation succeeded, false if the target field was not null.
188 | * @since 2.1.11
189 | */
190 | public static boolean setOnce(AtomicReference field, Subscription s, long request) {
191 | if (setOnce(field, s)) {
192 | s.request(request);
193 | return true;
194 | }
195 | return false;
196 | }
197 | }
--------------------------------------------------------------------------------
/src/main/java/hu/akarnokd/rxjava3/interop/SubscriptionV1ToDisposableV3.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2020 David Karnok
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package hu.akarnokd.rxjava3.interop;
18 |
19 | import io.reactivex.rxjava3.disposables.Disposable;
20 | import rx.Subscription;
21 |
22 | /**
23 | * Wraps a v1 Subscription and exposes it as a v3 Disposable.
24 | * @since 0.11.0
25 | * @author Artem Zinnatulin
26 | */
27 | final class SubscriptionV1ToDisposableV3 implements Disposable {
28 |
29 | private final Subscription subscription;
30 |
31 | SubscriptionV1ToDisposableV3(Subscription subscription) {
32 | this.subscription = subscription;
33 | }
34 |
35 | @Override
36 | public void dispose() {
37 | subscription.unsubscribe();
38 | }
39 |
40 | @Override
41 | public boolean isDisposed() {
42 | return subscription.isUnsubscribed();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/hu/akarnokd/rxjava3/interop/BackpressureHelperTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2020 David Karnok
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package hu.akarnokd.rxjava3.interop;
18 |
19 | import static org.junit.Assert.assertEquals;
20 |
21 | import java.util.List;
22 | import java.util.concurrent.atomic.AtomicLong;
23 |
24 | import org.junit.Test;
25 |
26 | import io.reactivex.rxjava3.plugins.RxJavaPlugins;
27 |
28 | public class BackpressureHelperTest {
29 | @Test
30 | public void constructorShouldBePrivate() {
31 | TestHelper.checkUtilityClass(BackpressureHelper.class);
32 | }
33 |
34 | @Test
35 | public void addCap() {
36 | assertEquals(2L, BackpressureHelper.addCap(1, 1));
37 | assertEquals(Long.MAX_VALUE, BackpressureHelper.addCap(1, Long.MAX_VALUE - 1));
38 | assertEquals(Long.MAX_VALUE, BackpressureHelper.addCap(1, Long.MAX_VALUE));
39 | assertEquals(Long.MAX_VALUE, BackpressureHelper.addCap(Long.MAX_VALUE - 1, Long.MAX_VALUE - 1));
40 | assertEquals(Long.MAX_VALUE, BackpressureHelper.addCap(Long.MAX_VALUE, Long.MAX_VALUE));
41 | }
42 |
43 | @Test
44 | public void multiplyCap() {
45 | assertEquals(6, BackpressureHelper.multiplyCap(2, 3));
46 | assertEquals(Long.MAX_VALUE, BackpressureHelper.multiplyCap(2, Long.MAX_VALUE));
47 | assertEquals(Long.MAX_VALUE, BackpressureHelper.multiplyCap(Long.MAX_VALUE, Long.MAX_VALUE));
48 | assertEquals(Long.MAX_VALUE, BackpressureHelper.multiplyCap(1L << 32, 1L << 32));
49 |
50 | }
51 |
52 | @Test
53 | public void producedMore() {
54 | List list = TestHelper.trackPluginErrors();
55 |
56 | try {
57 | AtomicLong requested = new AtomicLong(1);
58 |
59 | assertEquals(0, BackpressureHelper.produced(requested, 2));
60 |
61 | TestHelper.assertError(list, 0, IllegalStateException.class, "More produced than requested: -1");
62 | } finally {
63 | RxJavaPlugins.reset();
64 | }
65 | }
66 |
67 | @Test
68 | public void producedMoreCancel() {
69 | List list = TestHelper.trackPluginErrors();
70 |
71 | try {
72 | AtomicLong requested = new AtomicLong(1);
73 |
74 | assertEquals(0, BackpressureHelper.producedCancel(requested, 2));
75 |
76 | TestHelper.assertError(list, 0, IllegalStateException.class, "More produced than requested: -1");
77 | } finally {
78 | RxJavaPlugins.reset();
79 | }
80 | }
81 |
82 | @Test
83 | public void requestProduceRace() {
84 | final AtomicLong requested = new AtomicLong(1);
85 |
86 | for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) {
87 |
88 | Runnable r1 = new Runnable() {
89 | @Override
90 | public void run() {
91 | BackpressureHelper.produced(requested, 1);
92 | }
93 | };
94 |
95 | Runnable r2 = new Runnable() {
96 | @Override
97 | public void run() {
98 | BackpressureHelper.add(requested, 1);
99 | }
100 | };
101 |
102 | TestHelper.race(r1, r2);
103 | }
104 | }
105 |
106 | @Test
107 | public void requestCancelProduceRace() {
108 | final AtomicLong requested = new AtomicLong(1);
109 |
110 | for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) {
111 |
112 | Runnable r1 = new Runnable() {
113 | @Override
114 | public void run() {
115 | BackpressureHelper.produced(requested, 1);
116 | }
117 | };
118 |
119 | Runnable r2 = new Runnable() {
120 | @Override
121 | public void run() {
122 | BackpressureHelper.addCancel(requested, 1);
123 | }
124 | };
125 |
126 | TestHelper.race(r1, r2);
127 | }
128 | }
129 |
130 | @Test
131 | public void utilityClass() {
132 | TestHelper.checkUtilityClass(BackpressureHelper.class);
133 | }
134 |
135 | @Test
136 | public void capped() {
137 | final AtomicLong requested = new AtomicLong(Long.MIN_VALUE);
138 |
139 | assertEquals(Long.MIN_VALUE, BackpressureHelper.addCancel(requested, 1));
140 | assertEquals(Long.MIN_VALUE, BackpressureHelper.addCancel(requested, Long.MAX_VALUE));
141 |
142 | requested.set(0);
143 |
144 | assertEquals(0, BackpressureHelper.addCancel(requested, Long.MAX_VALUE));
145 | assertEquals(Long.MAX_VALUE, BackpressureHelper.addCancel(requested, 1));
146 | assertEquals(Long.MAX_VALUE, BackpressureHelper.addCancel(requested, Long.MAX_VALUE));
147 |
148 | requested.set(0);
149 |
150 | assertEquals(0, BackpressureHelper.add(requested, Long.MAX_VALUE));
151 | assertEquals(Long.MAX_VALUE, BackpressureHelper.add(requested, 1));
152 | assertEquals(Long.MAX_VALUE, BackpressureHelper.add(requested, Long.MAX_VALUE));
153 |
154 | assertEquals(Long.MAX_VALUE, BackpressureHelper.produced(requested, 1));
155 | assertEquals(Long.MAX_VALUE, BackpressureHelper.produced(requested, Long.MAX_VALUE));
156 | }
157 |
158 | @Test
159 | public void multiplyCap2() {
160 | assertEquals(Long.MAX_VALUE, BackpressureHelper.multiplyCap(3, Long.MAX_VALUE >> 1));
161 |
162 | assertEquals(Long.MAX_VALUE, BackpressureHelper.multiplyCap(1, Long.MAX_VALUE));
163 | }
164 |
165 | @Test
166 | public void alreadyNegativeMaxLong() {
167 | assertEquals(Long.MIN_VALUE, BackpressureHelper.addCancel(new AtomicLong(Long.MIN_VALUE), 1));
168 | }
169 |
170 | @Test
171 | public void alreadyMaxLong() {
172 | assertEquals(Long.MAX_VALUE, BackpressureHelper.addCancel(new AtomicLong(Long.MAX_VALUE), 1));
173 | }
174 |
175 | @Test
176 | public void producedNegativeMaxLong() {
177 | assertEquals(Long.MIN_VALUE, BackpressureHelper.producedCancel(new AtomicLong(Long.MIN_VALUE), 1));
178 | }
179 |
180 | @Test
181 | public void producedMaxLong() {
182 | assertEquals(Long.MAX_VALUE, BackpressureHelper.producedCancel(new AtomicLong(Long.MAX_VALUE), 1));
183 | }
184 | }
--------------------------------------------------------------------------------
/src/test/java/hu/akarnokd/rxjava3/interop/DisposableHelperTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2020 David Karnok
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package hu.akarnokd.rxjava3.interop;
18 |
19 | import static org.junit.Assert.*;
20 |
21 | import java.util.List;
22 | import java.util.concurrent.atomic.AtomicReference;
23 |
24 | import org.junit.Test;
25 |
26 | import io.reactivex.rxjava3.disposables.Disposable;
27 | import io.reactivex.rxjava3.exceptions.ProtocolViolationException;
28 | import io.reactivex.rxjava3.plugins.RxJavaPlugins;
29 |
30 | public class DisposableHelperTest {
31 | @Test
32 | public void enumMethods() {
33 | assertEquals(1, DisposableHelper.values().length);
34 | assertNotNull(DisposableHelper.valueOf("DISPOSED"));
35 | }
36 |
37 | @Test
38 | public void innerDisposed() {
39 | assertTrue(DisposableHelper.DISPOSED.isDisposed());
40 | DisposableHelper.DISPOSED.dispose();
41 | assertTrue(DisposableHelper.DISPOSED.isDisposed());
42 | }
43 |
44 | @Test
45 | public void validationNull() {
46 | List list = TestHelper.trackPluginErrors();
47 | try {
48 | assertFalse(DisposableHelper.validate(null, null));
49 |
50 | TestHelper.assertError(list, 0, NullPointerException.class, "next is null");
51 | } finally {
52 | RxJavaPlugins.reset();
53 | }
54 | }
55 |
56 | @Test
57 | public void disposeRace() {
58 | for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) {
59 | final AtomicReference d = new AtomicReference<>();
60 |
61 | Runnable r = new Runnable() {
62 | @Override
63 | public void run() {
64 | DisposableHelper.dispose(d);
65 | }
66 | };
67 |
68 | TestHelper.race(r, r);
69 | }
70 | }
71 |
72 | @Test
73 | public void dispose() {
74 | Disposable u = Disposable.empty();
75 | final AtomicReference d = new AtomicReference<>(u);
76 |
77 | DisposableHelper.dispose(d);
78 |
79 | assertTrue(u.isDisposed());
80 | }
81 |
82 | @Test
83 | public void trySet() {
84 | AtomicReference ref = new AtomicReference<>();
85 |
86 | Disposable d1 = Disposable.empty();
87 |
88 | assertTrue(DisposableHelper.trySet(ref, d1));
89 |
90 | Disposable d2 = Disposable.empty();
91 |
92 | assertFalse(DisposableHelper.trySet(ref, d2));
93 |
94 | assertFalse(d1.isDisposed());
95 |
96 | assertFalse(d2.isDisposed());
97 |
98 | DisposableHelper.dispose(ref);
99 |
100 | Disposable d3 = Disposable.empty();
101 |
102 | assertFalse(DisposableHelper.trySet(ref, d3));
103 |
104 | assertTrue(d3.isDisposed());
105 | }
106 |
107 | @Test
108 | public void reportDisposableSet() throws Throwable {
109 | TestHelper.withErrorTracking(errors -> {
110 |
111 | DisposableHelper.validate(Disposable.empty(), Disposable.empty());
112 |
113 | TestHelper.assertError(errors, 0, ProtocolViolationException.class);
114 | });
115 | }
116 |
117 | @Test
118 | public void isDisposed() {
119 | assertFalse(DisposableHelper.isDisposed(Disposable.empty()));
120 | assertTrue(DisposableHelper.isDisposed(DisposableHelper.DISPOSED));
121 | }
122 |
123 | @Test
124 | public void validate() {
125 | assertTrue(DisposableHelper.validate(null, Disposable.empty()));
126 | }
127 |
128 | @Test
129 | public void setOnceAndReport() throws Throwable {
130 | TestHelper.withErrorTracking(errors -> {
131 |
132 | AtomicReference ref = new AtomicReference<>(Disposable.empty());
133 |
134 | assertFalse(DisposableHelper.setOnce(ref, Disposable.empty()));
135 |
136 | TestHelper.assertError(errors, 0, ProtocolViolationException.class);
137 | });
138 | }
139 | }
--------------------------------------------------------------------------------
/src/test/java/hu/akarnokd/rxjava3/interop/RxJavaInteropV1SchedulerToV3SchedulerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2020 David Karnok
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package hu.akarnokd.rxjava3.interop;
18 |
19 | import static java.util.concurrent.TimeUnit.MINUTES;
20 | import static org.junit.Assert.*;
21 | import static org.mockito.Mockito.*;
22 |
23 | import java.util.concurrent.TimeUnit;
24 |
25 | import org.junit.Test;
26 |
27 | import rx.internal.schedulers.SchedulerLifecycle;
28 |
29 | public class RxJavaInteropV1SchedulerToV3SchedulerTest {
30 |
31 | @Test
32 | public void now() {
33 | rx.Scheduler v1Scheduler = mock(rx.Scheduler.class);
34 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
35 |
36 | when(v1Scheduler.now()).thenReturn(123L);
37 |
38 | assertEquals(123L, v3Scheduler.now(TimeUnit.MILLISECONDS));
39 | }
40 |
41 | @Test
42 | public void workerSchedule() {
43 | rx.schedulers.TestScheduler v1Scheduler = new rx.schedulers.TestScheduler();
44 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
45 |
46 | Runnable action0 = mock(Runnable.class);
47 |
48 | v3Scheduler.createWorker().schedule(action0);
49 | verifyNoInteractions(action0);
50 |
51 | v1Scheduler.triggerActions();
52 | verify(action0).run();
53 | }
54 |
55 | @Test
56 | public void workerScheduleNullAction() {
57 | rx.Scheduler v1Scheduler = mock(rx.Scheduler.class);
58 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
59 |
60 | try {
61 | v3Scheduler.createWorker().schedule(null);
62 | fail();
63 | } catch (NullPointerException expected) {
64 | assertEquals("Source 3.x Runnable is null", expected.getMessage());
65 | }
66 | }
67 |
68 | @Test
69 | public void workerScheduleDelayed() {
70 | rx.schedulers.TestScheduler v1Scheduler = new rx.schedulers.TestScheduler();
71 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
72 |
73 | Runnable action0 = mock(Runnable.class);
74 |
75 | v3Scheduler.createWorker().schedule(action0, 123L, MINUTES);
76 | verifyNoInteractions(action0);
77 |
78 | v1Scheduler.advanceTimeBy(122L, MINUTES);
79 | verifyNoInteractions(action0);
80 |
81 | v1Scheduler.advanceTimeBy(1L, MINUTES);
82 | verify(action0).run();
83 |
84 | v1Scheduler.advanceTimeBy(125L, MINUTES); // Check that it's not periodic.
85 | verifyNoMoreInteractions(action0);
86 | }
87 |
88 | @Test
89 | public void workerScheduleDelayedNullAction() {
90 | rx.Scheduler v1Scheduler = mock(rx.Scheduler.class);
91 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
92 |
93 | try {
94 | v3Scheduler.createWorker().schedule(null, 123L, MINUTES);
95 | fail();
96 | } catch (NullPointerException expected) {
97 | assertEquals("Source 3.x Runnable is null", expected.getMessage());
98 | }
99 | }
100 |
101 | @Test
102 | public void workerSchedulePeriodically() {
103 | rx.schedulers.TestScheduler v1Scheduler = new rx.schedulers.TestScheduler();
104 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
105 |
106 | Runnable action0 = mock(Runnable.class);
107 |
108 | v3Scheduler.createWorker().schedulePeriodically(action0, 10L, 123L, MINUTES);
109 | verifyNoInteractions(action0);
110 |
111 | v1Scheduler.advanceTimeBy(9L, MINUTES);
112 | verifyNoInteractions(action0);
113 |
114 | v1Scheduler.advanceTimeBy(1L, MINUTES);
115 | verify(action0).run();
116 |
117 | v1Scheduler.advanceTimeBy(122L, MINUTES);
118 | verifyNoMoreInteractions(action0);
119 |
120 | v1Scheduler.advanceTimeBy(1L, MINUTES);
121 | verify(action0, times(2)).run();
122 |
123 | v1Scheduler.advanceTimeBy(123L, MINUTES);
124 | verify(action0, times(3)).run(); // Verify periodic.
125 |
126 | v1Scheduler.advanceTimeBy(123L, MINUTES);
127 | verify(action0, times(4)).run(); // Verify periodic.
128 | }
129 |
130 | @Test
131 | public void workerSchedulePeriodicallyNullAction() {
132 | rx.Scheduler v1Scheduler = mock(rx.Scheduler.class);
133 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
134 |
135 | try {
136 | v3Scheduler.createWorker().schedulePeriodically(null, 10L, 123L, MINUTES);
137 | fail();
138 | } catch (NullPointerException expected) {
139 | assertEquals("Source 3.x Runnable is null", expected.getMessage());
140 | }
141 | }
142 |
143 | @Test
144 | public void workerNow() {
145 | rx.Scheduler v1Scheduler = mock(rx.Scheduler.class);
146 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
147 |
148 | rx.Scheduler.Worker v1Worker = mock(rx.Scheduler.Worker.class);
149 | when(v1Scheduler.createWorker()).thenReturn(v1Worker);
150 | io.reactivex.rxjava3.core.Scheduler.Worker v3Worker = v3Scheduler.createWorker();
151 |
152 | when(v1Worker.now()).thenReturn(123L);
153 |
154 | assertEquals(123L, v3Worker.now(TimeUnit.MILLISECONDS));
155 | }
156 |
157 | @Test
158 | public void workerUnsubscribe() {
159 | rx.Scheduler v1Scheduler = mock(rx.Scheduler.class);
160 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
161 |
162 | rx.Scheduler.Worker v1Worker = mock(rx.Scheduler.Worker.class);
163 | when(v1Scheduler.createWorker()).thenReturn(v1Worker);
164 |
165 | io.reactivex.rxjava3.core.Scheduler.Worker v3Worker = v3Scheduler.createWorker();
166 | verify(v1Worker, never()).unsubscribe();
167 |
168 | v3Worker.dispose();
169 | verify(v1Worker).unsubscribe();
170 | }
171 |
172 | @Test
173 | public void workerIsUnsubscribed() {
174 | rx.Scheduler v1Scheduler = mock(rx.Scheduler.class);
175 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
176 |
177 | rx.Scheduler.Worker v1Worker = mock(rx.Scheduler.Worker.class);
178 | when(v1Scheduler.createWorker()).thenReturn(v1Worker);
179 |
180 | io.reactivex.rxjava3.core.Scheduler.Worker v3Worker = v3Scheduler.createWorker();
181 |
182 | when(v1Worker.isUnsubscribed()).thenReturn(true);
183 | assertTrue(v3Worker.isDisposed());
184 |
185 | when(v1Worker.isUnsubscribed()).thenReturn(false);
186 | assertFalse(v3Worker.isDisposed());
187 | }
188 |
189 | @Test
190 | public void startStopNotSupported() {
191 | rx.Scheduler v1Scheduler = mock(rx.Scheduler.class);
192 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
193 |
194 | v3Scheduler.start();
195 |
196 | verifyNoMoreInteractions(v1Scheduler);
197 |
198 | v3Scheduler.shutdown();
199 |
200 | verifyNoMoreInteractions(v1Scheduler);
201 | }
202 |
203 | @Test
204 | public void startStopSupported() {
205 | rx.Scheduler v1Scheduler = mock(rx.Scheduler.class, withSettings().extraInterfaces(SchedulerLifecycle.class));
206 | io.reactivex.rxjava3.core.Scheduler v3Scheduler = RxJavaInterop.toV3Scheduler(v1Scheduler);
207 |
208 | v3Scheduler.start();
209 |
210 | ((SchedulerLifecycle)verify(v1Scheduler)).start();
211 |
212 | v3Scheduler.shutdown();
213 |
214 | ((SchedulerLifecycle)verify(v1Scheduler)).shutdown();
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/src/test/java/hu/akarnokd/rxjava3/interop/RxJavaInteropV3SchedulerToV1SchedulerTest.java:
--------------------------------------------------------------------------------
1 | package hu.akarnokd.rxjava3.interop;
2 |
3 | import static java.util.concurrent.TimeUnit.*;
4 | import static org.junit.Assert.*;
5 | import static org.mockito.Mockito.*;
6 |
7 | import org.junit.Test;
8 |
9 | import io.reactivex.rxjava3.core.Scheduler;
10 | import io.reactivex.rxjava3.schedulers.TestScheduler;
11 | import rx.functions.Action0;
12 | import rx.internal.schedulers.SchedulerLifecycle;
13 |
14 | public class RxJavaInteropV3SchedulerToV1SchedulerTest {
15 |
16 | @Test
17 | public void now() {
18 | Scheduler v3Scheduler = mock(Scheduler.class);
19 | rx.Scheduler v1Scheduler = RxJavaInterop.toV1Scheduler(v3Scheduler);
20 |
21 | when(v3Scheduler.now(MILLISECONDS)).thenReturn(123L);
22 |
23 | assertEquals(123L, v1Scheduler.now());
24 | }
25 |
26 | @Test
27 | public void workerSchedule() {
28 | TestScheduler v3Scheduler = new TestScheduler();
29 | rx.Scheduler v1Scheduler = RxJavaInterop.toV1Scheduler(v3Scheduler);
30 |
31 | Action0 action0 = mock(Action0.class);
32 |
33 | v1Scheduler.createWorker().schedule(action0);
34 | verifyNoInteractions(action0);
35 |
36 | v3Scheduler.triggerActions();
37 | verify(action0).call();
38 | }
39 |
40 | @Test
41 | public void workerScheduleNullAction() {
42 | Scheduler v3Scheduler = mock(Scheduler.class);
43 | rx.Scheduler v1Scheduler = RxJavaInterop.toV1Scheduler(v3Scheduler);
44 |
45 | try {
46 | v1Scheduler.createWorker().schedule(null);
47 | fail();
48 | } catch (NullPointerException expected) {
49 | assertEquals("Source 1.x Action0 is null", expected.getMessage());
50 | }
51 | }
52 |
53 | @Test
54 | public void workerScheduleDelayed() {
55 | TestScheduler v3Scheduler = new TestScheduler();
56 | rx.Scheduler v1Scheduler = RxJavaInterop.toV1Scheduler(v3Scheduler);
57 |
58 | Action0 action0 = mock(Action0.class);
59 |
60 | v1Scheduler.createWorker().schedule(action0, 123L, MINUTES);
61 | verifyNoInteractions(action0);
62 |
63 | v3Scheduler.advanceTimeBy(122L, MINUTES);
64 | verifyNoInteractions(action0);
65 |
66 | v3Scheduler.advanceTimeBy(1L, MINUTES);
67 | verify(action0).call();
68 |
69 | v3Scheduler.advanceTimeBy(125L, MINUTES); // Check that it's not periodic.
70 | verifyNoMoreInteractions(action0);
71 | }
72 |
73 | @Test
74 | public void workerScheduleDelayedNullAction() {
75 | Scheduler v3Scheduler = mock(Scheduler.class);
76 | rx.Scheduler v1Scheduler = RxJavaInterop.toV1Scheduler(v3Scheduler);
77 |
78 | try {
79 | v1Scheduler.createWorker().schedule(null, 123L, MINUTES);
80 | fail();
81 | } catch (NullPointerException expected) {
82 | assertEquals("Source 1.x Action0 is null", expected.getMessage());
83 | }
84 | }
85 |
86 | @Test
87 | public void workerSchedulePeriodically() {
88 | TestScheduler v3Scheduler = new TestScheduler();
89 | rx.Scheduler v1Scheduler = RxJavaInterop.toV1Scheduler(v3Scheduler);
90 |
91 | Action0 action0 = mock(Action0.class);
92 |
93 | v1Scheduler.createWorker().schedulePeriodically(action0, 10L, 123L, MINUTES);
94 | verifyNoInteractions(action0);
95 |
96 | v3Scheduler.advanceTimeBy(9L, MINUTES);
97 | verifyNoInteractions(action0);
98 |
99 | v3Scheduler.advanceTimeBy(1L, MINUTES);
100 | verify(action0).call();
101 |
102 | v3Scheduler.advanceTimeBy(122L, MINUTES);
103 | verifyNoMoreInteractions(action0);
104 |
105 | v3Scheduler.advanceTimeBy(1L, MINUTES);
106 | verify(action0, times(2)).call();
107 |
108 | v3Scheduler.advanceTimeBy(123L, MINUTES);
109 | verify(action0, times(3)).call(); // Verify periodic.
110 |
111 | v3Scheduler.advanceTimeBy(123L, MINUTES);
112 | verify(action0, times(4)).call(); // Verify periodic.
113 | }
114 |
115 | @Test
116 | public void workerSchedulePeriodicallyNullAction() {
117 | Scheduler v3Scheduler = mock(Scheduler.class);
118 | rx.Scheduler v1Scheduler = RxJavaInterop.toV1Scheduler(v3Scheduler);
119 |
120 | try {
121 | v1Scheduler.createWorker().schedulePeriodically(null, 10L, 123L, MINUTES);
122 | fail();
123 | } catch (NullPointerException expected) {
124 | assertEquals("Source 1.x Action0 is null", expected.getMessage());
125 | }
126 | }
127 |
128 | @Test
129 | public void workerNow() {
130 | Scheduler v3Scheduler = mock(Scheduler.class);
131 | rx.Scheduler v1Scheduler = RxJavaInterop.toV1Scheduler(v3Scheduler);
132 |
133 | Scheduler.Worker v3Worker = mock(Scheduler.Worker.class);
134 | when(v3Scheduler.createWorker()).thenReturn(v3Worker);
135 | rx.Scheduler.Worker v1Worker = v1Scheduler.createWorker();
136 |
137 | when(v3Worker.now(MILLISECONDS)).thenReturn(123L);
138 |
139 | assertEquals(123L, v1Worker.now());
140 | }
141 |
142 | @Test
143 | public void workerUnsubscribe() {
144 | Scheduler v3Scheduler = mock(Scheduler.class);
145 | rx.Scheduler v1Scheduler = RxJavaInterop.toV1Scheduler(v3Scheduler);
146 |
147 | Scheduler.Worker v3Worker = mock(Scheduler.Worker.class);
148 | when(v3Scheduler.createWorker()).thenReturn(v3Worker);
149 |
150 | rx.Scheduler.Worker v1Worker = v1Scheduler.createWorker();
151 | verify(v3Worker, never()).dispose();
152 |
153 | v1Worker.unsubscribe();
154 | verify(v3Worker).dispose();
155 | }
156 |
157 | @Test
158 | public void workerIsUnsubscribed() {
159 | Scheduler v3Scheduler = mock(Scheduler.class);
160 | rx.Scheduler v1Scheduler = RxJavaInterop.toV1Scheduler(v3Scheduler);
161 |
162 | Scheduler.Worker v3Worker = mock(Scheduler.Worker.class);
163 | when(v3Scheduler.createWorker()).thenReturn(v3Worker);
164 |
165 | rx.Scheduler.Worker v1Worker = v1Scheduler.createWorker();
166 |
167 | when(v3Worker.isDisposed()).thenReturn(true);
168 | assertTrue(v1Worker.isUnsubscribed());
169 |
170 | when(v3Worker.isDisposed()).thenReturn(false);
171 | assertFalse(v1Worker.isUnsubscribed());
172 | }
173 |
174 | @Test
175 | public void startStopSupport() {
176 | Scheduler v3Scheduler = mock(Scheduler.class);
177 | rx.Scheduler v1Scheduler = RxJavaInterop.toV1Scheduler(v3Scheduler);
178 |
179 | SchedulerLifecycle lc = (SchedulerLifecycle)v1Scheduler;
180 |
181 | lc.start();
182 |
183 | verify(v3Scheduler).start();
184 |
185 | lc.shutdown();
186 |
187 | verify(v3Scheduler).shutdown();
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/test/java/hu/akarnokd/rxjava3/interop/SubscriptionHelperTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2020 David Karnok
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package hu.akarnokd.rxjava3.interop;
18 |
19 | import static org.junit.Assert.*;
20 | import static org.mockito.ArgumentMatchers.anyLong;
21 | import static org.mockito.Mockito.*;
22 |
23 | import java.util.List;
24 | import java.util.concurrent.atomic.*;
25 |
26 | import org.junit.Test;
27 | import org.reactivestreams.Subscription;
28 |
29 | import io.reactivex.rxjava3.exceptions.ProtocolViolationException;
30 | import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription;
31 | import io.reactivex.rxjava3.plugins.RxJavaPlugins;
32 |
33 | public class SubscriptionHelperTest {
34 |
35 | @Test
36 | public void checkEnum() {
37 | TestHelper.checkEnum(SubscriptionHelper.class);
38 | }
39 |
40 | @Test
41 | public void validateNullThrows() {
42 | List errors = TestHelper.trackPluginErrors();
43 | try {
44 | SubscriptionHelper.validate(null, null);
45 |
46 | TestHelper.assertError(errors, 0, NullPointerException.class, "next is null");
47 | } finally {
48 | RxJavaPlugins.reset();
49 | }
50 | }
51 |
52 | @Test
53 | public void cancelNoOp() {
54 | SubscriptionHelper.CANCELLED.cancel();
55 | }
56 |
57 | @Test
58 | public void cancelRace() {
59 | for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) {
60 | final AtomicReference atomicSubscription = new AtomicReference<>();
61 |
62 | Runnable r = new Runnable() {
63 | @Override
64 | public void run() {
65 | SubscriptionHelper.cancel(atomicSubscription);
66 | }
67 | };
68 |
69 | TestHelper.race(r, r);
70 | }
71 | }
72 |
73 | @Test
74 | public void invalidDeferredRequest() {
75 | AtomicReference atomicSubscription = new AtomicReference<>();
76 | AtomicLong r = new AtomicLong();
77 |
78 | List