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