1/*
2 * Copyright (C) 2016 The Android Open Source Project
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
17package com.android.internal.util;
18
19import static org.junit.Assert.*;
20import static org.mockito.Mockito.*;
21
22import android.app.AlarmManager;
23import android.content.Context;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.Message;
27import android.test.suitebuilder.annotation.SmallTest;
28
29import org.junit.After;
30import org.junit.Before;
31import org.junit.Test;
32import org.mockito.ArgumentCaptor;
33import org.mockito.Mock;
34import org.mockito.MockitoAnnotations;
35import org.mockito.Spy;
36import org.mockito.invocation.InvocationOnMock;
37import org.mockito.stubbing.Answer;
38
39/**
40 * Unit tests for {@link com.android.internal.util.WakeupMessage}.
41 */
42@SmallTest
43public class WakeupMessageTest {
44    private static final String TEST_CMD_NAME = "TEST cmd Name";
45    private static final int TEST_CMD = 18;
46    private static final int TEST_ARG1 = 33;
47    private static final int TEST_ARG2 = 182;
48    private static final Object TEST_OBJ = "hello";
49
50    @Mock AlarmManager mAlarmManager;
51    WakeupMessage mMessage;
52    // Make a spy so that we can verify calls to it
53    @Spy MessageCapturingHandler mHandler = new MessageCapturingHandler();
54
55    ArgumentCaptor<AlarmManager.OnAlarmListener> mListenerCaptor =
56            ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
57
58    /**
59     * A Handler that will capture the most recent message sent to it.
60     *
61     * This handler is setup on the main Looper
62     */
63    public static class MessageCapturingHandler extends Handler {
64        private Message mLastMessage;
65
66        public MessageCapturingHandler() {
67            super(Looper.getMainLooper(), /* Nothing is actually dispatched on this Looper */
68                    null, false);
69        }
70
71        @Override
72        public void handleMessage(Message m) {
73            // need to copy since it will be recycled after this method returns
74            mLastMessage = Message.obtain(m);
75        }
76
77        public Message getLastMessage() {
78            return mLastMessage;
79        }
80    }
81
82    /**
83     * Sets up the test.
84     */
85    @Before
86    public void setUp() {
87        MockitoAnnotations.initMocks(this);
88
89        Context context = mock(Context.class);
90        when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
91        // capture the listener for each AlarmManager.setExact call
92        doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), any(String.class),
93                mListenerCaptor.capture(), any(Handler.class));
94
95        mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1,
96                TEST_ARG2, TEST_OBJ);
97    }
98
99    /**
100     * Ensure the test is cleaned up and ready for the next test.
101     */
102    @After
103    public void cleanup() {
104        validateMockitoUsage();
105    }
106
107    private void scheduleAndVerifyAlarm(long when) {
108        mMessage.schedule(when);
109        verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(when),
110                eq(TEST_CMD_NAME), any(AlarmManager.OnAlarmListener.class), eq(mHandler));
111    }
112
113    private void verifyMessageDispatchedOnce() {
114        verify(mHandler, times(1)).handleMessage(any(Message.class));
115        assertEquals("what", TEST_CMD, mHandler.getLastMessage().what);
116        assertEquals("arg1", TEST_ARG1, mHandler.getLastMessage().arg1);
117        assertEquals("arg2", TEST_ARG2, mHandler.getLastMessage().arg2);
118        assertEquals("obj", TEST_OBJ, mHandler.getLastMessage().obj);
119    }
120
121    /**
122     * Schedule and deliver a single message
123     */
124    @Test
125    public void scheduleAndDeliverMessage() {
126        final long when = 1001;
127        scheduleAndVerifyAlarm(when);
128        verify(mHandler, never()).handleMessage(any(Message.class));
129        mListenerCaptor.getValue().onAlarm();
130        verifyMessageDispatchedOnce();
131    }
132
133    /**
134     * Check that the message is not delivered if cancel is called it after its alarm fires but
135     * before onAlarm is called.
136     *
137     * This ensures that if cancel is called on the handler thread, any previously-scheduled message
138     * is guaranteed not to be delivered.
139     */
140    @Test
141    public void scheduleAndCancelMessage() {
142        final long when = 1010;
143        scheduleAndVerifyAlarm(when);
144        mMessage.cancel();
145        mListenerCaptor.getValue().onAlarm();
146        verify(mHandler, never()).handleMessage(any(Message.class));
147    }
148
149    /**
150     * Verify nothing happens when cancel is called without a schedule
151     */
152    @Test
153    public void cancelWithoutSchedule() {
154        mMessage.cancel();
155    }
156
157    /**
158     * Verify that the message is silently rescheduled if schedule is called twice without the
159     * message being dispatched first.
160     */
161    @Test
162    public void scheduleTwiceWithoutMessageDispatched() {
163        final long when1 = 1011;
164        final long when2 = 1012;
165        scheduleAndVerifyAlarm(when1);
166        scheduleAndVerifyAlarm(when2);
167        mListenerCaptor.getValue().onAlarm();
168        verifyMessageDispatchedOnce();
169    }
170
171}
172