Matt Buckley | 0c66836 | 2023-09-07 05:52:07 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2023 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 | #include <gmock/gmock.h> |
| 18 | #include <gtest/gtest.h> |
| 19 | #include <private/performance_hint_private.h> |
| 20 | #include <renderthread/HintSessionWrapper.h> |
| 21 | #include <utils/Log.h> |
| 22 | |
| 23 | #include <chrono> |
| 24 | |
| 25 | #include "Properties.h" |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 26 | #include "tests/common/TestUtils.h" |
Matt Buckley | 0c66836 | 2023-09-07 05:52:07 +0000 | [diff] [blame] | 27 | |
| 28 | using namespace testing; |
| 29 | using namespace std::chrono_literals; |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 30 | using namespace android::uirenderer::renderthread; |
Matt Buckley | 0c66836 | 2023-09-07 05:52:07 +0000 | [diff] [blame] | 31 | |
| 32 | APerformanceHintManager* managerPtr = reinterpret_cast<APerformanceHintManager*>(123); |
| 33 | APerformanceHintSession* sessionPtr = reinterpret_cast<APerformanceHintSession*>(456); |
| 34 | int uiThreadId = 1; |
| 35 | int renderThreadId = 2; |
| 36 | |
| 37 | namespace android::uirenderer::renderthread { |
| 38 | |
| 39 | class HintSessionWrapperTests : public testing::Test { |
| 40 | public: |
| 41 | void SetUp() override; |
| 42 | void TearDown() override; |
| 43 | |
| 44 | protected: |
| 45 | std::shared_ptr<HintSessionWrapper> mWrapper; |
| 46 | |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 47 | std::promise<int> blockDestroyCallUntil; |
| 48 | std::promise<int> waitForDestroyFinished; |
| 49 | |
Matt Buckley | 0c66836 | 2023-09-07 05:52:07 +0000 | [diff] [blame] | 50 | class MockHintSessionBinding : public HintSessionWrapper::HintSessionBinding { |
| 51 | public: |
| 52 | void init() override; |
| 53 | |
| 54 | MOCK_METHOD(APerformanceHintManager*, fakeGetManager, ()); |
| 55 | MOCK_METHOD(APerformanceHintSession*, fakeCreateSession, |
| 56 | (APerformanceHintManager*, const int32_t*, size_t, int64_t)); |
| 57 | MOCK_METHOD(void, fakeCloseSession, (APerformanceHintSession*)); |
| 58 | MOCK_METHOD(void, fakeUpdateTargetWorkDuration, (APerformanceHintSession*, int64_t)); |
| 59 | MOCK_METHOD(void, fakeReportActualWorkDuration, (APerformanceHintSession*, int64_t)); |
| 60 | MOCK_METHOD(void, fakeSendHint, (APerformanceHintSession*, int32_t)); |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 61 | // Needs to be on the binding so it can be accessed from static methods |
| 62 | std::promise<int> allowCreationToFinish; |
Matt Buckley | 0c66836 | 2023-09-07 05:52:07 +0000 | [diff] [blame] | 63 | }; |
| 64 | |
| 65 | // Must be static so it can have function pointers we can point to with static methods |
| 66 | static std::shared_ptr<MockHintSessionBinding> sMockBinding; |
| 67 | |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 68 | static void allowCreationToFinish() { sMockBinding->allowCreationToFinish.set_value(1); } |
| 69 | void allowDelayedDestructionToStart() { blockDestroyCallUntil.set_value(1); } |
| 70 | void waitForDelayedDestructionToFinish() { waitForDestroyFinished.get_future().wait(); } |
| 71 | |
Matt Buckley | 0c66836 | 2023-09-07 05:52:07 +0000 | [diff] [blame] | 72 | // Must be static so we can point to them as normal fn pointers with HintSessionBinding |
| 73 | static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); }; |
| 74 | static APerformanceHintSession* stubCreateSession(APerformanceHintManager* manager, |
| 75 | const int32_t* ids, size_t idsSize, |
| 76 | int64_t initialTarget) { |
| 77 | return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget); |
| 78 | } |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 79 | static APerformanceHintSession* stubManagedCreateSession(APerformanceHintManager* manager, |
| 80 | const int32_t* ids, size_t idsSize, |
| 81 | int64_t initialTarget) { |
| 82 | sMockBinding->allowCreationToFinish.get_future().wait(); |
| 83 | return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget); |
| 84 | } |
Matt Buckley | 0c66836 | 2023-09-07 05:52:07 +0000 | [diff] [blame] | 85 | static APerformanceHintSession* stubSlowCreateSession(APerformanceHintManager* manager, |
| 86 | const int32_t* ids, size_t idsSize, |
| 87 | int64_t initialTarget) { |
| 88 | std::this_thread::sleep_for(50ms); |
| 89 | return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget); |
| 90 | } |
| 91 | static void stubCloseSession(APerformanceHintSession* session) { |
| 92 | sMockBinding->fakeCloseSession(session); |
| 93 | }; |
| 94 | static void stubUpdateTargetWorkDuration(APerformanceHintSession* session, |
| 95 | int64_t workDuration) { |
| 96 | sMockBinding->fakeUpdateTargetWorkDuration(session, workDuration); |
| 97 | } |
| 98 | static void stubReportActualWorkDuration(APerformanceHintSession* session, |
| 99 | int64_t workDuration) { |
| 100 | sMockBinding->fakeReportActualWorkDuration(session, workDuration); |
| 101 | } |
| 102 | static void stubSendHint(APerformanceHintSession* session, int32_t hintId) { |
| 103 | sMockBinding->fakeSendHint(session, hintId); |
| 104 | }; |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 105 | void waitForWrapperReady() { |
| 106 | if (mWrapper->mHintSessionFuture.has_value()) { |
| 107 | mWrapper->mHintSessionFuture->wait(); |
| 108 | } |
| 109 | } |
| 110 | void scheduleDelayedDestroyManaged() { |
| 111 | TestUtils::runOnRenderThread([&](renderthread::RenderThread& rt) { |
| 112 | // Guaranteed to be scheduled first, allows destruction to start |
| 113 | rt.queue().postDelayed(0_ms, [&] { blockDestroyCallUntil.get_future().wait(); }); |
| 114 | // Guaranteed to be scheduled second, destroys the session |
| 115 | mWrapper->delayedDestroy(rt, 1_ms, mWrapper); |
| 116 | // This is guaranteed to be queued after the destroy, signals that destruction is done |
| 117 | rt.queue().postDelayed(1_ms, [&] { waitForDestroyFinished.set_value(1); }); |
| 118 | }); |
| 119 | } |
Matt Buckley | 0c66836 | 2023-09-07 05:52:07 +0000 | [diff] [blame] | 120 | }; |
| 121 | |
| 122 | std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding> |
| 123 | HintSessionWrapperTests::sMockBinding; |
| 124 | |
| 125 | void HintSessionWrapperTests::SetUp() { |
| 126 | // Pretend it's supported even if we're in an emulator |
| 127 | Properties::useHintManager = true; |
| 128 | sMockBinding = std::make_shared<NiceMock<MockHintSessionBinding>>(); |
| 129 | mWrapper = std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId); |
| 130 | mWrapper->mBinding = sMockBinding; |
| 131 | EXPECT_CALL(*sMockBinding, fakeGetManager).WillOnce(Return(managerPtr)); |
| 132 | ON_CALL(*sMockBinding, fakeCreateSession).WillByDefault(Return(sessionPtr)); |
| 133 | } |
| 134 | |
| 135 | void HintSessionWrapperTests::MockHintSessionBinding::init() { |
| 136 | sMockBinding->getManager = &stubGetManager; |
| 137 | if (sMockBinding->createSession == nullptr) { |
| 138 | sMockBinding->createSession = &stubCreateSession; |
| 139 | } |
| 140 | sMockBinding->closeSession = &stubCloseSession; |
| 141 | sMockBinding->updateTargetWorkDuration = &stubUpdateTargetWorkDuration; |
| 142 | sMockBinding->reportActualWorkDuration = &stubReportActualWorkDuration; |
| 143 | sMockBinding->sendHint = &stubSendHint; |
| 144 | } |
| 145 | |
| 146 | void HintSessionWrapperTests::TearDown() { |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 147 | // Ensure that anything running on RT is completely finished |
Matt Buckley | 0c66836 | 2023-09-07 05:52:07 +0000 | [diff] [blame] | 148 | mWrapper = nullptr; |
| 149 | sMockBinding = nullptr; |
| 150 | } |
| 151 | |
| 152 | TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) { |
| 153 | EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); |
| 154 | sMockBinding->createSession = stubSlowCreateSession; |
| 155 | mWrapper->init(); |
| 156 | mWrapper = nullptr; |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 157 | Mock::VerifyAndClearExpectations(sMockBinding.get()); |
Matt Buckley | 0c66836 | 2023-09-07 05:52:07 +0000 | [diff] [blame] | 158 | } |
| 159 | |
| 160 | TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) { |
| 161 | EXPECT_CALL(*sMockBinding, fakeCreateSession(managerPtr, _, Gt(1), _)).Times(1); |
| 162 | mWrapper->init(); |
| 163 | waitForWrapperReady(); |
| 164 | } |
| 165 | |
| 166 | TEST_F(HintSessionWrapperTests, loadUpHintsSendCorrectly) { |
| 167 | EXPECT_CALL(*sMockBinding, |
| 168 | fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP))) |
| 169 | .Times(1); |
| 170 | mWrapper->init(); |
| 171 | waitForWrapperReady(); |
| 172 | mWrapper->sendLoadIncreaseHint(); |
| 173 | } |
| 174 | |
| 175 | TEST_F(HintSessionWrapperTests, loadResetHintsSendCorrectly) { |
| 176 | EXPECT_CALL(*sMockBinding, |
| 177 | fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET))) |
| 178 | .Times(1); |
| 179 | mWrapper->init(); |
| 180 | waitForWrapperReady(); |
| 181 | mWrapper->sendLoadResetHint(); |
| 182 | } |
| 183 | |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 184 | TEST_F(HintSessionWrapperTests, delayedDeletionWorksCorrectlyAndOnlyClosesOnce) { |
| 185 | EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); |
| 186 | mWrapper->init(); |
| 187 | waitForWrapperReady(); |
| 188 | // Init a second time just to ensure the wrapper grabs the promise value |
| 189 | mWrapper->init(); |
| 190 | |
| 191 | EXPECT_EQ(mWrapper->alive(), true); |
| 192 | |
| 193 | // Schedule delayed destruction, allow it to run, and check when it's done |
| 194 | scheduleDelayedDestroyManaged(); |
| 195 | allowDelayedDestructionToStart(); |
| 196 | waitForDelayedDestructionToFinish(); |
| 197 | |
| 198 | // Ensure it closed within the timeframe of the test |
| 199 | Mock::VerifyAndClearExpectations(sMockBinding.get()); |
| 200 | EXPECT_EQ(mWrapper->alive(), false); |
| 201 | // If we then delete the wrapper, it shouldn't close the session again |
| 202 | EXPECT_CALL(*sMockBinding, fakeCloseSession(_)).Times(0); |
| 203 | mWrapper = nullptr; |
| 204 | } |
| 205 | |
| 206 | TEST_F(HintSessionWrapperTests, delayedDeletionResolvesBeforeAsyncCreationFinishes) { |
| 207 | // Here we test whether queueing delayedDestroy works while creation is still happening, if |
| 208 | // creation happens after |
| 209 | EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); |
| 210 | sMockBinding->createSession = &stubManagedCreateSession; |
| 211 | |
| 212 | // Start creating the session and destroying it at the same time |
| 213 | mWrapper->init(); |
| 214 | scheduleDelayedDestroyManaged(); |
| 215 | |
| 216 | // Allow destruction to happen first |
| 217 | allowDelayedDestructionToStart(); |
| 218 | |
| 219 | // Make sure destruction has had time to happen |
| 220 | std::this_thread::sleep_for(50ms); |
| 221 | |
| 222 | // Then, allow creation to finish after delayed destroy runs |
| 223 | allowCreationToFinish(); |
| 224 | |
| 225 | // Wait for destruction to finish |
| 226 | waitForDelayedDestructionToFinish(); |
| 227 | |
| 228 | // Ensure it closed within the timeframe of the test |
| 229 | Mock::VerifyAndClearExpectations(sMockBinding.get()); |
| 230 | EXPECT_EQ(mWrapper->alive(), false); |
| 231 | } |
| 232 | |
| 233 | TEST_F(HintSessionWrapperTests, delayedDeletionResolvesAfterAsyncCreationFinishes) { |
| 234 | // Here we test whether queueing delayedDestroy works while creation is still happening, if |
| 235 | // creation happens before |
| 236 | EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); |
| 237 | sMockBinding->createSession = &stubManagedCreateSession; |
| 238 | |
| 239 | // Start creating the session and destroying it at the same time |
| 240 | mWrapper->init(); |
| 241 | scheduleDelayedDestroyManaged(); |
| 242 | |
| 243 | // Allow creation to happen first |
| 244 | allowCreationToFinish(); |
| 245 | |
| 246 | // Make sure creation has had time to happen |
| 247 | waitForWrapperReady(); |
| 248 | |
| 249 | // Then allow destruction to happen after creation is done |
| 250 | allowDelayedDestructionToStart(); |
| 251 | |
| 252 | // Wait for it to finish |
| 253 | waitForDelayedDestructionToFinish(); |
| 254 | |
| 255 | // Ensure it closed within the timeframe of the test |
| 256 | Mock::VerifyAndClearExpectations(sMockBinding.get()); |
| 257 | EXPECT_EQ(mWrapper->alive(), false); |
| 258 | } |
| 259 | |
| 260 | TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) { |
| 261 | EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0); |
Matt Buckley | cef0bca | 2023-10-16 22:00:11 +0000 | [diff] [blame] | 262 | EXPECT_CALL(*sMockBinding, fakeReportActualWorkDuration(sessionPtr, 5_ms)).Times(1); |
| 263 | |
| 264 | mWrapper->init(); |
| 265 | waitForWrapperReady(); |
| 266 | // Init a second time just to grab the wrapper from the promise |
| 267 | mWrapper->init(); |
| 268 | EXPECT_EQ(mWrapper->alive(), true); |
| 269 | |
| 270 | // First schedule the deletion |
| 271 | scheduleDelayedDestroyManaged(); |
| 272 | |
| 273 | // Then, report an actual duration |
| 274 | mWrapper->reportActualWorkDuration(5_ms); |
| 275 | |
| 276 | // Then, run the delayed deletion after sending the update |
| 277 | allowDelayedDestructionToStart(); |
| 278 | waitForDelayedDestructionToFinish(); |
| 279 | |
| 280 | // Ensure it didn't close within the timeframe of the test |
| 281 | Mock::VerifyAndClearExpectations(sMockBinding.get()); |
| 282 | EXPECT_EQ(mWrapper->alive(), true); |
| 283 | } |
| 284 | |
| 285 | TEST_F(HintSessionWrapperTests, loadUpDoesNotResetDeletionTimer) { |
| 286 | EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 287 | EXPECT_CALL(*sMockBinding, |
| 288 | fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP))) |
| 289 | .Times(1); |
| 290 | |
| 291 | mWrapper->init(); |
| 292 | waitForWrapperReady(); |
| 293 | // Init a second time just to grab the wrapper from the promise |
| 294 | mWrapper->init(); |
| 295 | EXPECT_EQ(mWrapper->alive(), true); |
| 296 | |
| 297 | // First schedule the deletion |
| 298 | scheduleDelayedDestroyManaged(); |
| 299 | |
Matt Buckley | cef0bca | 2023-10-16 22:00:11 +0000 | [diff] [blame] | 300 | // Then, send a load_up hint |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 301 | mWrapper->sendLoadIncreaseHint(); |
| 302 | |
| 303 | // Then, run the delayed deletion after sending the update |
| 304 | allowDelayedDestructionToStart(); |
| 305 | waitForDelayedDestructionToFinish(); |
| 306 | |
Matt Buckley | cef0bca | 2023-10-16 22:00:11 +0000 | [diff] [blame] | 307 | // Ensure it closed within the timeframe of the test |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 308 | Mock::VerifyAndClearExpectations(sMockBinding.get()); |
Matt Buckley | cef0bca | 2023-10-16 22:00:11 +0000 | [diff] [blame] | 309 | EXPECT_EQ(mWrapper->alive(), false); |
| 310 | } |
| 311 | |
| 312 | TEST_F(HintSessionWrapperTests, manualSessionDestroyPlaysNiceWithDelayedDestruct) { |
| 313 | EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); |
| 314 | |
| 315 | mWrapper->init(); |
| 316 | waitForWrapperReady(); |
| 317 | // Init a second time just to grab the wrapper from the promise |
| 318 | mWrapper->init(); |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 319 | EXPECT_EQ(mWrapper->alive(), true); |
Matt Buckley | cef0bca | 2023-10-16 22:00:11 +0000 | [diff] [blame] | 320 | |
| 321 | // First schedule the deletion |
| 322 | scheduleDelayedDestroyManaged(); |
| 323 | |
| 324 | // Then, kill the session |
| 325 | mWrapper->destroy(); |
| 326 | |
| 327 | // Verify it died |
| 328 | Mock::VerifyAndClearExpectations(sMockBinding.get()); |
| 329 | EXPECT_EQ(mWrapper->alive(), false); |
| 330 | |
| 331 | EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0); |
| 332 | |
| 333 | // Then, run the delayed deletion after manually killing the session |
| 334 | allowDelayedDestructionToStart(); |
| 335 | waitForDelayedDestructionToFinish(); |
| 336 | |
| 337 | // Ensure it didn't close again and is still dead |
| 338 | Mock::VerifyAndClearExpectations(sMockBinding.get()); |
| 339 | EXPECT_EQ(mWrapper->alive(), false); |
Matt Buckley | 0daae6a | 2023-09-14 22:56:50 +0000 | [diff] [blame] | 340 | } |
| 341 | |
Matt Buckley | 0c66836 | 2023-09-07 05:52:07 +0000 | [diff] [blame] | 342 | } // namespace android::uirenderer::renderthread |