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 .*;
21
-
22
- import java .util .concurrent .atomic .AtomicInteger ;
23
-
24
18
import org .junit .Test ;
25
-
26
19
import rx .Observable ;
27
20
import rx .Observer ;
28
21
import rx .Subscription ;
22
+ import rx .subscriptions .Subscriptions ;
29
23
import rx .util .AtomicObservableSubscription ;
30
24
import rx .util .functions .Func1 ;
31
- import rx .util .functions .Func2 ;
32
- import rx .subjects .Subject ;
25
+
26
+ import java .util .concurrent .atomic .AtomicBoolean ;
27
+ import java .util .concurrent .atomic .AtomicInteger ;
28
+
29
+ import static org .junit .Assert .assertTrue ;
30
+ import static org .junit .Assert .fail ;
31
+ import static org .mockito .Matchers .any ;
32
+ import static org .mockito .Mockito .mock ;
33
+ import static org .mockito .Mockito .never ;
34
+ import static org .mockito .Mockito .times ;
35
+ import static org .mockito .Mockito .verify ;
36
+ import static rx .testing .TrustedObservableTester .assertTrustedObservable ;
37
+
33
38
/**
34
39
* Returns a specified number of contiguous values from the start of an observable sequence.
35
40
*/
@@ -43,61 +48,17 @@ public final class OperationTake {
43
48
* @return
44
49
*/
45
50
public static <T > Func1 <Observer <T >, Subscription > take (final Observable <T > items , final int num ) {
46
- return takeWhileWithIndex (items , OperationTake .<T > numPredicate (num ));
47
- }
48
-
49
- /**
50
- * Returns a specified number of contiguous values from the start of an observable sequence.
51
- *
52
- * @param items
53
- * @param predicate
54
- * a function to test each source element for a condition
55
- * @return
56
- */
57
- public static <T > Func1 <Observer <T >, Subscription > takeWhile (final Observable <T > items , final Func1 <T , Boolean > predicate ) {
58
- return takeWhileWithIndex (items , OperationTake .<T > skipIndex (predicate ));
59
- }
60
-
61
- /**
62
- * Returns values from an observable sequence as long as a specified condition is true, and then skips the remaining values.
63
- *
64
- * @param items
65
- * @param predicate
66
- * a function to test each element for a condition; the second parameter of the function represents the index of the source element; otherwise, false.
67
- * @return
68
- */
69
- public static <T > Func1 <Observer <T >, Subscription > takeWhileWithIndex (final Observable <T > items , final Func2 <T , Integer , Boolean > predicate ) {
70
51
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
71
52
return new Func1 <Observer <T >, Subscription >() {
72
53
73
54
@ Override
74
55
public Subscription call (Observer <T > observer ) {
75
- return new TakeWhile <T >(items , predicate ).call (observer );
56
+ return new Take <T >(items , num ).call (observer );
76
57
}
77
58
78
59
};
79
60
}
80
61
81
- private static <T > Func2 <T , Integer , Boolean > numPredicate (final int num ) {
82
- return new Func2 <T , Integer , Boolean >() {
83
-
84
- @ Override
85
- public Boolean call (T input , Integer index ) {
86
- return index < num ;
87
- }
88
-
89
- };
90
- }
91
-
92
- private static <T > Func2 <T , Integer , Boolean > skipIndex (final Func1 <T , Boolean > underlying ) {
93
- return new Func2 <T , Integer , Boolean >() {
94
- @ Override
95
- public Boolean call (T input , Integer index ) {
96
- return underlying .call (input );
97
- }
98
- };
99
- }
100
-
101
62
/**
102
63
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads.
103
64
* <p>
@@ -109,19 +70,41 @@ public Boolean call(T input, Integer index) {
109
70
*
110
71
* @param <T>
111
72
*/
112
- private static class TakeWhile <T > implements Func1 <Observer <T >, Subscription > {
73
+ private static class Take <T > implements Func1 <Observer <T >, Subscription > {
113
74
private final AtomicInteger counter = new AtomicInteger ();
114
75
private final Observable <T > items ;
115
- private final Func2 < T , Integer , Boolean > predicate ;
76
+ private final int num ;
116
77
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription ();
117
78
118
- private TakeWhile (Observable <T > items , Func2 < T , Integer , Boolean > predicate ) {
79
+ private Take (Observable <T > items , int num ) {
119
80
this .items = items ;
120
- this .predicate = predicate ;
81
+ this .num = num ;
121
82
}
122
83
123
84
@ Override
124
85
public Subscription call (Observer <T > observer ) {
86
+ if (num < 1 ) {
87
+ items .subscribe (new Observer <T >()
88
+ {
89
+ @ Override
90
+ public void onCompleted ()
91
+ {
92
+ }
93
+
94
+ @ Override
95
+ public void onError (Exception e )
96
+ {
97
+ }
98
+
99
+ @ Override
100
+ public void onNext (T args )
101
+ {
102
+ }
103
+ }).unsubscribe ();
104
+ observer .onCompleted ();
105
+ return Subscriptions .empty ();
106
+ }
107
+
125
108
return subscription .wrap (items .subscribe (new ItemObserver (observer )));
126
109
}
127
110
@@ -134,20 +117,28 @@ public ItemObserver(Observer<T> observer) {
134
117
135
118
@ Override
136
119
public void onCompleted () {
137
- observer .onCompleted ();
120
+ if (counter .getAndSet (num ) < num ) {
121
+ observer .onCompleted ();
122
+ }
138
123
}
139
124
140
125
@ Override
141
126
public void onError (Exception e ) {
142
- observer .onError (e );
127
+ if (counter .getAndSet (num ) < num ) {
128
+ observer .onError (e );
129
+ }
143
130
}
144
131
145
132
@ Override
146
133
public void onNext (T args ) {
147
- if (predicate .call (args , counter .getAndIncrement ())) {
134
+ final int count = counter .incrementAndGet ();
135
+ if (count <= num ) {
148
136
observer .onNext (args );
149
- } else {
150
- observer .onCompleted ();
137
+ if (count == num ) {
138
+ observer .onCompleted ();
139
+ }
140
+ }
141
+ if (count >= num ) {
151
142
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
152
143
subscription .unsubscribe ();
153
144
}
@@ -160,65 +151,9 @@ public void onNext(T args) {
160
151
public static class UnitTest {
161
152
162
153
@ Test
163
- public void testTakeWhile1 () {
164
- Observable <Integer > w = Observable .toObservable (1 , 2 , 3 );
165
- Observable <Integer > take = Observable .create (takeWhile (w , new Func1 <Integer , Boolean >() {
166
- @ Override
167
- public Boolean call (Integer input ) {
168
- return input < 3 ;
169
- }
170
- }));
171
-
172
- @ SuppressWarnings ("unchecked" )
173
- Observer <Integer > aObserver = mock (Observer .class );
174
- take .subscribe (aObserver );
175
- verify (aObserver , times (1 )).onNext (1 );
176
- verify (aObserver , times (1 )).onNext (2 );
177
- verify (aObserver , never ()).onNext (3 );
178
- verify (aObserver , never ()).onError (any (Exception .class ));
179
- verify (aObserver , times (1 )).onCompleted ();
180
- }
181
-
182
- @ Test
183
- public void testTakeWhileOnSubject1 () {
184
- Subject <Integer > s = Subject .create ();
185
- Observable <Integer > w = (Observable <Integer >)s ;
186
- Observable <Integer > take = Observable .create (takeWhile (w , new Func1 <Integer , Boolean >() {
187
- @ Override
188
- public Boolean call (Integer input ) {
189
- return input < 3 ;
190
- }
191
- }));
192
-
193
- @ SuppressWarnings ("unchecked" )
194
- Observer <Integer > aObserver = mock (Observer .class );
195
- take .subscribe (aObserver );
196
-
197
- s .onNext (1 );
198
- s .onNext (2 );
199
- s .onNext (3 );
200
- s .onNext (4 );
201
- s .onNext (5 );
202
- s .onCompleted ();
203
-
204
- verify (aObserver , times (1 )).onNext (1 );
205
- verify (aObserver , times (1 )).onNext (2 );
206
- verify (aObserver , never ()).onNext (3 );
207
- verify (aObserver , never ()).onNext (4 );
208
- verify (aObserver , never ()).onNext (5 );
209
- verify (aObserver , never ()).onError (any (Exception .class ));
210
- verify (aObserver , times (1 )).onCompleted ();
211
- }
212
-
213
- @ Test
214
- public void testTakeWhile2 () {
154
+ public void testTake1 () {
215
155
Observable <String > w = Observable .toObservable ("one" , "two" , "three" );
216
- Observable <String > take = Observable .create (takeWhileWithIndex (w , new Func2 <String , Integer , Boolean >() {
217
- @ Override
218
- public Boolean call (String input , Integer index ) {
219
- return index < 2 ;
220
- }
221
- }));
156
+ Observable <String > take = Observable .create (assertTrustedObservable (take (w , 2 )));
222
157
223
158
@ SuppressWarnings ("unchecked" )
224
159
Observer <String > aObserver = mock (Observer .class );
@@ -231,33 +166,59 @@ public Boolean call(String input, Integer index) {
231
166
}
232
167
233
168
@ Test
234
- public void testTake1 () {
169
+ public void testTake2 () {
235
170
Observable <String > w = Observable .toObservable ("one" , "two" , "three" );
236
- Observable <String > take = Observable .create (take (w , 2 ));
171
+ Observable <String > take = Observable .create (assertTrustedObservable ( take (w , 1 ) ));
237
172
238
173
@ SuppressWarnings ("unchecked" )
239
174
Observer <String > aObserver = mock (Observer .class );
240
175
take .subscribe (aObserver );
241
176
verify (aObserver , times (1 )).onNext ("one" );
242
- verify (aObserver , times ( 1 )).onNext ("two" );
177
+ verify (aObserver , never ( )).onNext ("two" );
243
178
verify (aObserver , never ()).onNext ("three" );
244
179
verify (aObserver , never ()).onError (any (Exception .class ));
245
180
verify (aObserver , times (1 )).onCompleted ();
246
181
}
247
182
248
183
@ Test
249
- public void testTake2 () {
250
- Observable <String > w = Observable .toObservable ("one" , "two" , "three" );
251
- Observable <String > take = Observable .create (take (w , 1 ));
184
+ public void testTakeDoesntLeakErrors () {
185
+ Observable <String > source = Observable .create (new Func1 <Observer <String >, Subscription >()
186
+ {
187
+ @ Override
188
+ public Subscription call (Observer <String > observer )
189
+ {
190
+ observer .onNext ("one" );
191
+ observer .onError (new Exception ("test failed" ));
192
+ return Subscriptions .empty ();
193
+ }
194
+ });
195
+ Observable .create (assertTrustedObservable (take (source , 1 ))).last ();
196
+ }
252
197
253
- @ SuppressWarnings ("unchecked" )
254
- Observer <String > aObserver = mock (Observer .class );
255
- take .subscribe (aObserver );
256
- verify (aObserver , times (1 )).onNext ("one" );
257
- verify (aObserver , never ()).onNext ("two" );
258
- verify (aObserver , never ()).onNext ("three" );
259
- verify (aObserver , never ()).onError (any (Exception .class ));
260
- verify (aObserver , times (1 )).onCompleted ();
198
+ @ Test
199
+ public void testTakeZeroDoesntLeakError () {
200
+ final AtomicBoolean subscribed = new AtomicBoolean (false );
201
+ final AtomicBoolean unSubscribed = new AtomicBoolean (false );
202
+ Observable <String > source = Observable .create (new Func1 <Observer <String >, Subscription >()
203
+ {
204
+ @ Override
205
+ public Subscription call (Observer <String > observer )
206
+ {
207
+ subscribed .set (true );
208
+ observer .onError (new Exception ("test failed" ));
209
+ return new Subscription ()
210
+ {
211
+ @ Override
212
+ public void unsubscribe ()
213
+ {
214
+ unSubscribed .set (true );
215
+ }
216
+ };
217
+ }
218
+ });
219
+ Observable .create (assertTrustedObservable (take (source , 0 ))).lastOrDefault ("ok" );
220
+ assertTrue ("source subscribed" , subscribed .get ());
221
+ assertTrue ("source unsubscribed" , unSubscribed .get ());
261
222
}
262
223
263
224
@ Test
@@ -267,7 +228,7 @@ public void testUnsubscribeAfterTake() {
267
228
268
229
@ SuppressWarnings ("unchecked" )
269
230
Observer <String > aObserver = mock (Observer .class );
270
- Observable <String > take = Observable .create (take (w , 1 ));
231
+ Observable <String > take = Observable .create (assertTrustedObservable ( take (w , 1 ) ));
271
232
take .subscribe (aObserver );
272
233
273
234
// wait for the Observable to complete
0 commit comments