blob: a8db0f4aa4f07855a23b75dbf30c94ba56af6a6c [file] [log] [blame]
Matt Buckley0c668362023-09-07 05:52:07 +00001/*
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 Buckley0daae6a2023-09-14 22:56:50 +000026#include "tests/common/TestUtils.h"
Matt Buckley0c668362023-09-07 05:52:07 +000027
28using namespace testing;
29using namespace std::chrono_literals;
Matt Buckley0daae6a2023-09-14 22:56:50 +000030using namespace android::uirenderer::renderthread;
Matt Buckley0c668362023-09-07 05:52:07 +000031
32APerformanceHintManager* managerPtr = reinterpret_cast<APerformanceHintManager*>(123);
33APerformanceHintSession* sessionPtr = reinterpret_cast<APerformanceHintSession*>(456);
34int uiThreadId = 1;
35int renderThreadId = 2;
36
37namespace android::uirenderer::renderthread {
38
39class HintSessionWrapperTests : public testing::Test {
40public:
41 void SetUp() override;
42 void TearDown() override;
43
44protected:
45 std::shared_ptr<HintSessionWrapper> mWrapper;
46
Matt Buckley0daae6a2023-09-14 22:56:50 +000047 std::promise<int> blockDestroyCallUntil;
48 std::promise<int> waitForDestroyFinished;
49
Matt Buckley0c668362023-09-07 05:52:07 +000050 class MockHintSessionBinding : public HintSessionWrapper::HintSessionBinding {
51 public:
52 void init() override;
53
54 MOCK_METHOD(APerformanceHintManager*, fakeGetManager, ());
Matt Buckley27da1342024-04-05 23:10:48 +000055 MOCK_METHOD(APerformanceHintSession*, fakeCreateSessionInternal,
56 (APerformanceHintManager*, const int32_t*, size_t, int64_t, SessionTag));
Matt Buckley0c668362023-09-07 05:52:07 +000057 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));
Igor Kraskevich2cec1582024-03-13 11:23:40 +000061 MOCK_METHOD(int, fakeSetThreads, (APerformanceHintSession*, const std::vector<pid_t>&));
Matt Buckley0daae6a2023-09-14 22:56:50 +000062 // Needs to be on the binding so it can be accessed from static methods
63 std::promise<int> allowCreationToFinish;
Matt Buckley0c668362023-09-07 05:52:07 +000064 };
65
66 // Must be static so it can have function pointers we can point to with static methods
67 static std::shared_ptr<MockHintSessionBinding> sMockBinding;
68
Matt Buckley0daae6a2023-09-14 22:56:50 +000069 static void allowCreationToFinish() { sMockBinding->allowCreationToFinish.set_value(1); }
70 void allowDelayedDestructionToStart() { blockDestroyCallUntil.set_value(1); }
71 void waitForDelayedDestructionToFinish() { waitForDestroyFinished.get_future().wait(); }
72
Matt Buckley0c668362023-09-07 05:52:07 +000073 // Must be static so we can point to them as normal fn pointers with HintSessionBinding
74 static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); };
Matt Buckley27da1342024-04-05 23:10:48 +000075 static APerformanceHintSession* stubCreateSessionInternal(APerformanceHintManager* manager,
76 const int32_t* ids, size_t idsSize,
77 int64_t initialTarget,
78 SessionTag tag) {
79 return sMockBinding->fakeCreateSessionInternal(manager, ids, idsSize, initialTarget,
80 SessionTag::HWUI);
Matt Buckley0c668362023-09-07 05:52:07 +000081 }
Matt Buckley27da1342024-04-05 23:10:48 +000082 static APerformanceHintSession* stubManagedCreateSessionInternal(
83 APerformanceHintManager* manager, const int32_t* ids, size_t idsSize,
84 int64_t initialTarget, SessionTag tag) {
Matt Buckley0daae6a2023-09-14 22:56:50 +000085 sMockBinding->allowCreationToFinish.get_future().wait();
Matt Buckley27da1342024-04-05 23:10:48 +000086 return sMockBinding->fakeCreateSessionInternal(manager, ids, idsSize, initialTarget,
87 SessionTag::HWUI);
Matt Buckley0daae6a2023-09-14 22:56:50 +000088 }
Matt Buckley27da1342024-04-05 23:10:48 +000089 static APerformanceHintSession* stubSlowCreateSessionInternal(APerformanceHintManager* manager,
90 const int32_t* ids,
91 size_t idsSize,
92 int64_t initialTarget,
93 SessionTag tag) {
Matt Buckley0c668362023-09-07 05:52:07 +000094 std::this_thread::sleep_for(50ms);
Matt Buckley27da1342024-04-05 23:10:48 +000095 return sMockBinding->fakeCreateSessionInternal(manager, ids, idsSize, initialTarget,
96 SessionTag::HWUI);
Matt Buckley0c668362023-09-07 05:52:07 +000097 }
98 static void stubCloseSession(APerformanceHintSession* session) {
99 sMockBinding->fakeCloseSession(session);
100 };
101 static void stubUpdateTargetWorkDuration(APerformanceHintSession* session,
102 int64_t workDuration) {
103 sMockBinding->fakeUpdateTargetWorkDuration(session, workDuration);
104 }
105 static void stubReportActualWorkDuration(APerformanceHintSession* session,
106 int64_t workDuration) {
107 sMockBinding->fakeReportActualWorkDuration(session, workDuration);
108 }
109 static void stubSendHint(APerformanceHintSession* session, int32_t hintId) {
110 sMockBinding->fakeSendHint(session, hintId);
111 };
Igor Kraskevich2cec1582024-03-13 11:23:40 +0000112 static int stubSetThreads(APerformanceHintSession* session, const pid_t* ids, size_t size) {
113 std::vector<pid_t> tids(ids, ids + size);
114 return sMockBinding->fakeSetThreads(session, tids);
115 }
Matt Buckley0daae6a2023-09-14 22:56:50 +0000116 void waitForWrapperReady() {
117 if (mWrapper->mHintSessionFuture.has_value()) {
118 mWrapper->mHintSessionFuture->wait();
119 }
120 }
Igor Kraskevich2cec1582024-03-13 11:23:40 +0000121 void waitForSetThreadsReady() {
122 if (mWrapper->mSetThreadsFuture.has_value()) {
123 mWrapper->mSetThreadsFuture->wait();
124 }
125 }
Matt Buckley0daae6a2023-09-14 22:56:50 +0000126 void scheduleDelayedDestroyManaged() {
127 TestUtils::runOnRenderThread([&](renderthread::RenderThread& rt) {
128 // Guaranteed to be scheduled first, allows destruction to start
129 rt.queue().postDelayed(0_ms, [&] { blockDestroyCallUntil.get_future().wait(); });
130 // Guaranteed to be scheduled second, destroys the session
131 mWrapper->delayedDestroy(rt, 1_ms, mWrapper);
132 // This is guaranteed to be queued after the destroy, signals that destruction is done
133 rt.queue().postDelayed(1_ms, [&] { waitForDestroyFinished.set_value(1); });
134 });
135 }
Matt Buckley0c668362023-09-07 05:52:07 +0000136};
137
138std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding>
139 HintSessionWrapperTests::sMockBinding;
140
141void HintSessionWrapperTests::SetUp() {
142 // Pretend it's supported even if we're in an emulator
143 Properties::useHintManager = true;
144 sMockBinding = std::make_shared<NiceMock<MockHintSessionBinding>>();
145 mWrapper = std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId);
146 mWrapper->mBinding = sMockBinding;
147 EXPECT_CALL(*sMockBinding, fakeGetManager).WillOnce(Return(managerPtr));
Matt Buckley27da1342024-04-05 23:10:48 +0000148 ON_CALL(*sMockBinding, fakeCreateSessionInternal).WillByDefault(Return(sessionPtr));
Igor Kraskevich2cec1582024-03-13 11:23:40 +0000149 ON_CALL(*sMockBinding, fakeSetThreads).WillByDefault(Return(0));
Matt Buckley0c668362023-09-07 05:52:07 +0000150}
151
152void HintSessionWrapperTests::MockHintSessionBinding::init() {
153 sMockBinding->getManager = &stubGetManager;
Matt Buckley27da1342024-04-05 23:10:48 +0000154 if (sMockBinding->createSessionInternal == nullptr) {
155 sMockBinding->createSessionInternal = &stubCreateSessionInternal;
Matt Buckley0c668362023-09-07 05:52:07 +0000156 }
157 sMockBinding->closeSession = &stubCloseSession;
158 sMockBinding->updateTargetWorkDuration = &stubUpdateTargetWorkDuration;
159 sMockBinding->reportActualWorkDuration = &stubReportActualWorkDuration;
160 sMockBinding->sendHint = &stubSendHint;
Igor Kraskevich2cec1582024-03-13 11:23:40 +0000161 sMockBinding->setThreads = &stubSetThreads;
Matt Buckley0c668362023-09-07 05:52:07 +0000162}
163
164void HintSessionWrapperTests::TearDown() {
Matt Buckley0daae6a2023-09-14 22:56:50 +0000165 // Ensure that anything running on RT is completely finished
Matt Buckley0c668362023-09-07 05:52:07 +0000166 mWrapper = nullptr;
167 sMockBinding = nullptr;
168}
169
170TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) {
171 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
Matt Buckley27da1342024-04-05 23:10:48 +0000172 sMockBinding->createSessionInternal = stubSlowCreateSessionInternal;
Matt Buckley0c668362023-09-07 05:52:07 +0000173 mWrapper->init();
174 mWrapper = nullptr;
Matt Buckley0daae6a2023-09-14 22:56:50 +0000175 Mock::VerifyAndClearExpectations(sMockBinding.get());
Matt Buckley0c668362023-09-07 05:52:07 +0000176}
177
178TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) {
Matt Buckley27da1342024-04-05 23:10:48 +0000179 EXPECT_CALL(*sMockBinding, fakeCreateSessionInternal(managerPtr, _, Gt(1), _, _)).Times(1);
Matt Buckley0c668362023-09-07 05:52:07 +0000180 mWrapper->init();
181 waitForWrapperReady();
182}
183
184TEST_F(HintSessionWrapperTests, loadUpHintsSendCorrectly) {
185 EXPECT_CALL(*sMockBinding,
186 fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
187 .Times(1);
188 mWrapper->init();
189 waitForWrapperReady();
190 mWrapper->sendLoadIncreaseHint();
191}
192
193TEST_F(HintSessionWrapperTests, loadResetHintsSendCorrectly) {
194 EXPECT_CALL(*sMockBinding,
195 fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET)))
196 .Times(1);
197 mWrapper->init();
198 waitForWrapperReady();
199 mWrapper->sendLoadResetHint();
200}
201
Matt Buckley0daae6a2023-09-14 22:56:50 +0000202TEST_F(HintSessionWrapperTests, delayedDeletionWorksCorrectlyAndOnlyClosesOnce) {
203 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
204 mWrapper->init();
205 waitForWrapperReady();
206 // Init a second time just to ensure the wrapper grabs the promise value
207 mWrapper->init();
208
209 EXPECT_EQ(mWrapper->alive(), true);
210
211 // Schedule delayed destruction, allow it to run, and check when it's done
212 scheduleDelayedDestroyManaged();
213 allowDelayedDestructionToStart();
214 waitForDelayedDestructionToFinish();
215
216 // Ensure it closed within the timeframe of the test
217 Mock::VerifyAndClearExpectations(sMockBinding.get());
218 EXPECT_EQ(mWrapper->alive(), false);
219 // If we then delete the wrapper, it shouldn't close the session again
220 EXPECT_CALL(*sMockBinding, fakeCloseSession(_)).Times(0);
221 mWrapper = nullptr;
222}
223
224TEST_F(HintSessionWrapperTests, delayedDeletionResolvesBeforeAsyncCreationFinishes) {
225 // Here we test whether queueing delayedDestroy works while creation is still happening, if
226 // creation happens after
227 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
Matt Buckley27da1342024-04-05 23:10:48 +0000228 sMockBinding->createSessionInternal = &stubManagedCreateSessionInternal;
Matt Buckley0daae6a2023-09-14 22:56:50 +0000229
230 // Start creating the session and destroying it at the same time
231 mWrapper->init();
232 scheduleDelayedDestroyManaged();
233
234 // Allow destruction to happen first
235 allowDelayedDestructionToStart();
236
237 // Make sure destruction has had time to happen
238 std::this_thread::sleep_for(50ms);
239
240 // Then, allow creation to finish after delayed destroy runs
241 allowCreationToFinish();
242
243 // Wait for destruction to finish
244 waitForDelayedDestructionToFinish();
245
246 // Ensure it closed within the timeframe of the test
247 Mock::VerifyAndClearExpectations(sMockBinding.get());
248 EXPECT_EQ(mWrapper->alive(), false);
249}
250
251TEST_F(HintSessionWrapperTests, delayedDeletionResolvesAfterAsyncCreationFinishes) {
252 // Here we test whether queueing delayedDestroy works while creation is still happening, if
253 // creation happens before
254 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
Matt Buckley27da1342024-04-05 23:10:48 +0000255 sMockBinding->createSessionInternal = &stubManagedCreateSessionInternal;
Matt Buckley0daae6a2023-09-14 22:56:50 +0000256
257 // Start creating the session and destroying it at the same time
258 mWrapper->init();
259 scheduleDelayedDestroyManaged();
260
261 // Allow creation to happen first
262 allowCreationToFinish();
263
264 // Make sure creation has had time to happen
265 waitForWrapperReady();
266
267 // Then allow destruction to happen after creation is done
268 allowDelayedDestructionToStart();
269
270 // Wait for it to finish
271 waitForDelayedDestructionToFinish();
272
273 // Ensure it closed within the timeframe of the test
274 Mock::VerifyAndClearExpectations(sMockBinding.get());
275 EXPECT_EQ(mWrapper->alive(), false);
276}
277
278TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) {
279 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
Matt Buckleycef0bca2023-10-16 22:00:11 +0000280 EXPECT_CALL(*sMockBinding, fakeReportActualWorkDuration(sessionPtr, 5_ms)).Times(1);
281
282 mWrapper->init();
283 waitForWrapperReady();
284 // Init a second time just to grab the wrapper from the promise
285 mWrapper->init();
286 EXPECT_EQ(mWrapper->alive(), true);
287
288 // First schedule the deletion
289 scheduleDelayedDestroyManaged();
290
291 // Then, report an actual duration
292 mWrapper->reportActualWorkDuration(5_ms);
293
294 // Then, run the delayed deletion after sending the update
295 allowDelayedDestructionToStart();
296 waitForDelayedDestructionToFinish();
297
298 // Ensure it didn't close within the timeframe of the test
299 Mock::VerifyAndClearExpectations(sMockBinding.get());
300 EXPECT_EQ(mWrapper->alive(), true);
301}
302
303TEST_F(HintSessionWrapperTests, loadUpDoesNotResetDeletionTimer) {
304 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
Matt Buckley0daae6a2023-09-14 22:56:50 +0000305 EXPECT_CALL(*sMockBinding,
306 fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
307 .Times(1);
308
309 mWrapper->init();
310 waitForWrapperReady();
311 // Init a second time just to grab the wrapper from the promise
312 mWrapper->init();
313 EXPECT_EQ(mWrapper->alive(), true);
314
315 // First schedule the deletion
316 scheduleDelayedDestroyManaged();
317
Matt Buckleycef0bca2023-10-16 22:00:11 +0000318 // Then, send a load_up hint
Matt Buckley0daae6a2023-09-14 22:56:50 +0000319 mWrapper->sendLoadIncreaseHint();
320
321 // Then, run the delayed deletion after sending the update
322 allowDelayedDestructionToStart();
323 waitForDelayedDestructionToFinish();
324
Matt Buckleycef0bca2023-10-16 22:00:11 +0000325 // Ensure it closed within the timeframe of the test
Matt Buckley0daae6a2023-09-14 22:56:50 +0000326 Mock::VerifyAndClearExpectations(sMockBinding.get());
Matt Buckleycef0bca2023-10-16 22:00:11 +0000327 EXPECT_EQ(mWrapper->alive(), false);
328}
329
330TEST_F(HintSessionWrapperTests, manualSessionDestroyPlaysNiceWithDelayedDestruct) {
331 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
332
333 mWrapper->init();
334 waitForWrapperReady();
335 // Init a second time just to grab the wrapper from the promise
336 mWrapper->init();
Matt Buckley0daae6a2023-09-14 22:56:50 +0000337 EXPECT_EQ(mWrapper->alive(), true);
Matt Buckleycef0bca2023-10-16 22:00:11 +0000338
339 // First schedule the deletion
340 scheduleDelayedDestroyManaged();
341
342 // Then, kill the session
343 mWrapper->destroy();
344
345 // Verify it died
346 Mock::VerifyAndClearExpectations(sMockBinding.get());
347 EXPECT_EQ(mWrapper->alive(), false);
348
349 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
350
351 // Then, run the delayed deletion after manually killing the session
352 allowDelayedDestructionToStart();
353 waitForDelayedDestructionToFinish();
354
355 // Ensure it didn't close again and is still dead
356 Mock::VerifyAndClearExpectations(sMockBinding.get());
357 EXPECT_EQ(mWrapper->alive(), false);
Matt Buckley0daae6a2023-09-14 22:56:50 +0000358}
359
Igor Kraskevich2cec1582024-03-13 11:23:40 +0000360TEST_F(HintSessionWrapperTests, setThreadsUpdatesSessionThreads) {
Matt Buckley27da1342024-04-05 23:10:48 +0000361 EXPECT_CALL(*sMockBinding, fakeCreateSessionInternal(managerPtr, _, Gt(1), _, _)).Times(1);
Igor Kraskevich2cec1582024-03-13 11:23:40 +0000362 EXPECT_CALL(*sMockBinding, fakeSetThreads(sessionPtr, testing::IsSupersetOf({11, 22})))
363 .Times(1);
364 mWrapper->init();
365 waitForWrapperReady();
366
367 // This changes the overall set of threads in the session, so the session wrapper should call
368 // setThreads.
369 mWrapper->setActiveFunctorThreads({11, 22});
370 waitForSetThreadsReady();
371
372 // The set of threads doesn't change, so the session wrapper should not call setThreads this
373 // time. The order of the threads shouldn't matter.
374 mWrapper->setActiveFunctorThreads({22, 11});
375 waitForSetThreadsReady();
376}
377
378TEST_F(HintSessionWrapperTests, setThreadsDoesntCrashAfterDestroy) {
379 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
380
381 mWrapper->init();
382 waitForWrapperReady();
383 // Init a second time just to grab the wrapper from the promise
384 mWrapper->init();
385 EXPECT_EQ(mWrapper->alive(), true);
386
387 // Then, kill the session
388 mWrapper->destroy();
389
390 // Verify it died
391 Mock::VerifyAndClearExpectations(sMockBinding.get());
392 EXPECT_EQ(mWrapper->alive(), false);
393
394 // setActiveFunctorThreads shouldn't do anything, and shouldn't crash.
395 EXPECT_CALL(*sMockBinding, fakeSetThreads(_, _)).Times(0);
396 mWrapper->setActiveFunctorThreads({11, 22});
397 waitForSetThreadsReady();
398}
399
Matt Buckley0c668362023-09-07 05:52:07 +0000400} // namespace android::uirenderer::renderthread