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