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