• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"
26 #include "tests/common/TestUtils.h"
27 
28 using namespace testing;
29 using namespace std::chrono_literals;
30 using namespace android::uirenderer::renderthread;
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 
47     std::promise<int> blockDestroyCallUntil;
48     std::promise<int> waitForDestroyFinished;
49 
50     class MockHintSessionBinding : public HintSessionWrapper::HintSessionBinding {
51     public:
52         void init() override;
53 
54         MOCK_METHOD(APerformanceHintManager*, fakeGetManager, ());
55         MOCK_METHOD(APerformanceHintSession*, fakeCreateSessionInternal,
56                     (APerformanceHintManager*, const int32_t*, size_t, int64_t, SessionTag));
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));
61         MOCK_METHOD(int, fakeSetThreads, (APerformanceHintSession*, const std::vector<pid_t>&));
62         // Needs to be on the binding so it can be accessed from static methods
63         std::promise<int> allowCreationToFinish;
64     };
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 
allowCreationToFinish()69     static void allowCreationToFinish() { sMockBinding->allowCreationToFinish.set_value(1); }
allowDelayedDestructionToStart()70     void allowDelayedDestructionToStart() { blockDestroyCallUntil.set_value(1); }
waitForDelayedDestructionToFinish()71     void waitForDelayedDestructionToFinish() { waitForDestroyFinished.get_future().wait(); }
72 
73     // Must be static so we can point to them as normal fn pointers with HintSessionBinding
stubGetManager()74     static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); };
stubCreateSessionInternal(APerformanceHintManager * manager,const int32_t * ids,size_t idsSize,int64_t initialTarget,SessionTag tag)75     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);
81     }
stubManagedCreateSessionInternal(APerformanceHintManager * manager,const int32_t * ids,size_t idsSize,int64_t initialTarget,SessionTag tag)82     static APerformanceHintSession* stubManagedCreateSessionInternal(
83             APerformanceHintManager* manager, const int32_t* ids, size_t idsSize,
84             int64_t initialTarget, SessionTag tag) {
85         sMockBinding->allowCreationToFinish.get_future().wait();
86         return sMockBinding->fakeCreateSessionInternal(manager, ids, idsSize, initialTarget,
87                                                        SessionTag::HWUI);
88     }
stubSlowCreateSessionInternal(APerformanceHintManager * manager,const int32_t * ids,size_t idsSize,int64_t initialTarget,SessionTag tag)89     static APerformanceHintSession* stubSlowCreateSessionInternal(APerformanceHintManager* manager,
90                                                                   const int32_t* ids,
91                                                                   size_t idsSize,
92                                                                   int64_t initialTarget,
93                                                                   SessionTag tag) {
94         std::this_thread::sleep_for(50ms);
95         return sMockBinding->fakeCreateSessionInternal(manager, ids, idsSize, initialTarget,
96                                                        SessionTag::HWUI);
97     }
stubCloseSession(APerformanceHintSession * session)98     static void stubCloseSession(APerformanceHintSession* session) {
99         sMockBinding->fakeCloseSession(session);
100     };
stubUpdateTargetWorkDuration(APerformanceHintSession * session,int64_t workDuration)101     static void stubUpdateTargetWorkDuration(APerformanceHintSession* session,
102                                              int64_t workDuration) {
103         sMockBinding->fakeUpdateTargetWorkDuration(session, workDuration);
104     }
stubReportActualWorkDuration(APerformanceHintSession * session,int64_t workDuration)105     static void stubReportActualWorkDuration(APerformanceHintSession* session,
106                                              int64_t workDuration) {
107         sMockBinding->fakeReportActualWorkDuration(session, workDuration);
108     }
stubSendHint(APerformanceHintSession * session,int32_t hintId)109     static void stubSendHint(APerformanceHintSession* session, int32_t hintId) {
110         sMockBinding->fakeSendHint(session, hintId);
111     };
stubSetThreads(APerformanceHintSession * session,const pid_t * ids,size_t size)112     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     }
waitForWrapperReady()116     void waitForWrapperReady() {
117         if (mWrapper->mHintSessionFuture.has_value()) {
118             mWrapper->mHintSessionFuture->wait();
119         }
120     }
waitForSetThreadsReady()121     void waitForSetThreadsReady() {
122         if (mWrapper->mSetThreadsFuture.has_value()) {
123             mWrapper->mSetThreadsFuture->wait();
124         }
125     }
scheduleDelayedDestroyManaged()126     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     }
136 };
137 
138 std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding>
139         HintSessionWrapperTests::sMockBinding;
140 
SetUp()141 void 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));
148     ON_CALL(*sMockBinding, fakeCreateSessionInternal).WillByDefault(Return(sessionPtr));
149     ON_CALL(*sMockBinding, fakeSetThreads).WillByDefault(Return(0));
150 }
151 
init()152 void HintSessionWrapperTests::MockHintSessionBinding::init() {
153     sMockBinding->getManager = &stubGetManager;
154     if (sMockBinding->createSessionInternal == nullptr) {
155         sMockBinding->createSessionInternal = &stubCreateSessionInternal;
156     }
157     sMockBinding->closeSession = &stubCloseSession;
158     sMockBinding->updateTargetWorkDuration = &stubUpdateTargetWorkDuration;
159     sMockBinding->reportActualWorkDuration = &stubReportActualWorkDuration;
160     sMockBinding->sendHint = &stubSendHint;
161     sMockBinding->setThreads = &stubSetThreads;
162 }
163 
TearDown()164 void HintSessionWrapperTests::TearDown() {
165     // Ensure that anything running on RT is completely finished
166     mWrapper = nullptr;
167     sMockBinding = nullptr;
168 }
169 
TEST_F(HintSessionWrapperTests,destructorClosesBackgroundSession)170 TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) {
171     EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
172     sMockBinding->createSessionInternal = stubSlowCreateSessionInternal;
173     mWrapper->init();
174     mWrapper = nullptr;
175     Mock::VerifyAndClearExpectations(sMockBinding.get());
176 }
177 
TEST_F(HintSessionWrapperTests,sessionInitializesCorrectly)178 TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) {
179     EXPECT_CALL(*sMockBinding, fakeCreateSessionInternal(managerPtr, _, Gt(1), _, _)).Times(1);
180     mWrapper->init();
181     waitForWrapperReady();
182 }
183 
TEST_F(HintSessionWrapperTests,loadUpHintsSendCorrectly)184 TEST_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 
TEST_F(HintSessionWrapperTests,loadResetHintsSendCorrectly)193 TEST_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 
TEST_F(HintSessionWrapperTests,delayedDeletionWorksCorrectlyAndOnlyClosesOnce)202 TEST_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 
TEST_F(HintSessionWrapperTests,delayedDeletionResolvesBeforeAsyncCreationFinishes)224 TEST_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);
228     sMockBinding->createSessionInternal = &stubManagedCreateSessionInternal;
229 
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 
TEST_F(HintSessionWrapperTests,delayedDeletionResolvesAfterAsyncCreationFinishes)251 TEST_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);
255     sMockBinding->createSessionInternal = &stubManagedCreateSessionInternal;
256 
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 
TEST_F(HintSessionWrapperTests,delayedDeletionDoesNotKillReusedSession)278 TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) {
279     EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
280     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 
TEST_F(HintSessionWrapperTests,loadUpDoesNotResetDeletionTimer)303 TEST_F(HintSessionWrapperTests, loadUpDoesNotResetDeletionTimer) {
304     EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
305     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 
318     // Then, send a load_up hint
319     mWrapper->sendLoadIncreaseHint();
320 
321     // Then, run the delayed deletion after sending the update
322     allowDelayedDestructionToStart();
323     waitForDelayedDestructionToFinish();
324 
325     // Ensure it closed within the timeframe of the test
326     Mock::VerifyAndClearExpectations(sMockBinding.get());
327     EXPECT_EQ(mWrapper->alive(), false);
328 }
329 
TEST_F(HintSessionWrapperTests,manualSessionDestroyPlaysNiceWithDelayedDestruct)330 TEST_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();
337     EXPECT_EQ(mWrapper->alive(), true);
338 
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);
358 }
359 
TEST_F(HintSessionWrapperTests,setThreadsUpdatesSessionThreads)360 TEST_F(HintSessionWrapperTests, setThreadsUpdatesSessionThreads) {
361     EXPECT_CALL(*sMockBinding, fakeCreateSessionInternal(managerPtr, _, Gt(1), _, _)).Times(1);
362     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 
TEST_F(HintSessionWrapperTests,setThreadsDoesntCrashAfterDestroy)378 TEST_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 
400 }  // namespace android::uirenderer::renderthread