blob: 10a740a1f803916628ed30aefe9e07c1e29ce6bb [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));
Matt Buckley0daae6a2023-09-14 22:56:50 +000061 // Needs to be on the binding so it can be accessed from static methods
62 std::promise<int> allowCreationToFinish;
Matt Buckley0c668362023-09-07 05:52:07 +000063 };
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 Buckley0daae6a2023-09-14 22:56:50 +000068 static void allowCreationToFinish() { sMockBinding->allowCreationToFinish.set_value(1); }
69 void allowDelayedDestructionToStart() { blockDestroyCallUntil.set_value(1); }
70 void waitForDelayedDestructionToFinish() { waitForDestroyFinished.get_future().wait(); }
71
Matt Buckley0c668362023-09-07 05:52:07 +000072 // 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 Buckley0daae6a2023-09-14 22:56:50 +000079 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 Buckley0c668362023-09-07 05:52:07 +000085 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 Buckley0daae6a2023-09-14 22:56:50 +0000105 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 Buckley0c668362023-09-07 05:52:07 +0000120};
121
122std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding>
123 HintSessionWrapperTests::sMockBinding;
124
125void 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
135void 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
146void HintSessionWrapperTests::TearDown() {
Matt Buckley0daae6a2023-09-14 22:56:50 +0000147 // Ensure that anything running on RT is completely finished
Matt Buckley0c668362023-09-07 05:52:07 +0000148 mWrapper = nullptr;
149 sMockBinding = nullptr;
150}
151
152TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) {
153 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
154 sMockBinding->createSession = stubSlowCreateSession;
155 mWrapper->init();
156 mWrapper = nullptr;
Matt Buckley0daae6a2023-09-14 22:56:50 +0000157 Mock::VerifyAndClearExpectations(sMockBinding.get());
Matt Buckley0c668362023-09-07 05:52:07 +0000158}
159
160TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) {
161 EXPECT_CALL(*sMockBinding, fakeCreateSession(managerPtr, _, Gt(1), _)).Times(1);
162 mWrapper->init();
163 waitForWrapperReady();
164}
165
166TEST_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
175TEST_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 Buckley0daae6a2023-09-14 22:56:50 +0000184TEST_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
206TEST_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
233TEST_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
260TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) {
261 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
Matt Buckleycef0bca2023-10-16 22:00:11 +0000262 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
285TEST_F(HintSessionWrapperTests, loadUpDoesNotResetDeletionTimer) {
286 EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
Matt Buckley0daae6a2023-09-14 22:56:50 +0000287 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 Buckleycef0bca2023-10-16 22:00:11 +0000300 // Then, send a load_up hint
Matt Buckley0daae6a2023-09-14 22:56:50 +0000301 mWrapper->sendLoadIncreaseHint();
302
303 // Then, run the delayed deletion after sending the update
304 allowDelayedDestructionToStart();
305 waitForDelayedDestructionToFinish();
306
Matt Buckleycef0bca2023-10-16 22:00:11 +0000307 // Ensure it closed within the timeframe of the test
Matt Buckley0daae6a2023-09-14 22:56:50 +0000308 Mock::VerifyAndClearExpectations(sMockBinding.get());
Matt Buckleycef0bca2023-10-16 22:00:11 +0000309 EXPECT_EQ(mWrapper->alive(), false);
310}
311
312TEST_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 Buckley0daae6a2023-09-14 22:56:50 +0000319 EXPECT_EQ(mWrapper->alive(), true);
Matt Buckleycef0bca2023-10-16 22:00:11 +0000320
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 Buckley0daae6a2023-09-14 22:56:50 +0000340}
341
Matt Buckley0c668362023-09-07 05:52:07 +0000342} // namespace android::uirenderer::renderthread