1 /*
2  * Copyright 2019 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 #undef LOG_TAG
18 #define LOG_TAG "LibSurfaceFlingerUnittests"
19 #define LOG_NDEBUG 0
20 
21 #include <inttypes.h>
22 
23 #include <gmock/gmock.h>
24 #include <gtest/gtest.h>
25 
26 #include <log/log.h>
27 
28 #include "AsyncCallRecorder.h"
29 #include "Scheduler/DispSyncSource.h"
30 #include "Scheduler/VSyncDispatch.h"
31 
32 namespace android {
33 namespace {
34 
35 using namespace std::chrono_literals;
36 using namespace testing;
37 
38 class MockVSyncDispatch : public scheduler::VSyncDispatch {
39 public:
40     MOCK_METHOD2(registerCallback,
41                  CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
42     MOCK_METHOD1(unregisterCallback, void(CallbackToken));
43     MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
44     MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
45     MOCK_CONST_METHOD1(dump, void(std::string&));
46 
MockVSyncDispatch()47     MockVSyncDispatch() {
48         ON_CALL(*this, registerCallback)
49                 .WillByDefault(
50                         [this](std::function<void(nsecs_t, nsecs_t, nsecs_t)> const& callback,
51                                std::string) {
52                             CallbackToken token(mNextToken);
53                             mNextToken++;
54 
55                             mCallbacks.emplace(token, CallbackData(callback));
56                             ALOGD("registerCallback: %zu", token.value());
57                             return token;
58                         });
59 
60         ON_CALL(*this, unregisterCallback).WillByDefault([this](CallbackToken token) {
61             ALOGD("unregisterCallback: %zu", token.value());
62             mCallbacks.erase(token);
63         });
64 
65         ON_CALL(*this, schedule).WillByDefault([this](CallbackToken token, ScheduleTiming timing) {
66             ALOGD("schedule: %zu", token.value());
67             if (mCallbacks.count(token) == 0) {
68                 ALOGD("schedule: callback %zu not registered", token.value());
69                 return scheduler::ScheduleResult{};
70             }
71 
72             auto& callback = mCallbacks.at(token);
73             callback.scheduled = true;
74             callback.vsyncTime = timing.earliestVsync;
75             callback.targetWakeupTime =
76                     timing.earliestVsync - timing.workDuration - timing.readyDuration;
77             ALOGD("schedule: callback %zu scheduled", token.value());
78             return scheduler::ScheduleResult{callback.targetWakeupTime};
79         });
80 
81         ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
82             ALOGD("cancel: %zu", token.value());
83             if (mCallbacks.count(token) == 0) {
84                 ALOGD("cancel: callback %zu is not registered", token.value());
85                 return scheduler::CancelResult::Error;
86             }
87 
88             auto& callback = mCallbacks.at(token);
89             callback.scheduled = false;
90             ALOGD("cancel: callback %zu cancelled", token.value());
91             return scheduler::CancelResult::Cancelled;
92         });
93     }
94 
triggerCallbacks()95     void triggerCallbacks() {
96         ALOGD("triggerCallbacks");
97         for (auto& [token, callback] : mCallbacks) {
98             if (callback.scheduled) {
99                 ALOGD("triggerCallbacks: callback %zu", token.value());
100                 callback.scheduled = false;
101                 callback.func(callback.vsyncTime, callback.targetWakeupTime, callback.readyTime);
102             } else {
103                 ALOGD("triggerCallbacks: callback %zu is not scheduled", token.value());
104             }
105         }
106     }
107 
108 private:
109     struct CallbackData {
CallbackDataandroid::__anon5a1696560110::MockVSyncDispatch::CallbackData110         explicit CallbackData(std::function<void(nsecs_t, nsecs_t, nsecs_t)> func)
111               : func(std::move(func)) {}
112 
113         std::function<void(nsecs_t, nsecs_t, nsecs_t)> func;
114         bool scheduled = false;
115         nsecs_t vsyncTime = 0;
116         nsecs_t targetWakeupTime = 0;
117         nsecs_t readyTime = 0;
118     };
119 
120     std::unordered_map<CallbackToken, CallbackData> mCallbacks;
121     size_t mNextToken;
122 };
123 
124 class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback {
125 protected:
126     DispSyncSourceTest();
127     ~DispSyncSourceTest() override;
128 
129     void createDispSync();
130     void createDispSyncSource();
131 
132     void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
133                       nsecs_t deadlineTimestamp) override;
134 
135     std::unique_ptr<MockVSyncDispatch> mVSyncDispatch;
136     std::unique_ptr<scheduler::DispSyncSource> mDispSyncSource;
137 
138     AsyncCallRecorder<void (*)(nsecs_t, nsecs_t, nsecs_t)> mVSyncEventCallRecorder;
139 
140     static constexpr std::chrono::nanoseconds mWorkDuration = 20ms;
141     static constexpr std::chrono::nanoseconds mReadyDuration = 10ms;
142     static constexpr int mIterations = 100;
143     const scheduler::VSyncDispatch::CallbackToken mFakeToken{2398};
144     const std::string mName = "DispSyncSourceTest";
145 };
146 
DispSyncSourceTest()147 DispSyncSourceTest::DispSyncSourceTest() {
148     const ::testing::TestInfo* const test_info =
149             ::testing::UnitTest::GetInstance()->current_test_info();
150     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
151 }
152 
~DispSyncSourceTest()153 DispSyncSourceTest::~DispSyncSourceTest() {
154     const ::testing::TestInfo* const test_info =
155             ::testing::UnitTest::GetInstance()->current_test_info();
156     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
157 }
158 
onVSyncEvent(nsecs_t when,nsecs_t expectedVSyncTimestamp,nsecs_t deadlineTimestamp)159 void DispSyncSourceTest::onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
160                                       nsecs_t deadlineTimestamp) {
161     ALOGD("onVSyncEvent: %" PRId64, when);
162 
163     mVSyncEventCallRecorder.recordCall(when, expectedVSyncTimestamp, deadlineTimestamp);
164 }
165 
createDispSync()166 void DispSyncSourceTest::createDispSync() {
167     mVSyncDispatch = std::make_unique<MockVSyncDispatch>();
168 }
169 
createDispSyncSource()170 void DispSyncSourceTest::createDispSyncSource() {
171     mDispSyncSource =
172             std::make_unique<scheduler::DispSyncSource>(*mVSyncDispatch, mWorkDuration,
173                                                         mReadyDuration, true, mName.c_str());
174     mDispSyncSource->setCallback(this);
175 }
176 
177 /* ------------------------------------------------------------------------
178  * Test cases
179  */
180 
TEST_F(DispSyncSourceTest,createDispSync)181 TEST_F(DispSyncSourceTest, createDispSync) {
182     createDispSync();
183     EXPECT_TRUE(mVSyncDispatch);
184 }
185 
TEST_F(DispSyncSourceTest,createDispSyncSource)186 TEST_F(DispSyncSourceTest, createDispSyncSource) {
187     createDispSync();
188 
189     InSequence seq;
190     EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).WillOnce(Return(mFakeToken));
191     EXPECT_CALL(*mVSyncDispatch, cancel(mFakeToken))
192             .WillOnce(Return(scheduler::CancelResult::Cancelled));
193     EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mFakeToken)).WillOnce(Return());
194     createDispSyncSource();
195 
196     EXPECT_TRUE(mDispSyncSource);
197 }
198 
TEST_F(DispSyncSourceTest,noCallbackAfterInit)199 TEST_F(DispSyncSourceTest, noCallbackAfterInit) {
200     createDispSync();
201 
202     InSequence seq;
203     EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
204     EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
205     EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
206     createDispSyncSource();
207 
208     EXPECT_TRUE(mDispSyncSource);
209 
210     // DispSyncSource starts with Vsync disabled
211     mVSyncDispatch->triggerCallbacks();
212     EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value());
213 }
214 
TEST_F(DispSyncSourceTest,waitForCallbacks)215 TEST_F(DispSyncSourceTest, waitForCallbacks) {
216     createDispSync();
217 
218     InSequence seq;
219     EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
220     EXPECT_CALL(*mVSyncDispatch,
221                 schedule(_, Truly([&](auto timings) {
222                              return timings.workDuration == mWorkDuration.count() &&
223                                      timings.readyDuration == mReadyDuration.count();
224                          })))
225             .Times(mIterations + 1);
226     EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
227     EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
228     createDispSyncSource();
229 
230     EXPECT_TRUE(mDispSyncSource);
231 
232     mDispSyncSource->setVSyncEnabled(true);
233     for (int i = 0; i < mIterations; i++) {
234         mVSyncDispatch->triggerCallbacks();
235         const auto callbackData = mVSyncEventCallRecorder.waitForCall();
236         ASSERT_TRUE(callbackData.has_value());
237         const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
238         EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count());
239     }
240 }
241 
TEST_F(DispSyncSourceTest,waitForCallbacksWithDurationChange)242 TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) {
243     createDispSync();
244 
245     InSequence seq;
246     EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
247     EXPECT_CALL(*mVSyncDispatch,
248                 schedule(_, Truly([&](auto timings) {
249                              return timings.workDuration == mWorkDuration.count() &&
250                                      timings.readyDuration == mReadyDuration.count();
251                          })))
252             .Times(1);
253 
254     createDispSyncSource();
255 
256     EXPECT_TRUE(mDispSyncSource);
257 
258     mDispSyncSource->setVSyncEnabled(true);
259     EXPECT_CALL(*mVSyncDispatch,
260                 schedule(_, Truly([&](auto timings) {
261                              return timings.workDuration == mWorkDuration.count() &&
262                                      timings.readyDuration == mReadyDuration.count();
263                          })))
264             .Times(mIterations);
265     for (int i = 0; i < mIterations; i++) {
266         mVSyncDispatch->triggerCallbacks();
267         const auto callbackData = mVSyncEventCallRecorder.waitForCall();
268         ASSERT_TRUE(callbackData.has_value());
269         const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
270         EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count());
271     }
272 
273     const auto newDuration = mWorkDuration / 2;
274     EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
275                                               return timings.workDuration == newDuration.count() &&
276                                                       timings.readyDuration == 0;
277                                           })))
278             .Times(1);
279     mDispSyncSource->setDuration(newDuration, 0ns);
280 
281     EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
282                                               return timings.workDuration == newDuration.count() &&
283                                                       timings.readyDuration == 0;
284                                           })))
285             .Times(mIterations);
286     for (int i = 0; i < mIterations; i++) {
287         mVSyncDispatch->triggerCallbacks();
288         const auto callbackData = mVSyncEventCallRecorder.waitForCall();
289         ASSERT_TRUE(callbackData.has_value());
290         const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
291         EXPECT_EQ(when, expectedVSyncTimestamp - newDuration.count());
292     }
293 
294     EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
295     EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
296 }
297 
298 } // namespace
299 } // namespace android
300