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); |
| 55 | }; |
| 56 | |
| 57 | void AidlPowerHalWrapperTest::SetUp() { |
| 58 | mMockHal = new NiceMock<MockIPower>(); |
| 59 | mMockSession = new NiceMock<MockIPowerHintSession>(); |
| 60 | ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok())); |
| 61 | mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal); |
| 62 | } |
| 63 | |
| 64 | void AidlPowerHalWrapperTest::verifyAndClearExpectations() { |
| 65 | Mock::VerifyAndClearExpectations(mMockHal.get()); |
| 66 | Mock::VerifyAndClearExpectations(mMockSession.get()); |
| 67 | } |
| 68 | |
| 69 | void AidlPowerHalWrapperTest::sendActualWorkDurationGroup( |
| 70 | std::vector<WorkDuration> durations, std::chrono::nanoseconds sleepBeforeLastSend) { |
| 71 | for (size_t i = 0; i < durations.size(); i++) { |
| 72 | if (i == durations.size() - 1) { |
| 73 | std::this_thread::sleep_for(sleepBeforeLastSend); |
| 74 | } |
| 75 | auto duration = durations[i]; |
| 76 | mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos); |
| 77 | } |
| 78 | } |
| 79 | WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) { |
| 80 | WorkDuration duration; |
| 81 | duration.durationNanos = durationNanos.count(); |
| 82 | duration.timeStampNanos = timeStampNanos; |
| 83 | return duration; |
| 84 | } |
| 85 | |
Xiang Wang | 0aba49e | 2022-04-06 16:13:59 +0000 | [diff] [blame^] | 86 | std::string printWorkDurations(const ::std::vector<WorkDuration>& durations) { |
| 87 | std::ostringstream os; |
| 88 | for (auto duration : durations) { |
| 89 | os << duration.toString(); |
| 90 | os << "\n"; |
| 91 | } |
| 92 | return os.str(); |
| 93 | } |
| 94 | |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 95 | namespace { |
| 96 | TEST_F(AidlPowerHalWrapperTest, supportsPowerHintSession) { |
| 97 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 98 | Mock::VerifyAndClearExpectations(mMockHal.get()); |
| 99 | ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)) |
| 100 | .WillByDefault(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE))); |
| 101 | auto newWrapper = AidlPowerHalWrapper(mMockHal); |
| 102 | EXPECT_FALSE(newWrapper.supportsPowerHintSession()); |
| 103 | } |
| 104 | |
| 105 | TEST_F(AidlPowerHalWrapperTest, startPowerHintSession) { |
| 106 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 107 | std::vector<int32_t> threadIds = {1, 2}; |
| 108 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 109 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 110 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 111 | EXPECT_TRUE(mWrapper->startPowerHintSession()); |
| 112 | EXPECT_FALSE(mWrapper->startPowerHintSession()); |
| 113 | } |
| 114 | |
| 115 | TEST_F(AidlPowerHalWrapperTest, restartNewPoserHintSessionWithNewThreadIds) { |
| 116 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 117 | |
| 118 | std::vector<int32_t> threadIds = {1, 2}; |
| 119 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 120 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 121 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 122 | EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds); |
| 123 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 124 | verifyAndClearExpectations(); |
| 125 | |
| 126 | threadIds = {2, 3}; |
| 127 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 128 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 129 | EXPECT_CALL(*mMockSession.get(), close()).Times(1); |
| 130 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 131 | EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds); |
| 132 | verifyAndClearExpectations(); |
| 133 | |
| 134 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)).Times(0); |
| 135 | EXPECT_CALL(*mMockSession.get(), close()).Times(0); |
| 136 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 137 | verifyAndClearExpectations(); |
| 138 | } |
| 139 | |
| 140 | TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) { |
| 141 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 142 | |
| 143 | std::vector<int32_t> threadIds = {1, 2}; |
| 144 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 145 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 146 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 147 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 148 | verifyAndClearExpectations(); |
| 149 | |
| 150 | std::chrono::nanoseconds base = 100ms; |
| 151 | // test cases with target work duration and whether it should update hint against baseline 100ms |
| 152 | const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = {{0ms, false}, |
| 153 | {-1ms, false}, |
| 154 | {200ms, true}, |
| 155 | {2ms, true}, |
Xiang Wang | 0aba49e | 2022-04-06 16:13:59 +0000 | [diff] [blame^] | 156 | {91ms, false}, |
| 157 | {109ms, false}}; |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 158 | |
| 159 | for (const auto& test : testCases) { |
| 160 | // reset to 100ms baseline |
| 161 | mWrapper->setTargetWorkDuration(1); |
| 162 | mWrapper->setTargetWorkDuration(base.count()); |
| 163 | |
| 164 | auto target = test.first; |
| 165 | EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(target.count())) |
| 166 | .Times(test.second ? 1 : 0); |
| 167 | mWrapper->setTargetWorkDuration(target.count()); |
| 168 | verifyAndClearExpectations(); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration_shouldReconnectOnError) { |
| 173 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 174 | |
| 175 | std::vector<int32_t> threadIds = {1, 2}; |
| 176 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 177 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 178 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 179 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 180 | verifyAndClearExpectations(); |
| 181 | |
| 182 | EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(1)) |
| 183 | .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE))); |
| 184 | mWrapper->setTargetWorkDuration(1); |
| 185 | EXPECT_TRUE(mWrapper->shouldReconnectHAL()); |
| 186 | } |
| 187 | |
| 188 | TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) { |
| 189 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 190 | |
| 191 | std::vector<int32_t> threadIds = {1, 2}; |
| 192 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 193 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 194 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 195 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 196 | verifyAndClearExpectations(); |
| 197 | |
| 198 | auto base = toWorkDuration(100ms, 0); |
| 199 | // test cases with actual work durations and whether it should update hint against baseline |
| 200 | // 100ms |
| 201 | const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>> |
| 202 | testCases = {{{{-1ms, 100}}, false}, |
| 203 | {{{91ms, 100}}, false}, |
| 204 | {{{109ms, 100}}, false}, |
| 205 | {{{100ms, 100}, {200ms, 200}}, true}, |
| 206 | {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}}; |
| 207 | |
| 208 | for (const auto& test : testCases) { |
| 209 | // reset actual duration |
| 210 | sendActualWorkDurationGroup({base}, 80ms); |
| 211 | |
| 212 | auto raw = test.first; |
| 213 | std::vector<WorkDuration> durations(raw.size()); |
| 214 | std::transform(raw.begin(), raw.end(), durations.begin(), |
| 215 | [](std::pair<std::chrono::nanoseconds, nsecs_t> d) { |
| 216 | return toWorkDuration(d.first, d.second); |
| 217 | }); |
| 218 | EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) |
| 219 | .Times(test.second ? 1 : 0); |
| 220 | sendActualWorkDurationGroup(durations, 0ms); |
| 221 | verifyAndClearExpectations(); |
| 222 | } |
| 223 | } |
| 224 | |
Xiang Wang | 0aba49e | 2022-04-06 16:13:59 +0000 | [diff] [blame^] | 225 | TEST_F(AidlPowerHalWrapperTest, sendAdjustedActualWorkDuration) { |
| 226 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 227 | |
| 228 | std::vector<int32_t> threadIds = {1, 2}; |
| 229 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 230 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 231 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 232 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 233 | verifyAndClearExpectations(); |
| 234 | |
| 235 | std::chrono::nanoseconds lastTarget = 100ms; |
| 236 | EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(lastTarget.count())).Times(1); |
| 237 | mWrapper->setTargetWorkDuration(lastTarget.count()); |
| 238 | std::chrono::nanoseconds newTarget = 105ms; |
| 239 | mWrapper->setTargetWorkDuration(newTarget.count()); |
| 240 | EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(newTarget.count())).Times(0); |
| 241 | std::chrono::nanoseconds actual = 21ms; |
| 242 | // 100 / 105 * 21ms = 20ms |
| 243 | std::chrono::nanoseconds expectedActualSent = 20ms; |
| 244 | std::vector<WorkDuration> expectedDurations = {toWorkDuration(expectedActualSent, 1)}; |
| 245 | |
| 246 | EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_)) |
| 247 | .WillOnce(DoAll( |
| 248 | [expectedDurations](const ::std::vector<WorkDuration>& durationsSent) { |
| 249 | EXPECT_EQ(expectedDurations, durationsSent) |
| 250 | << base::StringPrintf("actual sent: %s vs expected: %s", |
| 251 | printWorkDurations(durationsSent).c_str(), |
| 252 | printWorkDurations(expectedDurations) |
| 253 | .c_str()); |
| 254 | }, |
| 255 | Return(Status::ok()))); |
| 256 | mWrapper->sendActualWorkDuration(actual.count(), 1); |
| 257 | } |
| 258 | |
Xiang Wang | e12b4fa | 2022-03-25 23:48:40 +0000 | [diff] [blame] | 259 | TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) { |
| 260 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 261 | |
| 262 | std::vector<int32_t> threadIds = {1, 2}; |
| 263 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 264 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 265 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 266 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 267 | verifyAndClearExpectations(); |
| 268 | |
| 269 | auto base = toWorkDuration(100ms, 0); |
| 270 | // test cases with actual work durations and whether it should update hint against baseline |
| 271 | // 100ms |
| 272 | const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>> |
| 273 | testCases = {{{{91ms, 100}}, true}, {{{109ms, 100}}, true}}; |
| 274 | |
| 275 | for (const auto& test : testCases) { |
| 276 | // reset actual duration |
| 277 | sendActualWorkDurationGroup({base}, 80ms); |
| 278 | |
| 279 | auto raw = test.first; |
| 280 | std::vector<WorkDuration> durations(raw.size()); |
| 281 | std::transform(raw.begin(), raw.end(), durations.begin(), |
| 282 | [](std::pair<std::chrono::nanoseconds, nsecs_t> d) { |
| 283 | return toWorkDuration(d.first, d.second); |
| 284 | }); |
| 285 | EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) |
| 286 | .Times(test.second ? 1 : 0); |
| 287 | sendActualWorkDurationGroup(durations, 80ms); |
| 288 | verifyAndClearExpectations(); |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_shouldReconnectOnError) { |
| 293 | ASSERT_TRUE(mWrapper->supportsPowerHintSession()); |
| 294 | |
| 295 | std::vector<int32_t> threadIds = {1, 2}; |
| 296 | mWrapper->setPowerHintSessionThreadIds(threadIds); |
| 297 | EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) |
| 298 | .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); |
| 299 | ASSERT_TRUE(mWrapper->startPowerHintSession()); |
| 300 | verifyAndClearExpectations(); |
| 301 | WorkDuration duration; |
| 302 | duration.durationNanos = 1; |
| 303 | EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_)) |
| 304 | .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE))); |
| 305 | sendActualWorkDurationGroup({duration}, 0ms); |
| 306 | EXPECT_TRUE(mWrapper->shouldReconnectHAL()); |
| 307 | } |
| 308 | |
| 309 | } // namespace |
| 310 | } // namespace android::Hwc2::impl |