Create CarPowerManagerUnitTest
Create unit tests for CarPowerManager
Fixes: 154546484
Test: This is the test
Change-Id: Icd8618239f1e908982c075ee155aebdc60eb1158
diff --git a/car-test-lib/src/android/car/test/mocks/JavaMockitoHelper.java b/car-test-lib/src/android/car/test/mocks/JavaMockitoHelper.java
index af50638..ab772a9 100644
--- a/car-test-lib/src/android/car/test/mocks/JavaMockitoHelper.java
+++ b/car-test-lib/src/android/car/test/mocks/JavaMockitoHelper.java
@@ -19,6 +19,7 @@
import android.util.Log;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
@@ -33,7 +34,7 @@
*
* @param timeoutMs how long to wait for
*
- * @throws IllegalStateException} if it times out.
+ * @throws {@link IllegalStateException} if it times out.
*/
public static void await(@NonNull CountDownLatch latch, long timeoutMs)
throws InterruptedException {
@@ -43,6 +44,20 @@
}
/**
+ * Waits for a semaphore.
+ *
+ * @param timeoutMs how long to wait for
+ *
+ * @throws {@link IllegalStateException} if it times out.
+ */
+ public static void await(@NonNull Semaphore semaphore, long timeoutMs)
+ throws InterruptedException {
+ if (!semaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ throw new IllegalStateException(semaphore + " not released in " + timeoutMs + " ms");
+ }
+ }
+
+ /**
* Silently waits for a latch to be counted down, without throwing any exception if it isn't.
*
* @param timeoutMs how long to wait for
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index d130c43..7446d35 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -169,7 +169,7 @@
}
@VisibleForTesting
- CarPowerManagementService(Context context, Resources resources, PowerHalService powerHal,
+ public CarPowerManagementService(Context context, Resources resources, PowerHalService powerHal,
SystemInterface systemInterface, UserManager userManager,
CarUserService carUserService, InitialUserSetter initialUserSetter) {
mContext = context;
@@ -194,7 +194,7 @@
}
@VisibleForTesting
- protected void setShutdownTimersForTest(int pollingIntervalMs, int shutdownTimeoutMs) {
+ public void setShutdownTimersForTest(int pollingIntervalMs, int shutdownTimeoutMs) {
// Override timers to keep testing time short
// Passing in '0' resets the value to the default
synchronized (mLock) {
diff --git a/tests/carservice_unit_test/src/com/android/car/hardware/power/CarPowerManagerUnitTest.java b/tests/carservice_unit_test/src/com/android/car/hardware/power/CarPowerManagerUnitTest.java
new file mode 100644
index 0000000..29022f5
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/hardware/power/CarPowerManagerUnitTest.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.hardware.power;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.car.Car;
+import android.car.hardware.power.CarPowerManager;
+import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.car.test.mocks.JavaMockitoHelper;
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReq;
+import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateShutdownParam;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.car.CarPowerManagementService;
+import com.android.car.MockedPowerHalService;
+import com.android.car.R;
+import com.android.car.hal.PowerHalService;
+import com.android.car.hal.PowerHalService.PowerState;
+import com.android.car.systeminterface.DisplayInterface;
+import com.android.car.systeminterface.SystemInterface;
+import com.android.car.systeminterface.SystemStateInterface;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+
+@SmallTest
+public class CarPowerManagerUnitTest extends AbstractExtendedMockitoTestCase {
+ private static final String TAG = CarPowerManagerUnitTest.class.getSimpleName();
+ private static final long WAIT_TIMEOUT_MS = 20;
+ private static final long WAIT_TIMEOUT_LONG_MS = 50;
+
+ private final MockDisplayInterface mDisplayInterface = new MockDisplayInterface();
+ private final MockSystemStateInterface mSystemStateInterface = new MockSystemStateInterface();
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ private MockedPowerHalService mPowerHal;
+ private SystemInterface mSystemInterface;
+ private CarPowerManagementService mService;
+ private CarPowerManager mCarPowerManager;
+
+ @Mock
+ private Resources mResources;
+ @Mock
+ private Car mCar;
+
+ @Before
+ public void setUp() throws Exception {
+ mPowerHal = new MockedPowerHalService(true /*isPowerStateSupported*/,
+ true /*isDeepSleepAllowed*/, true /*isTimedWakeupAllowed*/);
+ mSystemInterface = SystemInterface.Builder.defaultSystemInterface(mContext)
+ .withDisplayInterface(mDisplayInterface)
+ .withSystemStateInterface(mSystemStateInterface)
+ .build();
+ setService();
+ mCarPowerManager = new CarPowerManager(mCar, mService);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mService != null) {
+ mService.release();
+ }
+ }
+
+ @Test
+ public void testRequestShutdownOnNextSuspend_positive() throws Exception {
+ setPowerOn();
+ // Tell it to shutdown
+ mCarPowerManager.requestShutdownOnNextSuspend();
+ // Request suspend
+ setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+ VehicleApPowerStateShutdownParam.CAN_SLEEP);
+ // Verify shutdown
+ assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_SHUTDOWN_START, 0);
+ }
+
+ @Test
+ public void testRequestShutdownOnNextSuspend_negative() throws Exception {
+ setPowerOn();
+
+ // Do not tell it to shutdown
+
+ // Request suspend
+ setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+ VehicleApPowerStateShutdownParam.CAN_SLEEP);
+ // Verify suspend
+ assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
+ }
+
+ @Test
+ public void testScheduleNextWakeupTime() throws Exception {
+ setPowerOn();
+
+ int wakeTime = 1234;
+ mCarPowerManager.scheduleNextWakeupTime(wakeTime);
+
+ setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+ VehicleApPowerStateShutdownParam.CAN_SLEEP);
+
+ // Verify that we suspended with the requested wake-up time
+ assertStateReceivedForShutdownOrSleepWithPostpone(
+ PowerHalService.SET_DEEP_SLEEP_ENTRY, wakeTime);
+ }
+
+ @Test
+ public void testSetListener() throws Exception {
+ setPowerOn();
+
+ WaitablePowerStateListener listener = new WaitablePowerStateListener(2);
+
+ setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+ VehicleApPowerStateShutdownParam.CAN_SLEEP);
+
+ assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
+
+ int finalState = listener.await();
+ assertThat(finalState).isEqualTo(PowerHalService.SET_DEEP_SLEEP_ENTRY);
+ }
+
+ @Test
+ public void testSetListenerWithCompletion() throws Exception {
+ setPowerOn();
+
+ WaitablePowerStateListenerWithCompletion listener =
+ new WaitablePowerStateListenerWithCompletion(2);
+
+ setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+ VehicleApPowerStateShutdownParam.CAN_SLEEP);
+ assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
+
+ int finalState = listener.await();
+ assertThat(finalState).isEqualTo(PowerHalService.SET_DEEP_SLEEP_ENTRY);
+ }
+
+ @Test
+ public void testClearListener() throws Exception {
+ setPowerOn();
+
+ // Set a listener
+ WaitablePowerStateListener listener = new WaitablePowerStateListener(1);
+
+ mCarPowerManager.clearListener();
+
+ setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+ VehicleApPowerStateShutdownParam.CAN_SLEEP);
+
+ assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
+ // Verify that the listener didn't run
+ assertThrows(IllegalStateException.class, () -> listener.await());
+ }
+
+ @Test
+ public void testGetPowerState() throws Exception {
+ setPowerOn();
+ assertThat(mCarPowerManager.getPowerState()).isEqualTo(PowerHalService.SET_ON);
+
+ // Request suspend
+ setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
+ VehicleApPowerStateShutdownParam.CAN_SLEEP);
+ assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
+ assertThat(mCarPowerManager.getPowerState())
+ .isEqualTo(PowerHalService.SET_DEEP_SLEEP_ENTRY);
+ }
+
+ /**
+ * Helper method to create mService and initialize a test case
+ */
+ private void setService() throws Exception {
+ Log.i(TAG, "setService(): overridden overlay properties: "
+ + "config_disableUserSwitchDuringResume="
+ + mResources.getBoolean(R.bool.config_disableUserSwitchDuringResume)
+ + ", maxGarageModeRunningDurationInSecs="
+ + mResources.getInteger(R.integer.maxGarageModeRunningDurationInSecs));
+ mService = new CarPowerManagementService(mContext, mResources, mPowerHal,
+ mSystemInterface, null, null, null);
+ mService.init();
+ mService.setShutdownTimersForTest(0, 0);
+ assertStateReceived(MockedPowerHalService.SET_WAIT_FOR_VHAL, 0);
+ }
+
+ private void assertStateReceived(int expectedState, int expectedParam) throws Exception {
+ int[] state = mPowerHal.waitForSend(WAIT_TIMEOUT_MS);
+ assertThat(state).asList().containsExactly(expectedState, expectedParam).inOrder();
+ }
+
+ /**
+ * Helper method to get the system into ON
+ */
+ private void setPowerOn() throws Exception {
+ setPowerState(VehicleApPowerStateReq.ON, 0);
+ assertThat(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)).isTrue();
+ }
+
+ /**
+ * Helper to set the PowerHal state
+ *
+ * @param stateEnum Requested state enum
+ * @param stateParam Addition state parameter
+ */
+ private void setPowerState(int stateEnum, int stateParam) {
+ mPowerHal.setCurrentPowerState(new PowerState(stateEnum, stateParam));
+ }
+
+ private void assertStateReceivedForShutdownOrSleepWithPostpone(
+ int lastState, int stateParameter) throws Exception {
+ long startTime = System.currentTimeMillis();
+ while (true) {
+ int[] state = mPowerHal.waitForSend(WAIT_TIMEOUT_LONG_MS);
+ if (state[0] == lastState) {
+ assertThat(state[1]).isEqualTo(stateParameter);
+ return;
+ }
+ assertThat(state[0]).isEqualTo(PowerHalService.SET_SHUTDOWN_POSTPONE);
+ assertThat(System.currentTimeMillis() - startTime).isLessThan(WAIT_TIMEOUT_LONG_MS);
+ }
+ }
+
+ private static final class MockDisplayInterface implements DisplayInterface {
+ private boolean mDisplayOn = true;
+ private final Semaphore mDisplayStateWait = new Semaphore(0);
+
+ @Override
+ public void setDisplayBrightness(int brightness) {}
+
+ @Override
+ public synchronized void setDisplayState(boolean on) {
+ mDisplayOn = on;
+ mDisplayStateWait.release();
+ }
+
+ public synchronized boolean getDisplayState() {
+ return mDisplayOn;
+ }
+
+ public boolean waitForDisplayStateChange(long timeoutMs) throws Exception {
+ JavaMockitoHelper.await(mDisplayStateWait, timeoutMs);
+ return mDisplayOn;
+ }
+
+ @Override
+ public void startDisplayStateMonitoring(CarPowerManagementService service) {}
+
+ @Override
+ public void stopDisplayStateMonitoring() {}
+
+ @Override
+ public void refreshDisplayBrightness() {}
+ }
+
+ /**
+ * Helper class to set a power-state listener,
+ * verify that the listener gets called the
+ * right number of times, and return the final
+ * power state.
+ */
+ private final class WaitablePowerStateListener {
+ private final CountDownLatch mLatch;
+ private int mListenedState = -1;
+ WaitablePowerStateListener(int initialCount) {
+ mLatch = new CountDownLatch(initialCount);
+ mCarPowerManager.setListener(
+ (state) -> {
+ mListenedState = state;
+ mLatch.countDown();
+ });
+ }
+
+ int await() throws Exception {
+ JavaMockitoHelper.await(mLatch, WAIT_TIMEOUT_MS);
+ return mListenedState;
+ }
+ }
+
+ /**
+ * Helper class to set a power-state listener with completion,
+ * verify that the listener gets called the right number of times,
+ * verify that the CompletableFuture is provided, complete the
+ * CompletableFuture, and return the final power state.
+ */
+ private final class WaitablePowerStateListenerWithCompletion {
+ private final CountDownLatch mLatch;
+ private int mListenedState = -1;
+ WaitablePowerStateListenerWithCompletion(int initialCount) {
+ mLatch = new CountDownLatch(initialCount);
+ mCarPowerManager.setListenerWithCompletion(
+ (state, future) -> {
+ mListenedState = state;
+ if (state == PowerHalService.SET_SHUTDOWN_PREPARE) {
+ assertThat(future).isNotNull();
+ future.complete(null);
+ } else {
+ assertThat(future).isNull();
+ }
+ mLatch.countDown();
+ });
+ }
+
+ int await() throws Exception {
+ JavaMockitoHelper.await(mLatch, WAIT_TIMEOUT_MS);
+ return mListenedState;
+ }
+ }
+
+ private static final class MockSystemStateInterface implements SystemStateInterface {
+ private final Semaphore mShutdownWait = new Semaphore(0);
+ private final Semaphore mSleepWait = new Semaphore(0);
+ private final Semaphore mSleepExitWait = new Semaphore(0);
+ private boolean mWakeupCausedByTimer = false;
+
+ @Override
+ public void shutdown() {
+ mShutdownWait.release();
+ }
+
+ public void waitForShutdown(long timeoutMs) throws Exception {
+ JavaMockitoHelper.await(mShutdownWait, timeoutMs);
+ }
+
+ @Override
+ public boolean enterDeepSleep() {
+ mSleepWait.release();
+ try {
+ mSleepExitWait.acquire();
+ } catch (InterruptedException e) {
+ }
+ return true;
+ }
+
+ public void waitForSleepEntryAndWakeup(long timeoutMs) throws Exception {
+ JavaMockitoHelper.await(mSleepWait, timeoutMs);
+ mSleepExitWait.release();
+ }
+
+ @Override
+ public void scheduleActionForBootCompleted(Runnable action, Duration delay) {}
+
+ @Override
+ public boolean isWakeupCausedByTimer() {
+ Log.i(TAG, "isWakeupCausedByTimer:" + mWakeupCausedByTimer);
+ return mWakeupCausedByTimer;
+ }
+
+ public synchronized void setWakeupCausedByTimer(boolean set) {
+ mWakeupCausedByTimer = set;
+ }
+
+ @Override
+ public boolean isSystemSupportingDeepSleep() {
+ return true;
+ }
+ }
+}