15
15
*/
16
16
package rx .operators ;
17
17
18
- import static org .junit .Assert .*;
19
- import static org .mockito .Matchers .*;
20
- import static org .mockito .Mockito .*;
18
+ import static org .junit .Assert .assertEquals ;
19
+ import static org .junit .Assert .fail ;
20
+ import static org .mockito .Matchers .any ;
21
+ import static org .mockito .Mockito .inOrder ;
22
+ import static org .mockito .Mockito .mock ;
23
+ import static org .mockito .Mockito .never ;
24
+ import static org .mockito .Mockito .times ;
21
25
22
26
import java .util .concurrent .CountDownLatch ;
23
27
import java .util .concurrent .TimeUnit ;
28
32
import org .mockito .InOrder ;
29
33
30
34
import rx .Observable ;
31
- import rx .Observable .OnSubscribeFunc ;
35
+ import rx .Observable .OnSubscribe ;
32
36
import rx .Observer ;
33
37
import rx .Subscriber ;
34
38
import rx .Subscription ;
35
39
import rx .functions .Action0 ;
36
40
import rx .functions .Action1 ;
41
+ import rx .observers .TestSubscriber ;
37
42
import rx .subjects .PublishSubject ;
38
43
import rx .subscriptions .Subscriptions ;
39
44
@@ -114,7 +119,7 @@ public void testInfiniteRetry() {
114
119
inOrder .verifyNoMoreInteractions ();
115
120
}
116
121
117
- public static class FuncWithErrors implements Observable .OnSubscribeFunc <String > {
122
+ public static class FuncWithErrors implements Observable .OnSubscribe <String > {
118
123
119
124
private final int numFailures ;
120
125
private final AtomicInteger count = new AtomicInteger (0 );
@@ -124,15 +129,14 @@ public static class FuncWithErrors implements Observable.OnSubscribeFunc<String>
124
129
}
125
130
126
131
@ Override
127
- public Subscription onSubscribe ( Observer <? super String > o ) {
132
+ public void call ( Subscriber <? super String > o ) {
128
133
o .onNext ("beginningEveryTime" );
129
134
if (count .incrementAndGet () <= numFailures ) {
130
135
o .onError (new RuntimeException ("forced failure: " + count .get ()));
131
136
} else {
132
137
o .onNext ("onSuccessOnly" );
133
138
o .onCompleted ();
134
139
}
135
- return Subscriptions .empty ();
136
140
}
137
141
}
138
142
@@ -153,13 +157,13 @@ public void call(Integer n) {
153
157
}
154
158
155
159
@ Test
156
- public void testRetryAllowsSubscriptionAfterAllSubscriptionsUnsubsribed () throws InterruptedException {
160
+ public void testRetryAllowsSubscriptionAfterAllSubscriptionsUnsubscribed () throws InterruptedException {
157
161
final AtomicInteger subsCount = new AtomicInteger (0 );
158
- OnSubscribeFunc <String > onSubscribe = new OnSubscribeFunc <String >() {
162
+ OnSubscribe <String > onSubscribe = new OnSubscribe <String >() {
159
163
@ Override
160
- public Subscription onSubscribe ( Observer <? super String > observer ) {
164
+ public void call ( Subscriber <? super String > s ) {
161
165
subsCount .incrementAndGet ();
162
- return new Subscription () {
166
+ s . add ( new Subscription () {
163
167
boolean unsubscribed = false ;
164
168
165
169
@ Override
@@ -172,7 +176,7 @@ public void unsubscribe() {
172
176
public boolean isUnsubscribed () {
173
177
return unsubscribed ;
174
178
}
175
- };
179
+ }) ;
176
180
}
177
181
};
178
182
Observable <String > stream = Observable .create (onSubscribe );
@@ -185,40 +189,65 @@ public boolean isUnsubscribed() {
185
189
assertEquals (1 , subsCount .get ());
186
190
}
187
191
192
+ @ Test
193
+ public void testSourceObservableCallsUnsubscribe () throws InterruptedException {
194
+ final AtomicInteger subsCount = new AtomicInteger (0 );
195
+
196
+ final TestSubscriber <String > ts = new TestSubscriber <String >();
197
+
198
+ OnSubscribe <String > onSubscribe = new OnSubscribe <String >() {
199
+ @ Override
200
+ public void call (Subscriber <? super String > s ) {
201
+ // if isUnsubscribed is true that means we have a bug such as https://github.com/Netflix/RxJava/issues/1024
202
+ if (!s .isUnsubscribed ()) {
203
+ subsCount .incrementAndGet ();
204
+ s .onError (new RuntimeException ("failed" ));
205
+ // it unsubscribes the child directly
206
+ // this simulates various error/completion scenarios that could occur
207
+ // or just a source that proactively triggers cleanup
208
+ s .unsubscribe ();
209
+ }
210
+ }
211
+ };
212
+
213
+ Observable .create (onSubscribe ).retry (3 ).subscribe (ts );
214
+ assertEquals (4 , subsCount .get ()); // 1 + 3 retries
215
+ }
216
+
188
217
class SlowObservable implements Observable .OnSubscribe <Long > {
189
218
190
- private AtomicInteger efforts = new AtomicInteger (0 );
191
- private AtomicInteger active = new AtomicInteger (0 ),maxActive = new AtomicInteger (0 );
219
+ private AtomicInteger efforts = new AtomicInteger (0 );
220
+ private AtomicInteger active = new AtomicInteger (0 ), maxActive = new AtomicInteger (0 );
192
221
private AtomicInteger nextBeforeFailure ;
193
222
194
223
private final int emitDelay ;
195
224
196
- public SlowObservable (int emitDelay ,int countNext ) {
197
- this .emitDelay = emitDelay ;
198
- this .nextBeforeFailure = new AtomicInteger (countNext );
225
+ public SlowObservable (int emitDelay , int countNext ) {
226
+ this .emitDelay = emitDelay ;
227
+ this .nextBeforeFailure = new AtomicInteger (countNext );
199
228
}
200
229
201
230
public void call (final Subscriber <? super Long > subscriber ) {
202
- final AtomicBoolean terminate = new AtomicBoolean (false );
231
+ final AtomicBoolean terminate = new AtomicBoolean (false );
203
232
efforts .getAndIncrement ();
204
233
active .getAndIncrement ();
205
- maxActive .set (Math .max (active .get (),maxActive .get ()));
234
+ maxActive .set (Math .max (active .get (), maxActive .get ()));
206
235
final Thread thread = new Thread () {
207
236
@ Override
208
237
public void run () {
209
238
long nr = 0 ;
210
239
try {
211
240
while (!terminate .get ()) {
212
241
Thread .sleep (emitDelay );
213
- if (nextBeforeFailure .getAndDecrement ()> 0 ) {
242
+ if (nextBeforeFailure .getAndDecrement () > 0 ) {
214
243
subscriber .onNext (nr ++);
215
244
}
216
245
else {
217
246
subscriber .onError (new RuntimeException ("expected-failed" ));
218
247
}
219
248
}
220
249
}
221
- catch (InterruptedException t ) {
250
+ catch (InterruptedException t ) {
222
251
}
223
252
}
224
253
};
@@ -236,13 +265,13 @@ public void call() {
236
265
/** Observer for listener on seperate thread */
237
266
class AsyncObserver <T > implements Observer <T > {
238
267
239
- protected CountDownLatch latch = new CountDownLatch (1 );
268
+ protected CountDownLatch latch = new CountDownLatch (1 );
240
269
241
270
protected Observer <T > target ;
242
271
243
272
/** Wrap existing Observer */
244
273
public AsyncObserver (Observer <T > target ) {
245
- this .target = target ;
274
+ this .target = target ;
246
275
}
247
276
248
277
/** Wait */
@@ -274,19 +303,19 @@ public void onNext(T v) {
274
303
}
275
304
}
276
305
277
- @ Test
306
+ @ Test ( timeout = 1000 )
278
307
public void testUnsubscribeAfterError () {
279
308
280
309
@ SuppressWarnings ("unchecked" )
281
- Observer <Long > observer = mock (Observer .class );
310
+ Observer <Long > observer = mock (Observer .class );
282
311
283
312
// Observable that always fails after 100ms
284
- SlowObservable so = new SlowObservable (100 ,0 );
285
- Observable <Long > o = Observable
286
- .create (so )
287
- .retry (5 );
313
+ SlowObservable so = new SlowObservable (100 , 0 );
314
+ Observable <Long > o = Observable
315
+ .create (so )
316
+ .retry (5 );
288
317
289
- AsyncObserver <Long > async = new AsyncObserver <Long >(observer );
318
+ AsyncObserver <Long > async = new AsyncObserver <Long >(observer );
290
319
291
320
o .subscribe (async );
292
321
@@ -297,24 +326,24 @@ public void testUnsubscribeAfterError() {
297
326
inOrder .verify (observer , times (1 )).onError (any (Throwable .class ));
298
327
inOrder .verify (observer , never ()).onCompleted ();
299
328
300
- assertEquals ("Start 6 threads, retry 5 then fail on 6" ,6 , so .efforts .get ());
301
- assertEquals ("Only 1 active subscription" ,1 , so .maxActive .get ());
329
+ assertEquals ("Start 6 threads, retry 5 then fail on 6" , 6 , so .efforts .get ());
330
+ assertEquals ("Only 1 active subscription" , 1 , so .maxActive .get ());
302
331
}
303
332
304
- @ Test
333
+ @ Test ( timeout = 1000 )
305
334
public void testTimeoutWithRetry () {
306
335
307
336
@ SuppressWarnings ("unchecked" )
308
- Observer <Long > observer = mock (Observer .class );
337
+ Observer <Long > observer = mock (Observer .class );
309
338
310
339
// Observable that sends every 100ms (timeout fails instead)
311
- SlowObservable so = new SlowObservable (100 ,10 );
312
- Observable <Long > o = Observable
313
- .create (so )
314
- .timeout (80 , TimeUnit .MILLISECONDS )
315
- .retry (5 );
340
+ SlowObservable so = new SlowObservable (100 , 10 );
341
+ Observable <Long > o = Observable
342
+ .create (so )
343
+ .timeout (80 , TimeUnit .MILLISECONDS )
344
+ .retry (5 );
316
345
317
- AsyncObserver <Long > async = new AsyncObserver <Long >(observer );
346
+ AsyncObserver <Long > async = new AsyncObserver <Long >(observer );
318
347
319
348
o .subscribe (async );
320
349
@@ -325,6 +354,6 @@ public void testTimeoutWithRetry() {
325
354
inOrder .verify (observer , times (1 )).onError (any (Throwable .class ));
326
355
inOrder .verify (observer , never ()).onCompleted ();
327
356
328
- assertEquals ("Start 6 threads, retry 5 then fail on 6" ,6 , so .efforts .get ());
357
+ assertEquals ("Start 6 threads, retry 5 then fail on 6" , 6 , so .efforts .get ());
329
358
}
330
359
}
0 commit comments