1package com.xtremelabs.robolectric.shadows;
2
3import android.os.Handler;
4import android.os.Looper;
5import android.os.Message;
6import com.xtremelabs.robolectric.Robolectric;
7import com.xtremelabs.robolectric.WithTestDefaultsRunner;
8import com.xtremelabs.robolectric.util.TestRunnable;
9import com.xtremelabs.robolectric.util.Transcript;
10import org.junit.Before;
11import org.junit.Test;
12import org.junit.runner.RunWith;
13
14import static com.xtremelabs.robolectric.Robolectric.newInstanceOf;
15import static com.xtremelabs.robolectric.Robolectric.shadowOf;
16import static org.hamcrest.CoreMatchers.equalTo;
17import static org.hamcrest.CoreMatchers.nullValue;
18import static org.junit.Assert.assertThat;
19import static org.junit.Assert.assertTrue;
20import static org.junit.Assert.assertFalse;
21
22import java.util.ArrayList;
23import java.util.List;
24
25@RunWith(WithTestDefaultsRunner.class)
26public class HandlerTest {
27    private Transcript transcript;
28    TestRunnable scratchRunnable = new TestRunnable();
29
30    private Handler.Callback callback = new Handler.Callback() {
31        @Override
32        public boolean handleMessage(Message msg) {
33            hasHandlerCallbackHandledMessage = true;
34            return false;
35        }
36    };
37
38    private Boolean hasHandlerCallbackHandledMessage = false;
39
40    @Before
41    public void setUp() throws Exception {
42        transcript = new Transcript();
43    }
44
45    @Test
46    public void testInsertsRunnablesBasedOnLooper() throws Exception {
47        Looper looper = newInstanceOf(Looper.class);
48
49        Handler handler1 = new Handler(looper);
50        handler1.post(new Say("first thing"));
51
52        Handler handler2 = new Handler(looper);
53        handler2.post(new Say("second thing"));
54
55        shadowOf(looper).idle();
56
57        transcript.assertEventsSoFar("first thing", "second thing");
58    }
59
60    @Test
61    public void testDefaultConstructorUsesDefaultLooper() throws Exception {
62        Handler handler1 = new Handler();
63        handler1.post(new Say("first thing"));
64
65        Handler handler2 = new Handler(Looper.myLooper());
66        handler2.post(new Say("second thing"));
67
68        shadowOf(Looper.myLooper()).idle();
69
70        transcript.assertEventsSoFar("first thing", "second thing");
71    }
72
73    @Test
74    public void testDifferentLoopersGetDifferentQueues() throws Exception {
75        Looper looper1 = Robolectric.newInstanceOf(Looper.class);
76        Robolectric.pauseLooper(looper1);
77
78        Looper looper2 = Robolectric.newInstanceOf(Looper.class);
79        Robolectric.pauseLooper(looper2);
80
81        Handler handler1 = new Handler(looper1);
82        handler1.post(new Say("first thing"));
83
84        Handler handler2 = new Handler(looper2);
85        handler2.post(new Say("second thing"));
86
87        shadowOf(looper2).idle();
88
89        transcript.assertEventsSoFar("second thing");
90    }
91
92    @Test
93    public void shouldCallProvidedHandlerCallback() {
94        Handler handler = new Handler(callback);
95        handler.sendMessage(new Message());
96        assertTrue(hasHandlerCallbackHandledMessage);
97    }
98
99    @Test
100    public void testPostAndIdleMainLooper() throws Exception {
101        new Handler().post(scratchRunnable);
102        ShadowHandler.idleMainLooper();
103        assertThat(scratchRunnable.wasRun, equalTo(true));
104    }
105
106    @Test
107    public void postDelayedThenIdleMainLooper_shouldNotRunRunnable() throws Exception {
108        new Handler().postDelayed(scratchRunnable, 1);
109        ShadowHandler.idleMainLooper();
110        assertThat(scratchRunnable.wasRun, equalTo(false));
111    }
112
113    @Test
114    public void testPostDelayedThenRunMainLooperOneTask() throws Exception {
115        new Handler().postDelayed(scratchRunnable, 1);
116        ShadowHandler.runMainLooperOneTask();
117        assertThat(scratchRunnable.wasRun, equalTo(true));
118    }
119
120    @Test
121    public void testRemoveCallbacks() throws Exception {
122        Handler handler = new Handler();
123        ShadowLooper shadowLooper = shadowOf(handler.getLooper());
124        shadowLooper.pause();
125        handler.post(scratchRunnable);
126        handler.removeCallbacks(scratchRunnable);
127
128        shadowLooper.unPause();
129
130        assertThat(scratchRunnable.wasRun, equalTo(false));
131    }
132
133    @Test
134    public void testPostDelayedThenRunMainLooperToNextTask_shouldRunOneTask() throws Exception {
135        new Handler().postDelayed(scratchRunnable, 1);
136        ShadowHandler.runMainLooperToNextTask();
137        assertThat(scratchRunnable.wasRun, equalTo(true));
138    }
139
140    @Test
141    public void testPostDelayedTwiceThenRunMainLooperToNextTask_shouldRunMultipleTasks() throws Exception {
142        TestRunnable task1 = new TestRunnable();
143        TestRunnable task2 = new TestRunnable();
144
145        new Handler().postDelayed(task1, 1);
146        new Handler().postDelayed(task2, 1);
147
148        ShadowHandler.runMainLooperToNextTask();
149        assertThat(task1.wasRun, equalTo(true));
150        assertThat(task2.wasRun, equalTo(true));
151    }
152
153    @Test
154    public void testPostDelayedTwiceThenRunMainLooperOneTask_shouldRunOnlyOneTask() throws Exception {
155        TestRunnable task1 = new TestRunnable();
156        TestRunnable task2 = new TestRunnable();
157
158        new Handler().postDelayed(task1, 1);
159        new Handler().postDelayed(task2, 1);
160
161        ShadowHandler.runMainLooperOneTask();
162        assertThat(task1.wasRun, equalTo(true));
163        assertThat(task2.wasRun, equalTo(false));
164    }
165
166    @Test
167    public void testPostDelayedMultipleThenRunMainLooperOneTask_shouldRunMultipleTask() throws Exception {
168        TestRunnable task1 = new TestRunnable();
169        TestRunnable task2 = new TestRunnable();
170        TestRunnable task3 = new TestRunnable();
171
172        new Handler().postDelayed(task1, 1);
173        new Handler().postDelayed(task2, 10);
174        new Handler().postDelayed(task3, 100);
175
176        ShadowHandler.runMainLooperToEndOfTasks();
177        assertThat(task1.wasRun, equalTo(true));
178        assertThat(task2.wasRun, equalTo(true));
179        assertThat(task3.wasRun, equalTo(true));
180    }
181
182    @Test
183    public void testPostAtFrontOfQueueThenRunMainLooperOneTaskAtATime_shouldRunFrontOfQueueTaskFirst() throws Exception {
184        TestRunnable task1 = new TestRunnable();
185        TestRunnable task2 = new TestRunnable();
186
187        ShadowLooper.pauseMainLooper();
188        new Handler().post(task1);
189        boolean result = new Handler().postAtFrontOfQueue(task2);
190
191        assertTrue(result);
192
193        ShadowHandler.runMainLooperOneTask();
194        assertThat(task2.wasRun, equalTo(true));
195        assertThat(task1.wasRun, equalTo(false));
196        ShadowHandler.runMainLooperOneTask();
197        assertThat(task1.wasRun, equalTo(true));
198    }
199
200    @Test
201    public void testSendMessageAtFrontOfQueueThenRunMainLooperOneMsgAtATime_shouldRunFrontOfQueueMsgFirst() throws Exception {
202        Handler handler = new Handler();
203
204        ShadowLooper.pauseMainLooper();
205        // Post two messages to handler. Handle first message and confirm that msg posted
206        // to front is removed.
207        handler.obtainMessage(123).sendToTarget();
208        Message frontMsg = handler.obtainMessage(345);
209        boolean result = handler.sendMessageAtFrontOfQueue(frontMsg);
210
211        assertTrue(result);
212
213        assertTrue(handler.hasMessages(123));
214        assertTrue(handler.hasMessages(345));
215        ShadowHandler.runMainLooperOneTask();
216        assertTrue(handler.hasMessages(123));
217        assertFalse(handler.hasMessages(345));
218        ShadowHandler.runMainLooperOneTask();
219        assertFalse(handler.hasMessages(123));
220        assertFalse(handler.hasMessages(345));
221    }
222
223    @Test
224    public void sendEmptyMessage_addMessageToQueue() {
225        Robolectric.pauseMainLooper();
226        Handler handler = new Handler();
227        assertThat(handler.hasMessages(123), equalTo(false));
228        handler.sendEmptyMessage(123);
229        assertThat(handler.hasMessages(456), equalTo(false));
230        assertThat(handler.hasMessages(123), equalTo(true));
231        Robolectric.idleMainLooper(0);
232        assertThat(handler.hasMessages(123), equalTo(false));
233    }
234
235    @Test
236    public void sendEmptyMessageDelayed_sendsMessageAtCorrectTime() {
237        Robolectric.pauseMainLooper();
238        Handler handler = new Handler();
239        handler.sendEmptyMessageDelayed(123, 500);
240        assertThat(handler.hasMessages(123), equalTo(true));
241        Robolectric.idleMainLooper(100);
242        assertThat(handler.hasMessages(123), equalTo(true));
243        Robolectric.idleMainLooper(400);
244        assertThat(handler.hasMessages(123), equalTo(false));
245    }
246
247    @Test
248    public void removeMessages_takesMessageOutOfQueue() {
249        Robolectric.pauseMainLooper();
250        Handler handler = new Handler();
251        handler.sendEmptyMessageDelayed(123, 500);
252        handler.removeMessages(123);
253        assertThat(handler.hasMessages(123), equalTo(false));
254    }
255
256    @Test
257    public void removeMessage_withSpecifiedObject() throws Exception {
258        Robolectric.pauseMainLooper();
259        Handler handler = new Handler();
260        Message.obtain(handler, 123, "foo").sendToTarget();
261        Message.obtain(handler, 123, "bar").sendToTarget();
262
263        assertThat(handler.hasMessages(123), equalTo(true));
264        assertThat(handler.hasMessages(123, "foo"), equalTo(true));
265        assertThat(handler.hasMessages(123, "bar"), equalTo(true));
266        assertThat(handler.hasMessages(123, "baz"), equalTo(false));
267
268        handler.removeMessages(123, "foo");
269        assertThat(handler.hasMessages(123), equalTo(true));
270
271        handler.removeMessages(123, "bar");
272        assertThat(handler.hasMessages(123), equalTo(false));
273    }
274
275    @Test
276    public void testHasMessagesWithWhatAndObject() {
277        Robolectric.pauseMainLooper();
278        Object testObject = new Object();
279        Handler handler = new Handler();
280        Message message = handler.obtainMessage(123, testObject);
281
282        assertFalse(handler.hasMessages(123, testObject));
283
284        handler.sendMessage(message);
285
286        assertTrue(handler.hasMessages(123, testObject));
287    }
288
289    @Test
290    public void testSendToTarget() {
291        Robolectric.pauseMainLooper();
292        Object testObject = new Object();
293        Handler handler = new Handler();
294        Message message = handler.obtainMessage(123, testObject);
295
296        assertThat(handler, equalTo(message.getTarget()));
297
298        message.sendToTarget();
299
300        assertTrue(handler.hasMessages(123, testObject));
301    }
302
303    @Test
304    public void removeMessages_removesFromLooperQueueAsWell() {
305        final boolean[] wasRun = new boolean[1];
306        Robolectric.pauseMainLooper();
307        Handler handler = new Handler() {
308            @Override
309            public void handleMessage(Message msg) {
310                wasRun[0] = true;
311            }
312        };
313        handler.sendEmptyMessageDelayed(123, 500);
314        handler.removeMessages(123);
315        Robolectric.unPauseMainLooper();
316        assertThat(wasRun[0], equalTo(false));
317    }
318
319    @Test
320    public void shouldObtainMessage() throws Exception {
321        Message m0 = new Handler().obtainMessage();
322        assertThat(m0.what, equalTo(0));
323        assertThat(m0.obj, nullValue());
324
325        Message m1 = new Handler().obtainMessage(1);
326        assertThat(m1.what, equalTo(1));
327        assertThat(m1.obj, nullValue());
328
329        Message m2 = new Handler().obtainMessage(1, "foo");
330        assertThat(m2.what, equalTo(1));
331        assertThat(m2.obj, equalTo((Object)"foo"));
332
333        Message m3 = new Handler().obtainMessage(1, 2, 3);
334        assertThat(m3.what, equalTo(1));
335        assertThat(m3.arg1, equalTo(2));
336        assertThat(m3.arg2, equalTo(3));
337        assertThat(m3.obj, nullValue());
338
339        Message m4 = new Handler().obtainMessage(1, 2, 3, "foo");
340        assertThat(m4.what, equalTo(1));
341        assertThat(m4.arg1, equalTo(2));
342        assertThat(m4.arg2, equalTo(3));
343        assertThat(m4.obj, equalTo((Object)"foo"));
344    }
345
346    @Test
347    public void shouldSetWhenOnMessage() throws Exception {
348        final List<Message>  msgs = new ArrayList<Message>();
349        Handler h = new Handler(new Handler.Callback() {
350            @Override
351            public boolean handleMessage(Message msg) {
352                msgs.add(msg);
353                return false;
354            }
355        });
356
357        h.sendEmptyMessage(0);
358        h.sendEmptyMessageDelayed(0, 4000l);
359        Robolectric.getUiThreadScheduler().advanceToLastPostedRunnable();
360        h.sendEmptyMessageDelayed(0, 12000l);
361        Robolectric.getUiThreadScheduler().advanceToLastPostedRunnable();
362        assertThat(msgs.size(), equalTo(3));
363
364        Message m0 = msgs.get(0);
365        Message m1 = msgs.get(1);
366        Message m2 = msgs.get(2);
367
368        assertThat(m0.getWhen(), equalTo(0l));
369        assertThat(m1.getWhen(), equalTo(4000l));
370        assertThat(m2.getWhen(), equalTo(16000l));
371    }
372
373    @Test
374    public void shouldRemoveMessageFromQueueBeforeDispatching() throws Exception {
375        Handler h = new Handler(Looper.myLooper()) {
376            @Override
377            public void handleMessage(Message msg) {
378                assertFalse(hasMessages(0));
379            }
380        };
381        h.sendEmptyMessage(0);
382        h.sendMessageAtFrontOfQueue(h.obtainMessage());
383    }
384
385
386    private class Say implements Runnable {
387        private String event;
388
389        public Say(String event) {
390            this.event = event;
391        }
392
393        @Override
394        public void run() {
395            transcript.add(event);
396        }
397    }
398}
399