blob: c16602c29e2aa75fe603d52aae154ccc12f83ca9 [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, ());
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));
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(); };
75 static APerformanceHintSession* stubCreateSession(APerformanceHintManager* manager,
76 const int32_t* ids, size_t idsSize,
77 int64_t initialTarget) {
78 return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
79 }
Matt Buckley0daae6a2023-09-14 22:56:50 +000080 static APerformanceHintSession* stubManagedCreateSession(APerformanceHintManager* manager,
81 const int32_t* ids, size_t idsSize,
82 int64_t initialTarget) {
83 sMockBinding->allowCreationToFinish.get_future().wait();
84 return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
85 }
Matt Buckley0c668362023-09-07 05:52:07 +000086 static APerformanceHintSession* stubSlowCreateSession(APerformanceHintManager* manager,
87 const int32_t* ids, size_t idsSize,
88 int64_t initialTarget) {
89 std::this_thread::sleep_for(50ms);
90 return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
91 }
92 static void stubCloseSession(APerformanceHintSession* session) {
93 sMockBinding->fakeCloseSession(session);
94 };
95 static void stubUpdateTargetWorkDuration(APerformanceHintSession* session,
96 int64_t workDuration) {
97 sMockBinding->fakeUpdateTargetWorkDuration(session, workDuration);
98 }
99 static void stubReportActualWorkDuration(APerformanceHintSession* session,
100 int64_t workDuration) {
101 sMockBinding->fakeReportActualWorkDuration(session, workDuration);
102 }
103 static void stubSendHint(APerformanceHintSession* session, int32_t hintId) {
104 sMockBinding->fakeSendHint(session, hintId);
105 };
Igor Kraskevich2cec1582024-03-13 11:23:40 +0000106 static int stubSetThreads(APerformanceHintSession* session, const pid_t* ids, size_t size) {
107 std::vector<pid_t> tids(ids, ids + size);
108 return sMockBinding->fakeSetThreads(session, tids);
109 }
Matt Buckley0daae6a2023-09-14 22:56:50 +0000110 void waitForWrapperReady() {
111 if (mWrapper->mHintSessionFuture.has_value()) {
112 mWrapper->mHintSessionFuture->wait();
113 }
114 }
Igor Kraskevich2cec1582024-03-13 11:23:40 +0000115 void waitForSetThreadsReady() {
116 if (mWrapper->mSetThreadsFuture.has_value()) {
117 mWrapper->mSetThreadsFuture->wait();
118 }
119 }
Matt Buckley0daae6a2023-09-14 22:56:50 +0000120 void scheduleDelayedDestroyManaged() {
121 TestUtils::runOnRenderThread([&](renderthread::RenderThread& rt) {
122 // Guaranteed to be scheduled first, allows destruction to start
123 rt.queue().postDelayed(0_ms, [&] { blockDestroyCallUntil.get_future().wait(); });
124 // Guaranteed to be scheduled second, destroys the session
125 mWrapper->delayedDestroy(rt, 1_ms, mWrapper);
126 // This is guaranteed to be queued after the destroy, signals that destruction is done
127 rt.queue().postDelayed(1_ms, [&] { waitForDestroyFinished.set_value(1); });
128 });
129 }
Matt Buckley0c668362023-09-07 05:52:07 +0000130};
131
132std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding>
133 HintSessionWrapperTests::sMockBinding;
134
135void HintSessionWrapperTests::SetUp() {
136 // Pretend it's supported even if we're in an emulator
137 Properties::useHintManager = true;
138 sMockBinding = std::make_shared<NiceMock<MockHintSessionBinding>>();
139 mWrapper = std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId);
140 mWrapper->mBinding = sMockBinding;
141 EXPECT_CALL(*sMockBinding, fakeGetManager).WillOnce(Return(managerPtr));
142 ON_CALL(*sMockBinding, fakeCreateSession).WillByDefault(Return(sessionPtr));
Igor Kraskevich2cec1582024-03-13 11:23:40 +0000143 ON_CALL(*sMockBinding, fakeSetThreads).WillByDefault(Return(0));
Matt Buckley0c668362023-09-07 05:52:07 +0000144}
145
146void HintSessionWrapperTests::MockHintSessionBinding::init() {
147 sMockBinding->getManager = &stubGetManager;
148 if (sMockBinding->createSession == nullptr) {
149 sMockBinding->createSession = &stubCreateSession;
150 }
151 sMockBinding->closeSession = &stubCloseSession;
152 sMockBinding->updateTargetWorkDuration = &stubUpdateTargetWorkDuration;
153 sMockBinding->reportActualWorkDuration = &stubReportActualWorkDuration;
154 sMockBinding->sendHint = &stubSendHint;
Igor Kraskevich2cec1582024-03-13 11:23:40 +0000155 sMockBinding->setThreads = &stubSetThreads;
Matt Buckley0c668362023-09-07 05:52:07 +0000156}
157
158void HintSessionWrapperTests::TearDown() {
Matt Buckley0daae6a2023-09-14 22:56:50 +0000159 // Ensure that anything running on RT is completely finished
Matt Buckley0c668362023-09-07 05:52:07 +0000160 mWrapper = nullptr;
161 sMockBinding = nullptr;
162}
163
164TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) {
165 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
166 sMockBinding->createSession = stubSlowCreateSession;
167 mWrapper->init();
168 mWrapper = nullptr;
Matt Buckley0daae6a2023-09-14 22:56:50 +0000169 Mock::VerifyAndClearExpectations(sMockBinding.get());
Matt Buckley0c668362023-09-07 05:52:07 +0000170}
171
172TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) {
173 EXPECT_CALL(*sMockBinding, fakeCreateSession(managerPtr, _, Gt(1), _)).Times(1);
174 mWrapper->init();
175 waitForWrapperReady();
176}
177
178TEST_F(HintSessionWrapperTests, loadUpHintsSendCorrectly) {
179 EXPECT_CALL(*sMockBinding,
180 fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
181 .Times(1);
182 mWrapper->init();
183 waitForWrapperReady();
184 mWrapper->sendLoadIncreaseHint();
185}
186
187TEST_F(HintSessionWrapperTests, loadResetHintsSendCorrectly) {
188 EXPECT_CALL(*sMockBinding,
189 fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET)))
190 .Times(1);
191 mWrapper->init();
192 waitForWrapperReady();
193 mWrapper->sendLoadResetHint();
194}
195
Matt Buckley0daae6a2023-09-14 22:56:50 +0000196TEST_F(HintSessionWrapperTests, delayedDeletionWorksCorrectlyAndOnlyClosesOnce) {
197 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
198 mWrapper->init();
199 waitForWrapperReady();
200 // Init a second time just to ensure the wrapper grabs the promise value
201 mWrapper->init();
202
203 EXPECT_EQ(mWrapper->alive(), true);
204
205 // Schedule delayed destruction, allow it to run, and check when it's done
206 scheduleDelayedDestroyManaged();
207 allowDelayedDestructionToStart();
208 waitForDelayedDestructionToFinish();
209
210 // Ensure it closed within the timeframe of the test
211 Mock::VerifyAndClearExpectations(sMockBinding.get());
212 EXPECT_EQ(mWrapper->alive(), false);
213 // If we then delete the wrapper, it shouldn't close the session again
214 EXPECT_CALL(*sMockBinding, fakeCloseSession(_)).Times(0);
215 mWrapper = nullptr;
216}
217
218TEST_F(HintSessionWrapperTests, delayedDeletionResolvesBeforeAsyncCreationFinishes) {
219 // Here we test whether queueing delayedDestroy works while creation is still happening, if
220 // creation happens after
221 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
222 sMockBinding->createSession = &stubManagedCreateSession;
223
224 // Start creating the session and destroying it at the same time
225 mWrapper->init();
226 scheduleDelayedDestroyManaged();
227
228 // Allow destruction to happen first
229 allowDelayedDestructionToStart();
230
231 // Make sure destruction has had time to happen
232 std::this_thread::sleep_for(50ms);
233
234 // Then, allow creation to finish after delayed destroy runs
235 allowCreationToFinish();
236
237 // Wait for destruction to finish
238 waitForDelayedDestructionToFinish();
239
240 // Ensure it closed within the timeframe of the test
241 Mock::VerifyAndClearExpectations(sMockBinding.get());
242 EXPECT_EQ(mWrapper->alive(), false);
243}
244
245TEST_F(HintSessionWrapperTests, delayedDeletionResolvesAfterAsyncCreationFinishes) {
246 // Here we test whether queueing delayedDestroy works while creation is still happening, if
247 // creation happens before
248 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
249 sMockBinding->createSession = &stubManagedCreateSession;
250
251 // Start creating the session and destroying it at the same time
252 mWrapper->init();
253 scheduleDelayedDestroyManaged();
254
255 // Allow creation to happen first
256 allowCreationToFinish();
257
258 // Make sure creation has had time to happen
259 waitForWrapperReady();
260
261 // Then allow destruction to happen after creation is done
262 allowDelayedDestructionToStart();
263
264 // Wait for it to finish
265 waitForDelayedDestructionToFinish();
266
267 // Ensure it closed within the timeframe of the test
268 Mock::VerifyAndClearExpectations(sMockBinding.get());
269 EXPECT_EQ(mWrapper->alive(), false);
270}
271
272TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) {
273 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
Matt Buckleycef0bca2023-10-16 22:00:11 +0000274 EXPECT_CALL(*sMockBinding, fakeReportActualWorkDuration(sessionPtr, 5_ms)).Times(1);
275
276 mWrapper->init();
277 waitForWrapperReady();
278 // Init a second time just to grab the wrapper from the promise
279 mWrapper->init();
280 EXPECT_EQ(mWrapper->alive(), true);
281
282 // First schedule the deletion
283 scheduleDelayedDestroyManaged();
284
285 // Then, report an actual duration
286 mWrapper->reportActualWorkDuration(5_ms);
287
288 // Then, run the delayed deletion after sending the update
289 allowDelayedDestructionToStart();
290 waitForDelayedDestructionToFinish();
291
292 // Ensure it didn't close within the timeframe of the test
293 Mock::VerifyAndClearExpectations(sMockBinding.get());
294 EXPECT_EQ(mWrapper->alive(), true);
295}
296
297TEST_F(HintSessionWrapperTests, loadUpDoesNotResetDeletionTimer) {
298 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
Matt Buckley0daae6a2023-09-14 22:56:50 +0000299 EXPECT_CALL(*sMockBinding,
300 fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
301 .Times(1);
302
303 mWrapper->init();
304 waitForWrapperReady();
305 // Init a second time just to grab the wrapper from the promise
306 mWrapper->init();
307 EXPECT_EQ(mWrapper->alive(), true);
308
309 // First schedule the deletion
310 scheduleDelayedDestroyManaged();
311
Matt Buckleycef0bca2023-10-16 22:00:11 +0000312 // Then, send a load_up hint
Matt Buckley0daae6a2023-09-14 22:56:50 +0000313 mWrapper->sendLoadIncreaseHint();
314
315 // Then, run the delayed deletion after sending the update
316 allowDelayedDestructionToStart();
317 waitForDelayedDestructionToFinish();
318
Matt Buckleycef0bca2023-10-16 22:00:11 +0000319 // Ensure it closed within the timeframe of the test
Matt Buckley0daae6a2023-09-14 22:56:50 +0000320 Mock::VerifyAndClearExpectations(sMockBinding.get());
Matt Buckleycef0bca2023-10-16 22:00:11 +0000321 EXPECT_EQ(mWrapper->alive(), false);
322}
323
324TEST_F(HintSessionWrapperTests, manualSessionDestroyPlaysNiceWithDelayedDestruct) {
325 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
326
327 mWrapper->init();
328 waitForWrapperReady();
329 // Init a second time just to grab the wrapper from the promise
330 mWrapper->init();
Matt Buckley0daae6a2023-09-14 22:56:50 +0000331 EXPECT_EQ(mWrapper->alive(), true);
Matt Buckleycef0bca2023-10-16 22:00:11 +0000332
333 // First schedule the deletion
334 scheduleDelayedDestroyManaged();
335
336 // Then, kill the session
337 mWrapper->destroy();
338
339 // Verify it died
340 Mock::VerifyAndClearExpectations(sMockBinding.get());
341 EXPECT_EQ(mWrapper->alive(), false);
342
343 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
344
345 // Then, run the delayed deletion after manually killing the session
346 allowDelayedDestructionToStart();
347 waitForDelayedDestructionToFinish();
348
349 // Ensure it didn't close again and is still dead
350 Mock::VerifyAndClearExpectations(sMockBinding.get());
351 EXPECT_EQ(mWrapper->alive(), false);
Matt Buckley0daae6a2023-09-14 22:56:50 +0000352}
353
Igor Kraskevich2cec1582024-03-13 11:23:40 +0000354TEST_F(HintSessionWrapperTests, setThreadsUpdatesSessionThreads) {
355 EXPECT_CALL(*sMockBinding, fakeCreateSession(managerPtr, _, Gt(1), _)).Times(1);
356 EXPECT_CALL(*sMockBinding, fakeSetThreads(sessionPtr, testing::IsSupersetOf({11, 22})))
357 .Times(1);
358 mWrapper->init();
359 waitForWrapperReady();
360
361 // This changes the overall set of threads in the session, so the session wrapper should call
362 // setThreads.
363 mWrapper->setActiveFunctorThreads({11, 22});
364 waitForSetThreadsReady();
365
366 // The set of threads doesn't change, so the session wrapper should not call setThreads this
367 // time. The order of the threads shouldn't matter.
368 mWrapper->setActiveFunctorThreads({22, 11});
369 waitForSetThreadsReady();
370}
371
372TEST_F(HintSessionWrapperTests, setThreadsDoesntCrashAfterDestroy) {
373 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
374
375 mWrapper->init();
376 waitForWrapperReady();
377 // Init a second time just to grab the wrapper from the promise
378 mWrapper->init();
379 EXPECT_EQ(mWrapper->alive(), true);
380
381 // Then, kill the session
382 mWrapper->destroy();
383
384 // Verify it died
385 Mock::VerifyAndClearExpectations(sMockBinding.get());
386 EXPECT_EQ(mWrapper->alive(), false);
387
388 // setActiveFunctorThreads shouldn't do anything, and shouldn't crash.
389 EXPECT_CALL(*sMockBinding, fakeSetThreads(_, _)).Times(0);
390 mWrapper->setActiveFunctorThreads({11, 22});
391 waitForSetThreadsReady();
392}
393
Matt Buckley0c668362023-09-07 05:52:07 +0000394} // namespace android::uirenderer::renderthread