Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2022 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 | #undef LOG_TAG |
| 18 | #define LOG_TAG "AidlPowerHalWrapperTest" |
| 19 | |
Xiang Wang | 0aba49e | 2022-04-06 16:13:59 +0000 | [diff] [blame] | 20 | #include <android-base/stringprintf.h> |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 21 | #include <android/hardware/power/IPower.h> |
| 22 | #include <android/hardware/power/IPowerHintSession.h> |
| 23 | #include <gmock/gmock.h> |
| 24 | #include <gtest/gtest.h> |
| 25 | #include <algorithm> |
| 26 | #include <chrono> |
| 27 | #include <memory> |
| 28 | #include "DisplayHardware/PowerAdvisor.h" |
| 29 | #include "android/hardware/power/WorkDuration.h" |
| 30 | #include "binder/Status.h" |
| 31 | #include "log/log_main.h" |
| 32 | #include "mock/DisplayHardware/MockIPower.h" |
| 33 | #include "mock/DisplayHardware/MockIPowerHintSession.h" |
| 34 | #include "utils/Timers.h" |
| 35 | |
| 36 | using namespace android; |
| 37 | using namespace android::Hwc2::mock; |
| 38 | using namespace android::hardware::power; |
| 39 | using namespace std::chrono_literals; |
| 40 | using namespace testing; |
| 41 | |
| 42 | namespace android::Hwc2::impl { |
| 43 | |
| 44 | class AidlPowerHalWrapperTest : public testing::Test { |
| 45 | public: |
| 46 | void SetUp() override; |
| 47 | |
| 48 | protected: |
| 49 | std::unique_ptr<AidlPowerHalWrapper> mWrapper = nullptr; |
| 50 | sp<NiceMock<MockIPower>> mMockHal = nullptr; |
| 51 | sp<NiceMock<MockIPowerHintSession>> mMockSession = nullptr; |
| 52 | void verifyAndClearExpectations(); |
| 53 | void sendActualWorkDurationGroup(std::vector<WorkDuration> durations, |
| 54 | std::chrono::nanoseconds sleepBeforeLastSend); |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 55 | std::chrono::nanoseconds mAllowedDeviation; |
| 56 | std::chrono::nanoseconds mStaleTimeout; |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 57 | }; |
| 58 | |
| 59 | void AidlPowerHalWrapperTest::SetUp() { |
| 60 | mMockHal = new NiceMock<MockIPower>(); |
| 61 | mMockSession = new NiceMock<MockIPowerHintSession>(); |
| 62 | ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok())); |
| 63 | mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal); |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 64 | mWrapper->setAllowedActualDeviation(std::chrono::nanoseconds{10ms}.count()); |
| 65 | mAllowedDeviation = std::chrono::nanoseconds{mWrapper->mAllowedActualDeviation}; |
| 66 | mStaleTimeout = AidlPowerHalWrapper::kStaleTimeout; |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 67 | } |
| 68 | |
| 69 | void AidlPowerHalWrapperTest::verifyAndClearExpectations() { |
| 70 | Mock::VerifyAndClearExpectations(mMockHal.get()); |
| 71 | Mock::VerifyAndClearExpectations(mMockSession.get()); |
| 72 | } |
| 73 | |
| 74 | void AidlPowerHalWrapperTest::sendActualWorkDurationGroup( |
| 75 | std::vector<WorkDuration> durations, std::chrono::nanoseconds sleepBeforeLastSend) { |
| 76 | for (size_t i = 0; i < durations.size(); i++) { |
| 77 | if (i == durations.size() - 1) { |
| 78 | std::this_thread::sleep_for(sleepBeforeLastSend); |
| 79 | } |
| 80 | auto duration = durations[i]; |
| 81 | mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos); |
| 82 | } |
| 83 | } |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 84 | |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 85 | WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) { |
| 86 | WorkDuration duration; |
| 87 | duration.durationNanos = durationNanos.count(); |
| 88 | duration.timeStampNanos = timeStampNanos; |
| 89 | return duration; |
| 90 | } |
| 91 | |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 92 | WorkDuration toWorkDuration(std::pair<std::chrono::nanoseconds, nsecs_t> timePair) { |
| 93 | return toWorkDuration(timePair.first, timePair.second); |
| 94 | } |
| 95 | |
Xiang Wang | 0aba49e | 2022-04-06 16:13:59 +0000 | [diff] [blame] | 96 | std::string printWorkDurations(const ::std::vector<WorkDuration>& durations) { |
| 97 | std::ostringstream os; |
| 98 | for (auto duration : durations) { |
| 99 | os << duration.toString(); |
| 100 | os << "\n"; |
| 101 | } |
| 102 | return os.str(); |
| 103 | } |
| 104 | |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 105 | namespace { |
| 106 | TEST_F(AidlPowerHalWrapperTest, supportsPowerHintSession) { |
| 107 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 108 | Mock::VerifyAndClearExpectations(mMockHal.get()); |
| 109 | ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)) |
| 110 | .WillByDefault(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE))); |
| 111 | auto newWrapper = AidlPowerHalWrapper(mMockHal); |
| 112 | EXPECT_FALSE(newWrapper.supportsPowerHintSession()); |
| 113 | } |
| 114 | |
| 115 | TEST_F(AidlPowerHalWrapperTest, startPowerHintSession) { |
| 116 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 117 | std::vector<int32_t> threadIds = {1, 2}; |
| 118 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 119 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 120 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 121 | EXPECT_TRUE(mWrapper->startPowerHintSession()); |
| 122 | EXPECT_FALSE(mWrapper->startPowerHintSession()); |
| 123 | } |
| 124 | |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 125 | TEST_F(AidlPowerHalWrapperTest, restartNewPowerHintSessionWithNewThreadIds) { |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 126 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 127 | |
| 128 | std::vector<int32_t> threadIds = {1, 2}; |
| 129 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 130 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 131 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 132 | EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds); |
| 133 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 134 | verifyAndClearExpectations(); |
| 135 | |
| 136 | threadIds = {2, 3}; |
| 137 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 138 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 139 | EXPECT_CALL(*mMockSession.get(), close()).Times(1); |
| 140 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 141 | EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds); |
| 142 | verifyAndClearExpectations(); |
| 143 | |
| 144 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)).Times(0); |
| 145 | EXPECT_CALL(*mMockSession.get(), close()).Times(0); |
| 146 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 147 | verifyAndClearExpectations(); |
| 148 | } |
| 149 | |
| 150 | TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) { |
| 151 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 152 | |
| 153 | std::vector<int32_t> threadIds = {1, 2}; |
| 154 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 155 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 156 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 157 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 158 | verifyAndClearExpectations(); |
| 159 | |
| 160 | std::chrono::nanoseconds base = 100ms; |
| 161 | // test cases with target work duration and whether it should update hint against baseline 100ms |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 162 | const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = |
| 163 | {{0ms, true}, {-1ms, true}, {200ms, true}, {2ms, true}, {100ms, false}, {109ms, true}}; |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 164 | |
| 165 | for (const auto& test : testCases) { |
| 166 | // reset to 100ms baseline |
| 167 | mWrapper->setTargetWorkDuration(1); |
| 168 | mWrapper->setTargetWorkDuration(base.count()); |
| 169 | |
| 170 | auto target = test.first; |
| 171 | EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(target.count())) |
| 172 | .Times(test.second ? 1 : 0); |
| 173 | mWrapper->setTargetWorkDuration(target.count()); |
| 174 | verifyAndClearExpectations(); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration_shouldReconnectOnError) { |
| 179 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 180 | |
| 181 | std::vector<int32_t> threadIds = {1, 2}; |
| 182 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 183 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 184 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 185 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 186 | verifyAndClearExpectations(); |
| 187 | |
| 188 | EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(1)) |
| 189 | .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE))); |
| 190 | mWrapper->setTargetWorkDuration(1); |
| 191 | EXPECT_TRUE(mWrapper->shouldReconnectHAL()); |
| 192 | } |
| 193 | |
| 194 | TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) { |
| 195 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 196 | |
| 197 | std::vector<int32_t> threadIds = {1, 2}; |
| 198 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 199 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 200 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 201 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 202 | verifyAndClearExpectations(); |
| 203 | |
| 204 | auto base = toWorkDuration(100ms, 0); |
| 205 | // test cases with actual work durations and whether it should update hint against baseline |
| 206 | // 100ms |
| 207 | const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>> |
| 208 | testCases = {{{{-1ms, 100}}, false}, |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 209 | {{{100ms - (mAllowedDeviation / 2), 100}}, false}, |
| 210 | {{{100ms + (mAllowedDeviation / 2), 100}}, false}, |
| 211 | {{{100ms + (mAllowedDeviation + 1ms), 100}}, true}, |
| 212 | {{{100ms - (mAllowedDeviation + 1ms), 100}}, true}, |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 213 | {{{100ms, 100}, {200ms, 200}}, true}, |
| 214 | {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}}; |
| 215 | |
| 216 | for (const auto& test : testCases) { |
| 217 | // reset actual duration |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 218 | sendActualWorkDurationGroup({base}, mStaleTimeout); |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 219 | |
| 220 | auto raw = test.first; |
| 221 | std::vector<WorkDuration> durations(raw.size()); |
| 222 | std::transform(raw.begin(), raw.end(), durations.begin(), |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 223 | [](auto d) { return toWorkDuration(d); }); |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 224 | EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) |
| 225 | .Times(test.second ? 1 : 0); |
| 226 | sendActualWorkDurationGroup(durations, 0ms); |
| 227 | verifyAndClearExpectations(); |
| 228 | } |
| 229 | } |
| 230 | |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 231 | TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) { |
| 232 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 233 | |
| 234 | std::vector<int32_t> threadIds = {1, 2}; |
| 235 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 236 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 237 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 238 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 239 | verifyAndClearExpectations(); |
| 240 | |
| 241 | auto base = toWorkDuration(100ms, 0); |
| 242 | // test cases with actual work durations and whether it should update hint against baseline |
| 243 | // 100ms |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 244 | const std::vector<std::tuple<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, |
| 245 | std::chrono::nanoseconds, bool>> |
| 246 | testCases = {{{{100ms, 100}}, mStaleTimeout, true}, |
| 247 | {{{100ms + (mAllowedDeviation / 2), 100}}, mStaleTimeout, true}, |
| 248 | {{{100ms, 100}}, mStaleTimeout / 2, false}}; |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 249 | |
| 250 | for (const auto& test : testCases) { |
| 251 | // reset actual duration |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 252 | sendActualWorkDurationGroup({base}, mStaleTimeout); |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 253 | |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 254 | auto raw = std::get<0>(test); |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 255 | std::vector<WorkDuration> durations(raw.size()); |
| 256 | std::transform(raw.begin(), raw.end(), durations.begin(), |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 257 | [](auto d) { return toWorkDuration(d); }); |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 258 | EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) |
Matt Buckley | 50c4406 | 2022-01-17 20:48:10 +0000 | [diff] [blame^] | 259 | .Times(std::get<2>(test) ? 1 : 0); |
| 260 | sendActualWorkDurationGroup(durations, std::get<1>(test)); |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 261 | verifyAndClearExpectations(); |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_shouldReconnectOnError) { |
| 266 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 267 | |
| 268 | std::vector<int32_t> threadIds = {1, 2}; |
| 269 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 270 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 271 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 272 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 273 | verifyAndClearExpectations(); |
| 274 | WorkDuration duration; |
| 275 | duration.durationNanos = 1; |
| 276 | EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_)) |
| 277 | .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE))); |
| 278 | sendActualWorkDurationGroup({duration}, 0ms); |
| 279 | EXPECT_TRUE(mWrapper->shouldReconnectHAL()); |
| 280 | } |
| 281 | |
| 282 | } // namespace |
| 283 | } // namespace android::Hwc2::impl |