blob: 5a0ea352d5da9602dbe9d61fb894e0fdf4d63bf1 [file] [log] [blame]
Ady Abraham50c202a2019-03-14 11:44:38 -07001/*
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"
Ady Abraham9c53ee72020-07-22 21:16:18 -070030#include "Scheduler/VSyncDispatch.h"
Ady Abraham50c202a2019-03-14 11:44:38 -070031
32namespace android {
33namespace {
34
35using namespace std::chrono_literals;
Ady Abraham9c53ee72020-07-22 21:16:18 -070036using namespace testing;
37
38class MockVSyncDispatch : public scheduler::VSyncDispatch {
39public:
Dominik Laskowski4e0d20d2021-12-06 11:31:02 -080040 MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
41 MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
42 MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
43 MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken), (override));
44 MOCK_METHOD(void, dump, (std::string&), (const, override));
Ady Abraham9c53ee72020-07-22 21:16:18 -070045
46 MockVSyncDispatch() {
47 ON_CALL(*this, registerCallback)
48 .WillByDefault(
49 [this](std::function<void(nsecs_t, nsecs_t, nsecs_t)> const& callback,
50 std::string) {
51 CallbackToken token(mNextToken);
52 mNextToken++;
53
54 mCallbacks.emplace(token, CallbackData(callback));
55 ALOGD("registerCallback: %zu", token.value());
56 return token;
57 });
58
59 ON_CALL(*this, unregisterCallback).WillByDefault([this](CallbackToken token) {
60 ALOGD("unregisterCallback: %zu", token.value());
61 mCallbacks.erase(token);
62 });
63
64 ON_CALL(*this, schedule).WillByDefault([this](CallbackToken token, ScheduleTiming timing) {
65 ALOGD("schedule: %zu", token.value());
66 if (mCallbacks.count(token) == 0) {
67 ALOGD("schedule: callback %zu not registered", token.value());
Ady Abrahamb5d3afa2021-05-07 11:22:23 -070068 return scheduler::ScheduleResult{};
Ady Abraham9c53ee72020-07-22 21:16:18 -070069 }
70
71 auto& callback = mCallbacks.at(token);
72 callback.scheduled = true;
73 callback.vsyncTime = timing.earliestVsync;
74 callback.targetWakeupTime =
75 timing.earliestVsync - timing.workDuration - timing.readyDuration;
76 ALOGD("schedule: callback %zu scheduled", token.value());
Ady Abrahamb5d3afa2021-05-07 11:22:23 -070077 return scheduler::ScheduleResult{callback.targetWakeupTime};
Ady Abraham9c53ee72020-07-22 21:16:18 -070078 });
79
80 ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
81 ALOGD("cancel: %zu", token.value());
82 if (mCallbacks.count(token) == 0) {
83 ALOGD("cancel: callback %zu is not registered", token.value());
84 return scheduler::CancelResult::Error;
85 }
86
87 auto& callback = mCallbacks.at(token);
88 callback.scheduled = false;
89 ALOGD("cancel: callback %zu cancelled", token.value());
90 return scheduler::CancelResult::Cancelled;
91 });
92 }
93
94 void triggerCallbacks() {
95 ALOGD("triggerCallbacks");
96 for (auto& [token, callback] : mCallbacks) {
97 if (callback.scheduled) {
98 ALOGD("triggerCallbacks: callback %zu", token.value());
99 callback.scheduled = false;
100 callback.func(callback.vsyncTime, callback.targetWakeupTime, callback.readyTime);
101 } else {
102 ALOGD("triggerCallbacks: callback %zu is not scheduled", token.value());
103 }
104 }
105 }
106
107private:
108 struct CallbackData {
109 explicit CallbackData(std::function<void(nsecs_t, nsecs_t, nsecs_t)> func)
110 : func(std::move(func)) {}
111
112 std::function<void(nsecs_t, nsecs_t, nsecs_t)> func;
113 bool scheduled = false;
114 nsecs_t vsyncTime = 0;
115 nsecs_t targetWakeupTime = 0;
116 nsecs_t readyTime = 0;
117 };
118
119 std::unordered_map<CallbackToken, CallbackData> mCallbacks;
120 size_t mNextToken;
121};
Ady Abraham50c202a2019-03-14 11:44:38 -0700122
123class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback {
124protected:
125 DispSyncSourceTest();
126 ~DispSyncSourceTest() override;
127
128 void createDispSync();
129 void createDispSyncSource();
130
Rachel Leef16da3c2022-01-20 13:57:18 -0800131 void onVSyncEvent(nsecs_t when, VSyncSource::VSyncData) override;
Ady Abraham50c202a2019-03-14 11:44:38 -0700132
Ady Abraham9c53ee72020-07-22 21:16:18 -0700133 std::unique_ptr<MockVSyncDispatch> mVSyncDispatch;
134 std::unique_ptr<scheduler::DispSyncSource> mDispSyncSource;
Ady Abraham50c202a2019-03-14 11:44:38 -0700135
Rachel Leef16da3c2022-01-20 13:57:18 -0800136 AsyncCallRecorder<void (*)(nsecs_t, VSyncSource::VSyncData)> mVSyncEventCallRecorder;
Ady Abraham50c202a2019-03-14 11:44:38 -0700137
Ady Abraham9c53ee72020-07-22 21:16:18 -0700138 static constexpr std::chrono::nanoseconds mWorkDuration = 20ms;
139 static constexpr std::chrono::nanoseconds mReadyDuration = 10ms;
Ady Abraham50c202a2019-03-14 11:44:38 -0700140 static constexpr int mIterations = 100;
Ady Abraham9c53ee72020-07-22 21:16:18 -0700141 const scheduler::VSyncDispatch::CallbackToken mFakeToken{2398};
142 const std::string mName = "DispSyncSourceTest";
Ady Abraham50c202a2019-03-14 11:44:38 -0700143};
144
145DispSyncSourceTest::DispSyncSourceTest() {
146 const ::testing::TestInfo* const test_info =
147 ::testing::UnitTest::GetInstance()->current_test_info();
148 ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
149}
150
151DispSyncSourceTest::~DispSyncSourceTest() {
152 const ::testing::TestInfo* const test_info =
153 ::testing::UnitTest::GetInstance()->current_test_info();
154 ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
155}
156
Rachel Leef16da3c2022-01-20 13:57:18 -0800157void DispSyncSourceTest::onVSyncEvent(nsecs_t when, VSyncSource::VSyncData vsyncData) {
Ady Abraham50c202a2019-03-14 11:44:38 -0700158 ALOGD("onVSyncEvent: %" PRId64, when);
159
Rachel Leef16da3c2022-01-20 13:57:18 -0800160 mVSyncEventCallRecorder.recordCall(when, vsyncData);
Ady Abraham50c202a2019-03-14 11:44:38 -0700161}
162
163void DispSyncSourceTest::createDispSync() {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700164 mVSyncDispatch = std::make_unique<MockVSyncDispatch>();
Ady Abraham50c202a2019-03-14 11:44:38 -0700165}
166
167void DispSyncSourceTest::createDispSyncSource() {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700168 mDispSyncSource =
169 std::make_unique<scheduler::DispSyncSource>(*mVSyncDispatch, mWorkDuration,
170 mReadyDuration, true, mName.c_str());
Ady Abraham50c202a2019-03-14 11:44:38 -0700171 mDispSyncSource->setCallback(this);
172}
173
174/* ------------------------------------------------------------------------
175 * Test cases
176 */
177
178TEST_F(DispSyncSourceTest, createDispSync) {
179 createDispSync();
Ady Abraham9c53ee72020-07-22 21:16:18 -0700180 EXPECT_TRUE(mVSyncDispatch);
Ady Abraham50c202a2019-03-14 11:44:38 -0700181}
182
183TEST_F(DispSyncSourceTest, createDispSyncSource) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700184 createDispSync();
185
186 InSequence seq;
187 EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).WillOnce(Return(mFakeToken));
188 EXPECT_CALL(*mVSyncDispatch, cancel(mFakeToken))
189 .WillOnce(Return(scheduler::CancelResult::Cancelled));
190 EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mFakeToken)).WillOnce(Return());
Ady Abraham50c202a2019-03-14 11:44:38 -0700191 createDispSyncSource();
Ady Abraham9c53ee72020-07-22 21:16:18 -0700192
Ady Abraham50c202a2019-03-14 11:44:38 -0700193 EXPECT_TRUE(mDispSyncSource);
194}
195
196TEST_F(DispSyncSourceTest, noCallbackAfterInit) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700197 createDispSync();
198
199 InSequence seq;
200 EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
201 EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
202 EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
Ady Abraham50c202a2019-03-14 11:44:38 -0700203 createDispSyncSource();
Ady Abraham9c53ee72020-07-22 21:16:18 -0700204
Ady Abraham50c202a2019-03-14 11:44:38 -0700205 EXPECT_TRUE(mDispSyncSource);
206
207 // DispSyncSource starts with Vsync disabled
Ady Abraham9c53ee72020-07-22 21:16:18 -0700208 mVSyncDispatch->triggerCallbacks();
Ady Abraham50c202a2019-03-14 11:44:38 -0700209 EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value());
210}
211
212TEST_F(DispSyncSourceTest, waitForCallbacks) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700213 createDispSync();
214
215 InSequence seq;
216 EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
217 EXPECT_CALL(*mVSyncDispatch,
218 schedule(_, Truly([&](auto timings) {
219 return timings.workDuration == mWorkDuration.count() &&
220 timings.readyDuration == mReadyDuration.count();
221 })))
222 .Times(mIterations + 1);
223 EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
224 EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
Ady Abraham50c202a2019-03-14 11:44:38 -0700225 createDispSyncSource();
Ady Abraham9c53ee72020-07-22 21:16:18 -0700226
Ady Abraham50c202a2019-03-14 11:44:38 -0700227 EXPECT_TRUE(mDispSyncSource);
228
229 mDispSyncSource->setVSyncEnabled(true);
Ady Abraham50c202a2019-03-14 11:44:38 -0700230 for (int i = 0; i < mIterations; i++) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700231 mVSyncDispatch->triggerCallbacks();
232 const auto callbackData = mVSyncEventCallRecorder.waitForCall();
233 ASSERT_TRUE(callbackData.has_value());
Rachel Leef16da3c2022-01-20 13:57:18 -0800234 const auto [when, vsyncData] = callbackData.value();
235 EXPECT_EQ(when,
236 vsyncData.expectedVSyncTimestamp - mWorkDuration.count() -
237 mReadyDuration.count());
Ady Abraham50c202a2019-03-14 11:44:38 -0700238 }
239}
240
Ady Abraham9c53ee72020-07-22 21:16:18 -0700241TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) {
242 createDispSync();
243
244 InSequence seq;
245 EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
246 EXPECT_CALL(*mVSyncDispatch,
247 schedule(_, Truly([&](auto timings) {
248 return timings.workDuration == mWorkDuration.count() &&
249 timings.readyDuration == mReadyDuration.count();
250 })))
251 .Times(1);
252
Ady Abraham50c202a2019-03-14 11:44:38 -0700253 createDispSyncSource();
Ady Abraham9c53ee72020-07-22 21:16:18 -0700254
Ady Abraham50c202a2019-03-14 11:44:38 -0700255 EXPECT_TRUE(mDispSyncSource);
256
257 mDispSyncSource->setVSyncEnabled(true);
Ady Abraham9c53ee72020-07-22 21:16:18 -0700258 EXPECT_CALL(*mVSyncDispatch,
259 schedule(_, Truly([&](auto timings) {
260 return timings.workDuration == mWorkDuration.count() &&
261 timings.readyDuration == mReadyDuration.count();
262 })))
263 .Times(mIterations);
Ady Abraham50c202a2019-03-14 11:44:38 -0700264 for (int i = 0; i < mIterations; i++) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700265 mVSyncDispatch->triggerCallbacks();
266 const auto callbackData = mVSyncEventCallRecorder.waitForCall();
267 ASSERT_TRUE(callbackData.has_value());
Rachel Leef16da3c2022-01-20 13:57:18 -0800268 const auto [when, vsyncData] = callbackData.value();
269 EXPECT_EQ(when,
270 vsyncData.expectedVSyncTimestamp - mWorkDuration.count() -
271 mReadyDuration.count());
Ady Abraham50c202a2019-03-14 11:44:38 -0700272 }
273
Ady Abraham9c53ee72020-07-22 21:16:18 -0700274 const auto newDuration = mWorkDuration / 2;
275 EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
276 return timings.workDuration == newDuration.count() &&
277 timings.readyDuration == 0;
278 })))
279 .Times(1);
280 mDispSyncSource->setDuration(newDuration, 0ns);
Ady Abraham50c202a2019-03-14 11:44:38 -0700281
Ady Abraham9c53ee72020-07-22 21:16:18 -0700282 EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
283 return timings.workDuration == newDuration.count() &&
284 timings.readyDuration == 0;
285 })))
286 .Times(mIterations);
Ady Abraham50c202a2019-03-14 11:44:38 -0700287 for (int i = 0; i < mIterations; i++) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700288 mVSyncDispatch->triggerCallbacks();
289 const auto callbackData = mVSyncEventCallRecorder.waitForCall();
290 ASSERT_TRUE(callbackData.has_value());
Rachel Leef16da3c2022-01-20 13:57:18 -0800291 const auto [when, vsyncData] = callbackData.value();
292 EXPECT_EQ(when, vsyncData.expectedVSyncTimestamp - newDuration.count());
Ady Abraham50c202a2019-03-14 11:44:38 -0700293 }
Ady Abraham9c53ee72020-07-22 21:16:18 -0700294
295 EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
296 EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
Ady Abraham50c202a2019-03-14 11:44:38 -0700297}
298
Ady Abraham50c202a2019-03-14 11:44:38 -0700299} // namespace
300} // namespace android