20
20
import java .awt .EventQueue ;
21
21
import java .awt .event .ActionEvent ;
22
22
import java .awt .event .ActionListener ;
23
- import java .lang .reflect .InvocationTargetException ;
24
23
import java .util .concurrent .TimeUnit ;
25
24
import java .util .concurrent .atomic .AtomicReference ;
26
25
27
26
import javax .swing .Timer ;
28
27
28
+ import org .junit .Rule ;
29
29
import org .junit .Test ;
30
+ import org .junit .rules .ExpectedException ;
30
31
import org .mockito .InOrder ;
31
32
32
33
import rx .Scheduler ;
33
34
import rx .Subscription ;
34
35
import rx .subscriptions .CompositeSubscription ;
35
36
import rx .subscriptions .Subscriptions ;
36
37
import rx .util .functions .Action0 ;
38
+ import rx .util .functions .Func0 ;
37
39
import rx .util .functions .Func2 ;
38
40
39
41
/**
@@ -110,7 +112,6 @@ public void call() {
110
112
111
113
@ Override
112
114
public <T > Subscription schedulePeriodically (T state , final Func2 <Scheduler , T , Subscription > action , long initialDelay , long period , TimeUnit unit ) {
113
- // FIXME test this!
114
115
final AtomicReference <Timer > timer = new AtomicReference <Timer >();
115
116
116
117
final long delay = unit .toMillis (period );
@@ -119,20 +120,17 @@ public <T> Subscription schedulePeriodically(T state, final Func2<Scheduler, T,
119
120
final CompositeSubscription subscriptions = new CompositeSubscription ();
120
121
final Func2 <Scheduler , T , Subscription > initialAction = new Func2 <Scheduler , T , Subscription >() {
121
122
@ Override
122
- public Subscription call (final Scheduler scheduler , final T state ) {
123
- // call the action once initially
124
- subscriptions .add (action .call (scheduler , state ));
125
-
123
+ public Subscription call (final Scheduler scheduler , final T state0 ) {
126
124
// start timer for periodic execution, collect subscriptions
127
125
timer .set (new Timer ((int ) delay , new ActionListener () {
128
126
@ Override
129
127
public void actionPerformed (ActionEvent e ) {
130
- subscriptions .add (action .call (scheduler , state ));
128
+ subscriptions .add (action .call (scheduler , state0 ));
131
129
}
132
130
}));
133
131
timer .get ().start ();
134
132
135
- return action .call (scheduler , state );
133
+ return action .call (scheduler , state0 );
136
134
}
137
135
};
138
136
subscriptions .add (schedule (state , initialAction , initialDelay , unit ));
@@ -141,22 +139,68 @@ public void actionPerformed(ActionEvent e) {
141
139
@ Override
142
140
public void call () {
143
141
// in addition to all the individual unsubscriptions, stop the timer on unsubscribing
144
- timer .get ().stop ();
142
+ Timer maybeTimer = timer .get ();
143
+ if (maybeTimer != null ) {
144
+ maybeTimer .stop ();
145
+ }
145
146
}
146
147
}));
147
148
148
149
return subscriptions ;
149
150
}
150
151
151
152
private static void assertThatTheDelayIsValidForTheSwingTimer (long delay ) {
152
- if (delay > Integer .MAX_VALUE ) {
153
- throw new IllegalArgumentException (String .format ("The swing timer only accepts delays up to %d milliseconds." , Integer .MAX_VALUE ));
153
+ if (delay < 0 || delay > Integer .MAX_VALUE ) {
154
+ throw new IllegalArgumentException (String .format ("The swing timer only accepts non-negative delays up to %d milliseconds." , Integer .MAX_VALUE ));
154
155
}
155
156
}
156
157
157
158
public static class UnitTest {
159
+ @ Rule
160
+ public ExpectedException exception = ExpectedException .none ();
161
+
158
162
@ Test
159
- public void testNestedActions () throws InterruptedException , InvocationTargetException {
163
+ public void testInvalidDelayValues () {
164
+ final SwingScheduler scheduler = new SwingScheduler ();
165
+ final Action0 action = mock (Action0 .class );
166
+
167
+ exception .expect (IllegalArgumentException .class );
168
+ scheduler .schedulePeriodically (action , -1L , 100L , TimeUnit .SECONDS );
169
+
170
+ exception .expect (IllegalArgumentException .class );
171
+ scheduler .schedulePeriodically (action , 100L , -1L , TimeUnit .SECONDS );
172
+
173
+ exception .expect (IllegalArgumentException .class );
174
+ scheduler .schedulePeriodically (action , 1L + Integer .MAX_VALUE , 100L , TimeUnit .MILLISECONDS );
175
+
176
+ exception .expect (IllegalArgumentException .class );
177
+ scheduler .schedulePeriodically (action , 100L , 1L + Integer .MAX_VALUE / 1000 , TimeUnit .SECONDS );
178
+ }
179
+
180
+ @ Test
181
+ public void testPeriodicScheduling () throws Exception {
182
+ final SwingScheduler scheduler = new SwingScheduler ();
183
+
184
+ final Action0 innerAction = mock (Action0 .class );
185
+ final Action0 unsubscribe = mock (Action0 .class );
186
+ final Func0 <Subscription > action = new Func0 <Subscription >() {
187
+ @ Override
188
+ public Subscription call () {
189
+ innerAction .call ();
190
+ return Subscriptions .create (unsubscribe );
191
+ }
192
+ };
193
+
194
+ Subscription sub = scheduler .schedulePeriodically (action , 20 , 100 , TimeUnit .MILLISECONDS );
195
+ Thread .sleep (400 );
196
+ sub .unsubscribe ();
197
+ waitForEmptyEventQueue ();
198
+ verify (innerAction , times (4 )).call ();
199
+ verify (unsubscribe , times (4 )).call ();
200
+ }
201
+
202
+ @ Test
203
+ public void testNestedActions () throws Exception {
160
204
final SwingScheduler scheduler = new SwingScheduler ();
161
205
162
206
final Action0 firstStepStart = mock (Action0 .class );
@@ -195,12 +239,7 @@ public void call() {
195
239
InOrder inOrder = inOrder (firstStepStart , firstStepEnd , secondStepStart , secondStepEnd , thirdStepStart , thirdStepEnd );
196
240
197
241
scheduler .schedule (thirdAction );
198
- EventQueue .invokeAndWait (new Runnable () {
199
- @ Override
200
- public void run () {
201
- // nothing to do, we're just waiting here for the event queue to be emptied
202
- }
203
- });
242
+ waitForEmptyEventQueue ();
204
243
205
244
inOrder .verify (thirdStepStart , times (1 )).call ();
206
245
inOrder .verify (thirdStepEnd , times (1 )).call ();
@@ -210,5 +249,13 @@ public void run() {
210
249
inOrder .verify (firstStepEnd , times (1 )).call ();
211
250
}
212
251
252
+ private static void waitForEmptyEventQueue () throws Exception {
253
+ EventQueue .invokeAndWait (new Runnable () {
254
+ @ Override
255
+ public void run () {
256
+ // nothing to do, we're just waiting here for the event queue to be emptied
257
+ }
258
+ });
259
+ }
213
260
}
214
261
}
0 commit comments