/* * Copyright 2022 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. */ #undef LOG_TAG #define LOG_TAG "AidlPowerHalWrapperTest" #include #include #include #include #include #include #include #include #include "DisplayHardware/PowerAdvisor.h" #include "android/hardware/power/WorkDuration.h" #include "binder/Status.h" #include "log/log_main.h" #include "mock/DisplayHardware/MockIPower.h" #include "mock/DisplayHardware/MockIPowerHintSession.h" #include "utils/Timers.h" using namespace android; using namespace android::Hwc2::mock; using namespace android::hardware::power; using namespace std::chrono_literals; using namespace testing; namespace android::Hwc2::impl { class AidlPowerHalWrapperTest : public testing::Test { public: void SetUp() override; protected: std::unique_ptr mWrapper = nullptr; sp> mMockHal = nullptr; sp> mMockSession = nullptr; void verifyAndClearExpectations(); void sendActualWorkDurationGroup(std::vector durations, std::chrono::nanoseconds sleepBeforeLastSend); std::chrono::nanoseconds mAllowedDeviation; std::chrono::nanoseconds mStaleTimeout; }; void AidlPowerHalWrapperTest::SetUp() { mMockHal = new NiceMock(); mMockSession = new NiceMock(); ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok())); mWrapper = std::make_unique(mMockHal); mWrapper->setAllowedActualDeviation(std::chrono::nanoseconds{10ms}.count()); mAllowedDeviation = std::chrono::nanoseconds{mWrapper->mAllowedActualDeviation}; mStaleTimeout = AidlPowerHalWrapper::kStaleTimeout; } void AidlPowerHalWrapperTest::verifyAndClearExpectations() { Mock::VerifyAndClearExpectations(mMockHal.get()); Mock::VerifyAndClearExpectations(mMockSession.get()); } void AidlPowerHalWrapperTest::sendActualWorkDurationGroup( std::vector durations, std::chrono::nanoseconds sleepBeforeLastSend) { for (size_t i = 0; i < durations.size(); i++) { if (i == durations.size() - 1) { std::this_thread::sleep_for(sleepBeforeLastSend); } auto duration = durations[i]; mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos); } } WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) { WorkDuration duration; duration.durationNanos = durationNanos.count(); duration.timeStampNanos = timeStampNanos; return duration; } WorkDuration toWorkDuration(std::pair timePair) { return toWorkDuration(timePair.first, timePair.second); } std::string printWorkDurations(const ::std::vector& durations) { std::ostringstream os; for (auto duration : durations) { os << duration.toString(); os << "\n"; } return os.str(); } namespace { TEST_F(AidlPowerHalWrapperTest, supportsPowerHintSession) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); Mock::VerifyAndClearExpectations(mMockHal.get()); ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)) .WillByDefault(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE))); auto newWrapper = AidlPowerHalWrapper(mMockHal); EXPECT_FALSE(newWrapper.supportsPowerHintSession()); } TEST_F(AidlPowerHalWrapperTest, startPowerHintSession) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); std::vector threadIds = {1, 2}; mWrapper->setPowerHintSessionThreadIds(threadIds); EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); EXPECT_TRUE(mWrapper->startPowerHintSession()); EXPECT_FALSE(mWrapper->startPowerHintSession()); } TEST_F(AidlPowerHalWrapperTest, restartNewPowerHintSessionWithNewThreadIds) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); std::vector threadIds = {1, 2}; EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); mWrapper->setPowerHintSessionThreadIds(threadIds); EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds); ASSERT_TRUE(mWrapper->startPowerHintSession()); verifyAndClearExpectations(); threadIds = {2, 3}; EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); EXPECT_CALL(*mMockSession.get(), close()).Times(1); mWrapper->setPowerHintSessionThreadIds(threadIds); EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds); verifyAndClearExpectations(); EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)).Times(0); EXPECT_CALL(*mMockSession.get(), close()).Times(0); mWrapper->setPowerHintSessionThreadIds(threadIds); verifyAndClearExpectations(); } TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); std::vector threadIds = {1, 2}; mWrapper->setPowerHintSessionThreadIds(threadIds); EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); ASSERT_TRUE(mWrapper->startPowerHintSession()); verifyAndClearExpectations(); std::chrono::nanoseconds base = 100ms; // test cases with target work duration and whether it should update hint against baseline 100ms const std::vector> testCases = {{0ms, true}, {-1ms, true}, {200ms, true}, {2ms, true}, {100ms, false}, {109ms, true}}; for (const auto& test : testCases) { // reset to 100ms baseline mWrapper->setTargetWorkDuration(1); mWrapper->setTargetWorkDuration(base.count()); auto target = test.first; EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(target.count())) .Times(test.second ? 1 : 0); mWrapper->setTargetWorkDuration(target.count()); verifyAndClearExpectations(); } } TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration_shouldReconnectOnError) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); std::vector threadIds = {1, 2}; mWrapper->setPowerHintSessionThreadIds(threadIds); EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); ASSERT_TRUE(mWrapper->startPowerHintSession()); verifyAndClearExpectations(); EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(1)) .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE))); mWrapper->setTargetWorkDuration(1); EXPECT_TRUE(mWrapper->shouldReconnectHAL()); } TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); std::vector threadIds = {1, 2}; mWrapper->setPowerHintSessionThreadIds(threadIds); EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); ASSERT_TRUE(mWrapper->startPowerHintSession()); verifyAndClearExpectations(); auto base = toWorkDuration(100ms, 0); // test cases with actual work durations and whether it should update hint against baseline // 100ms const std::vector>, bool>> testCases = {{{{-1ms, 100}}, false}, {{{100ms - (mAllowedDeviation / 2), 100}}, false}, {{{100ms + (mAllowedDeviation / 2), 100}}, false}, {{{100ms + (mAllowedDeviation + 1ms), 100}}, true}, {{{100ms - (mAllowedDeviation + 1ms), 100}}, true}, {{{100ms, 100}, {200ms, 200}}, true}, {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}}; for (const auto& test : testCases) { // reset actual duration sendActualWorkDurationGroup({base}, mStaleTimeout); auto raw = test.first; std::vector durations(raw.size()); std::transform(raw.begin(), raw.end(), durations.begin(), [](auto d) { return toWorkDuration(d); }); EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) .Times(test.second ? 1 : 0); sendActualWorkDurationGroup(durations, 0ms); verifyAndClearExpectations(); } } TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); std::vector threadIds = {1, 2}; mWrapper->setPowerHintSessionThreadIds(threadIds); EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); ASSERT_TRUE(mWrapper->startPowerHintSession()); verifyAndClearExpectations(); auto base = toWorkDuration(100ms, 0); // test cases with actual work durations and whether it should update hint against baseline // 100ms const std::vector>, std::chrono::nanoseconds, bool>> testCases = {{{{100ms, 100}}, mStaleTimeout, true}, {{{100ms + (mAllowedDeviation / 2), 100}}, mStaleTimeout, true}, {{{100ms, 100}}, mStaleTimeout / 2, false}}; for (const auto& test : testCases) { // reset actual duration sendActualWorkDurationGroup({base}, mStaleTimeout); auto raw = std::get<0>(test); std::vector durations(raw.size()); std::transform(raw.begin(), raw.end(), durations.begin(), [](auto d) { return toWorkDuration(d); }); EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) .Times(std::get<2>(test) ? 1 : 0); sendActualWorkDurationGroup(durations, std::get<1>(test)); verifyAndClearExpectations(); } } TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_shouldReconnectOnError) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); std::vector threadIds = {1, 2}; mWrapper->setPowerHintSessionThreadIds(threadIds); EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); ASSERT_TRUE(mWrapper->startPowerHintSession()); verifyAndClearExpectations(); WorkDuration duration; duration.durationNanos = 1; EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_)) .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE))); sendActualWorkDurationGroup({duration}, 0ms); EXPECT_TRUE(mWrapper->shouldReconnectHAL()); } } // namespace } // namespace android::Hwc2::impl