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
49    @Mock AlarmManager mAlarmManager;
50    WakeupMessage mMessage;
51    // Make a spy so that we can verify calls to it
52    @Spy MessageCapturingHandler mHandler = new MessageCapturingHandler();
53
54    ArgumentCaptor<AlarmManager.OnAlarmListener> mListenerCaptor =
55            ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
56
57    /**
58     * A Handler that will capture the most recent message sent to it.
59     *
60     * This handler is setup on the main Looper
61     */
62    public static class MessageCapturingHandler extends Handler {
63        private Message mLastMessage;
64
65        public MessageCapturingHandler() {
66            super(Looper.getMainLooper(), /* Nothing is actually dispatched on this Looper */
67                    null, false);
68        }
69
70        @Override
71        public void handleMessage(Message m) {
72            // need to copy since it will be recycled after this method returns
73            mLastMessage = Message.obtain(m);
74        }
75
76        public Message getLastMessage() {
77            return mLastMessage;
78        }
79    }
80
81    /**
82     * Sets up the test.
83     */
84    @Before
85    public void setUp() {
86        MockitoAnnotations.initMocks(this);
87
88        Context context = mock(Context.class);
89        when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
90        // capture the listener for each AlarmManager.setExact call
91        doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), any(String.class),
92                mListenerCaptor.capture(), any(Handler.class));
93
94        mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1,
95                TEST_ARG2);
96    }
97
98    /**
99     * Ensure the test is cleaned up and ready for the next test.
100     */
101    @After
102    public void cleanup() {
103        validateMockitoUsage();
104    }
105
106    private void scheduleAndVerifyAlarm(long when) {
107        mMessage.schedule(when);
108        verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(when),
109                eq(TEST_CMD_NAME), any(AlarmManager.OnAlarmListener.class), eq(mHandler));
110    }
111
112    private void verifyMessageDispatchedOnce() {
113        verify(mHandler, times(1)).handleMessage(any(Message.class));
114        assertEquals("what", TEST_CMD, mHandler.getLastMessage().what);
115        assertEquals("arg1", TEST_ARG1, mHandler.getLastMessage().arg1);
116        assertEquals("arg2", TEST_ARG2, mHandler.getLastMessage().arg2);
117    }
118
119    /**
120     * Schedule and deliver a single message
121     */
122    @Test
123    public void scheduleAndDeliverMessage() {
124        final long when = 1001;
125        scheduleAndVerifyAlarm(when);
126        verify(mHandler, never()).handleMessage(any(Message.class));
127        mListenerCaptor.getValue().onAlarm();
128        verifyMessageDispatchedOnce();
129    }
130
131    /**
132     * Check that the message is not delivered if cancel is called it after its alarm fires but
133     * before onAlarm is called.
134     *
135     * This ensures that if cancel is called on the handler thread, any previously-scheduled message
136     * is guaranteed not to be delivered.
137     */
138    @Test
139    public void scheduleAndCancelMessage() {
140        final long when = 1010;
141        scheduleAndVerifyAlarm(when);
142        mMessage.cancel();
143        mListenerCaptor.getValue().onAlarm();
144        verify(mHandler, never()).handleMessage(any(Message.class));
145    }
146
147    /**
148     * Verify nothing happens when cancel is called without a schedule
149     */
150    @Test
151    public void cancelWithoutSchedule() {
152        mMessage.cancel();
153    }
154
155    /**
156     * Verify that the message is silently rescheduled if schedule is called twice without the
157     * message being dispatched first.
158     */
159    @Test
160    public void scheduleTwiceWithoutMessageDispatched() {
161        final long when1 = 1011;
162        final long when2 = 1012;
163        scheduleAndVerifyAlarm(when1);
164        scheduleAndVerifyAlarm(when2);
165        mListenerCaptor.getValue().onAlarm();
166        verifyMessageDispatchedOnce();
167    }
168
169}
170