|  | /* | 
|  | * Copyright 2019 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | // TODO(b/129481165): remove the #pragma below and fix conversion issues | 
|  | #pragma clang diagnostic push | 
|  | #pragma clang diagnostic ignored "-Wconversion" | 
|  | #pragma clang diagnostic ignored "-Wextra" | 
|  |  | 
|  | #undef LOG_TAG | 
|  | #define LOG_TAG "LibSurfaceFlingerUnittests" | 
|  | #define LOG_NDEBUG 0 | 
|  |  | 
|  | #include "Scheduler/TimeKeeper.h" | 
|  | #include "Scheduler/VSyncDispatchTimerQueue.h" | 
|  | #include "Scheduler/VSyncTracker.h" | 
|  |  | 
|  | #include <gmock/gmock.h> | 
|  | #include <gtest/gtest.h> | 
|  | #include <thread> | 
|  |  | 
|  | using namespace testing; | 
|  | using namespace std::literals; | 
|  | namespace android::scheduler { | 
|  |  | 
|  | class MockVSyncTracker : public VSyncTracker { | 
|  | public: | 
|  | MockVSyncTracker(nsecs_t period) : mPeriod{period} { | 
|  | ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_)) | 
|  | .WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime)); | 
|  | ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); | 
|  | } | 
|  |  | 
|  | MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t)); | 
|  | MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t)); | 
|  | MOCK_CONST_METHOD0(currentPeriod, nsecs_t()); | 
|  | MOCK_METHOD1(setPeriod, void(nsecs_t)); | 
|  | MOCK_METHOD0(resetModel, void()); | 
|  | MOCK_CONST_METHOD0(needsMoreSamples, bool()); | 
|  | MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); | 
|  | MOCK_CONST_METHOD1(dump, void(std::string&)); | 
|  |  | 
|  | nsecs_t nextVSyncTime(nsecs_t timePoint) const { | 
|  | if (timePoint % mPeriod == 0) { | 
|  | return timePoint; | 
|  | } | 
|  | return (timePoint - (timePoint % mPeriod) + mPeriod); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | nsecs_t const mPeriod; | 
|  | }; | 
|  |  | 
|  | class ControllableClock : public TimeKeeper { | 
|  | public: | 
|  | ControllableClock() { | 
|  | ON_CALL(*this, alarmAt(_, _)) | 
|  | .WillByDefault(Invoke(this, &ControllableClock::alarmAtDefaultBehavior)); | 
|  | ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime)); | 
|  | } | 
|  |  | 
|  | MOCK_CONST_METHOD0(now, nsecs_t()); | 
|  | MOCK_METHOD2(alarmAt, void(std::function<void()> const&, nsecs_t time)); | 
|  | MOCK_METHOD0(alarmCancel, void()); | 
|  | MOCK_CONST_METHOD1(dump, void(std::string&)); | 
|  |  | 
|  | void alarmAtDefaultBehavior(std::function<void()> const& callback, nsecs_t time) { | 
|  | mCallback = callback; | 
|  | mNextCallbackTime = time; | 
|  | } | 
|  |  | 
|  | nsecs_t fakeTime() const { return mCurrentTime; } | 
|  |  | 
|  | void advanceToNextCallback() { | 
|  | mCurrentTime = mNextCallbackTime; | 
|  | if (mCallback) { | 
|  | mCallback(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void advanceBy(nsecs_t advancement) { | 
|  | mCurrentTime += advancement; | 
|  | if (mCurrentTime >= (mNextCallbackTime + mLag) && mCallback) { | 
|  | mCallback(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | void setLag(nsecs_t lag) { mLag = lag; } | 
|  |  | 
|  | private: | 
|  | std::function<void()> mCallback; | 
|  | nsecs_t mNextCallbackTime = 0; | 
|  | nsecs_t mCurrentTime = 0; | 
|  | nsecs_t mLag = 0; | 
|  | }; | 
|  |  | 
|  | class CountingCallback { | 
|  | public: | 
|  | CountingCallback(VSyncDispatch& dispatch) | 
|  | : mDispatch(dispatch), | 
|  | mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this, | 
|  | std::placeholders::_1, std::placeholders::_2, | 
|  | std::placeholders::_3), | 
|  | "test")) {} | 
|  | ~CountingCallback() { mDispatch.unregisterCallback(mToken); } | 
|  |  | 
|  | operator VSyncDispatch::CallbackToken() const { return mToken; } | 
|  |  | 
|  | void counter(nsecs_t time, nsecs_t wakeup_time, nsecs_t readyTime) { | 
|  | mCalls.push_back(time); | 
|  | mWakeupTime.push_back(wakeup_time); | 
|  | mReadyTime.push_back(readyTime); | 
|  | } | 
|  |  | 
|  | VSyncDispatch& mDispatch; | 
|  | VSyncDispatch::CallbackToken mToken; | 
|  | std::vector<nsecs_t> mCalls; | 
|  | std::vector<nsecs_t> mWakeupTime; | 
|  | std::vector<nsecs_t> mReadyTime; | 
|  | }; | 
|  |  | 
|  | class PausingCallback { | 
|  | public: | 
|  | PausingCallback(VSyncDispatch& dispatch, std::chrono::milliseconds pauseAmount) | 
|  | : mDispatch(dispatch), | 
|  | mToken(dispatch.registerCallback(std::bind(&PausingCallback::pause, this, | 
|  | std::placeholders::_1, | 
|  | std::placeholders::_2), | 
|  | "test")), | 
|  | mRegistered(true), | 
|  | mPauseAmount(pauseAmount) {} | 
|  | ~PausingCallback() { unregister(); } | 
|  |  | 
|  | operator VSyncDispatch::CallbackToken() const { return mToken; } | 
|  |  | 
|  | void pause(nsecs_t, nsecs_t) { | 
|  | std::unique_lock lock(mMutex); | 
|  | mPause = true; | 
|  | mCv.notify_all(); | 
|  |  | 
|  | mCv.wait_for(lock, mPauseAmount, [this] { return !mPause; }); | 
|  |  | 
|  | mResourcePresent = (mResource.lock() != nullptr); | 
|  | } | 
|  |  | 
|  | bool waitForPause() { | 
|  | std::unique_lock lock(mMutex); | 
|  | auto waiting = mCv.wait_for(lock, 10s, [this] { return mPause; }); | 
|  | return waiting; | 
|  | } | 
|  |  | 
|  | void stashResource(std::weak_ptr<void> const& resource) { mResource = resource; } | 
|  |  | 
|  | bool resourcePresent() { return mResourcePresent; } | 
|  |  | 
|  | void unpause() { | 
|  | std::unique_lock lock(mMutex); | 
|  | mPause = false; | 
|  | mCv.notify_all(); | 
|  | } | 
|  |  | 
|  | void unregister() { | 
|  | if (mRegistered) { | 
|  | mDispatch.unregisterCallback(mToken); | 
|  | mRegistered = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | VSyncDispatch& mDispatch; | 
|  | VSyncDispatch::CallbackToken mToken; | 
|  | bool mRegistered = true; | 
|  |  | 
|  | std::mutex mMutex; | 
|  | std::condition_variable mCv; | 
|  | bool mPause = false; | 
|  | std::weak_ptr<void> mResource; | 
|  | bool mResourcePresent = false; | 
|  | std::chrono::milliseconds const mPauseAmount; | 
|  | }; | 
|  |  | 
|  | class VSyncDispatchTimerQueueTest : public testing::Test { | 
|  | protected: | 
|  | std::unique_ptr<TimeKeeper> createTimeKeeper() { | 
|  | class TimeKeeperWrapper : public TimeKeeper { | 
|  | public: | 
|  | TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {} | 
|  | void alarmAt(std::function<void()> const& callback, nsecs_t time) final { | 
|  | mControllableClock.alarmAt(callback, time); | 
|  | } | 
|  | void alarmCancel() final { mControllableClock.alarmCancel(); } | 
|  | nsecs_t now() const final { return mControllableClock.now(); } | 
|  | void dump(std::string&) const final {} | 
|  |  | 
|  | private: | 
|  | TimeKeeper& mControllableClock; | 
|  | }; | 
|  | return std::make_unique<TimeKeeperWrapper>(mMockClock); | 
|  | } | 
|  |  | 
|  | ~VSyncDispatchTimerQueueTest() { | 
|  | // destructor of  dispatch will cancelAlarm(). Ignore final cancel in common test. | 
|  | Mock::VerifyAndClearExpectations(&mMockClock); | 
|  | } | 
|  |  | 
|  | void advanceToNextCallback() { mMockClock.advanceToNextCallback(); } | 
|  |  | 
|  | NiceMock<ControllableClock> mMockClock; | 
|  | static nsecs_t constexpr mDispatchGroupThreshold = 5; | 
|  | nsecs_t const mPeriod = 1000; | 
|  | nsecs_t const mVsyncMoveThreshold = 300; | 
|  | NiceMock<MockVSyncTracker> mStubTracker{mPeriod}; | 
|  | VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold, | 
|  | mVsyncMoveThreshold}; | 
|  | }; | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) { | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)); | 
|  | EXPECT_CALL(mMockClock, alarmCancel()); | 
|  | { | 
|  | VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold, | 
|  | mVsyncMoveThreshold}; | 
|  | CountingCallback cb(mDispatch); | 
|  | const auto result = mDispatch.schedule(cb, | 
|  | {.workDuration = 100, | 
|  | .readyDuration = 0, | 
|  | .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(900, *result); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) { | 
|  | auto intended = mPeriod - 230; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)); | 
|  |  | 
|  | CountingCallback cb(mDispatch); | 
|  | const auto result = mDispatch.schedule(cb, | 
|  | {.workDuration = 100, | 
|  | .readyDuration = 0, | 
|  | .earliestVsync = intended}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(900, *result); | 
|  |  | 
|  | advanceToNextCallback(); | 
|  |  | 
|  | ASSERT_THAT(cb.mCalls.size(), Eq(1)); | 
|  | EXPECT_THAT(cb.mCalls[0], Eq(mPeriod)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) { | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150)); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1050)); | 
|  |  | 
|  | CountingCallback cb(mDispatch); | 
|  | mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); | 
|  | advanceToNextCallback(); | 
|  |  | 
|  | ASSERT_THAT(cb.mCalls.size(), Eq(1)); | 
|  | EXPECT_THAT(cb.mCalls[0], Eq(1150)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) { | 
|  | auto const now = 234; | 
|  | mMockClock.advanceBy(234); | 
|  | auto const workDuration = 10 * mPeriod; | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration)) | 
|  | .WillOnce(Return(mPeriod * 11)); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, mPeriod)); | 
|  |  | 
|  | CountingCallback cb(mDispatch); | 
|  | const auto result = mDispatch.schedule(cb, | 
|  | {.workDuration = workDuration, | 
|  | .readyDuration = 0, | 
|  | .earliestVsync = mPeriod}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(mPeriod, *result); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) { | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)); | 
|  | EXPECT_CALL(mMockClock, alarmCancel()); | 
|  |  | 
|  | CountingCallback cb(mDispatch); | 
|  | const auto result = | 
|  | mDispatch.schedule(cb, | 
|  | {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(mPeriod - 100, *result); | 
|  | EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) { | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)); | 
|  | EXPECT_CALL(mMockClock, alarmCancel()); | 
|  |  | 
|  | CountingCallback cb(mDispatch); | 
|  | const auto result = | 
|  | mDispatch.schedule(cb, | 
|  | {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(mPeriod - 100, *result); | 
|  | mMockClock.advanceBy(950); | 
|  | EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) { | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)); | 
|  | EXPECT_CALL(mMockClock, alarmCancel()); | 
|  |  | 
|  | PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s)); | 
|  | const auto result = | 
|  | mDispatch.schedule(cb, | 
|  | {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(mPeriod - 100, *result); | 
|  |  | 
|  | std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); }); | 
|  | EXPECT_TRUE(cb.waitForPause()); | 
|  | EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate); | 
|  | cb.unpause(); | 
|  | pausingThread.join(); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) { | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)); | 
|  | EXPECT_CALL(mMockClock, alarmCancel()); | 
|  |  | 
|  | auto resource = std::make_shared<int>(110); | 
|  |  | 
|  | PausingCallback cb(mDispatch, 50ms); | 
|  | cb.stashResource(resource); | 
|  | const auto result = | 
|  | mDispatch.schedule(cb, | 
|  | {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(mPeriod - 100, *result); | 
|  |  | 
|  | std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); }); | 
|  | EXPECT_TRUE(cb.waitForPause()); | 
|  |  | 
|  | cb.unregister(); | 
|  | resource.reset(); | 
|  |  | 
|  | cb.unpause(); | 
|  | pausingThread.join(); | 
|  |  | 
|  | EXPECT_TRUE(cb.resourcePresent()); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)) | 
|  | .Times(4) | 
|  | .WillOnce(Return(1055)) | 
|  | .WillOnce(Return(1063)) | 
|  | .WillOnce(Return(1063)) | 
|  | .WillOnce(Return(1075)); | 
|  |  | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 955)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 813)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 975)).InSequence(seq); | 
|  |  | 
|  | CountingCallback cb0(mDispatch); | 
|  | CountingCallback cb1(mDispatch); | 
|  |  | 
|  | mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); | 
|  | mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); | 
|  |  | 
|  | advanceToNextCallback(); | 
|  | advanceToNextCallback(); | 
|  |  | 
|  | ASSERT_THAT(cb0.mCalls.size(), Eq(1)); | 
|  | EXPECT_THAT(cb0.mCalls[0], Eq(1075)); | 
|  | ASSERT_THAT(cb1.mCalls.size(), Eq(1)); | 
|  | EXPECT_THAT(cb1.mCalls[0], Eq(1063)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) { | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) | 
|  | .Times(4) | 
|  | .WillOnce(Return(10000)) | 
|  | .WillOnce(Return(1000)) | 
|  | .WillOnce(Return(10000)) | 
|  | .WillOnce(Return(10000)); | 
|  |  | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 9900)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 750)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 9900)).InSequence(seq); | 
|  |  | 
|  | CountingCallback cb0(mDispatch); | 
|  | CountingCallback cb1(mDispatch); | 
|  |  | 
|  | mDispatch.schedule(cb0, | 
|  | {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10}); | 
|  | mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); | 
|  | mDispatch.cancel(cb1); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq); | 
|  |  | 
|  | CountingCallback cb0(mDispatch); | 
|  | CountingCallback cb1(mDispatch); | 
|  |  | 
|  | mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | mDispatch.schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | advanceToNextCallback(); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); | 
|  |  | 
|  | CountingCallback cb0(mDispatch); | 
|  | CountingCallback cb1(mDispatch); | 
|  |  | 
|  | mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | advanceToNextCallback(); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1600)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1590)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1600)).InSequence(seq); | 
|  |  | 
|  | auto offset = 400; | 
|  | auto closeOffset = offset + mDispatchGroupThreshold - 1; | 
|  | auto notCloseOffset = offset + 2 * mDispatchGroupThreshold; | 
|  |  | 
|  | CountingCallback cb0(mDispatch); | 
|  | CountingCallback cb1(mDispatch); | 
|  |  | 
|  | mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | mDispatch.schedule(cb1, | 
|  | {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000}); | 
|  |  | 
|  | advanceToNextCallback(); | 
|  | ASSERT_THAT(cb0.mCalls.size(), Eq(1)); | 
|  | EXPECT_THAT(cb0.mCalls[0], Eq(mPeriod)); | 
|  | ASSERT_THAT(cb1.mCalls.size(), Eq(1)); | 
|  | EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod)); | 
|  |  | 
|  | mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000}); | 
|  | mDispatch.schedule(cb1, | 
|  | {.workDuration = notCloseOffset, .readyDuration = 0, .earliestVsync = 2000}); | 
|  | advanceToNextCallback(); | 
|  | ASSERT_THAT(cb1.mCalls.size(), Eq(2)); | 
|  | EXPECT_THAT(cb1.mCalls[1], Eq(2000)); | 
|  |  | 
|  | advanceToNextCallback(); | 
|  | ASSERT_THAT(cb0.mCalls.size(), Eq(2)); | 
|  | EXPECT_THAT(cb0.mCalls[1], Eq(2000)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 800)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmCancel()); | 
|  |  | 
|  | CountingCallback cb0(mDispatch); | 
|  | CountingCallback cb1(mDispatch); | 
|  |  | 
|  | mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | advanceToNextCallback(); | 
|  | EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) { | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) | 
|  | .Times(3) | 
|  | .WillOnce(Return(950)) | 
|  | .WillOnce(Return(1975)) | 
|  | .WillOnce(Return(2950)); | 
|  |  | 
|  | CountingCallback cb(mDispatch); | 
|  | mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920}); | 
|  |  | 
|  | mMockClock.advanceBy(850); | 
|  | EXPECT_THAT(cb.mCalls.size(), Eq(1)); | 
|  |  | 
|  | mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900}); | 
|  | mMockClock.advanceBy(900); | 
|  | EXPECT_THAT(cb.mCalls.size(), Eq(1)); | 
|  | mMockClock.advanceBy(125); | 
|  | EXPECT_THAT(cb.mCalls.size(), Eq(2)); | 
|  |  | 
|  | mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900}); | 
|  | mMockClock.advanceBy(975); | 
|  | EXPECT_THAT(cb.mCalls.size(), Eq(3)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq); | 
|  |  | 
|  | VSyncDispatch::CallbackToken tmp; | 
|  | tmp = mDispatch.registerCallback( | 
|  | [&](auto, auto, auto) { | 
|  | mDispatch.schedule(tmp, | 
|  | {.workDuration = 100, | 
|  | .readyDuration = 0, | 
|  | .earliestVsync = 2000}); | 
|  | }, | 
|  | "o.o"); | 
|  |  | 
|  | mDispatch.schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | advanceToNextCallback(); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) { | 
|  | VSyncDispatch::CallbackToken tmp; | 
|  | std::optional<nsecs_t> lastTarget; | 
|  | tmp = mDispatch.registerCallback( | 
|  | [&](auto timestamp, auto, auto) { | 
|  | auto result = | 
|  | mDispatch.schedule(tmp, | 
|  | {.workDuration = 400, | 
|  | .readyDuration = 0, | 
|  | .earliestVsync = timestamp - mVsyncMoveThreshold}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(mPeriod + timestamp - 400, *result); | 
|  | result = mDispatch.schedule(tmp, | 
|  | {.workDuration = 400, | 
|  | .readyDuration = 0, | 
|  | .earliestVsync = timestamp}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(mPeriod + timestamp - 400, *result); | 
|  | result = mDispatch.schedule(tmp, | 
|  | {.workDuration = 400, | 
|  | .readyDuration = 0, | 
|  | .earliestVsync = timestamp + mVsyncMoveThreshold}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(mPeriod + timestamp - 400, *result); | 
|  | lastTarget = timestamp; | 
|  | }, | 
|  | "oo"); | 
|  |  | 
|  | mDispatch.schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | advanceToNextCallback(); | 
|  | EXPECT_THAT(lastTarget, Eq(1000)); | 
|  |  | 
|  | advanceToNextCallback(); | 
|  | EXPECT_THAT(lastTarget, Eq(2000)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1000)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 950)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1950)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq); | 
|  |  | 
|  | CountingCallback cb(mDispatch); | 
|  | mDispatch.schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000}); | 
|  |  | 
|  | mMockClock.advanceBy(750); | 
|  | mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000}); | 
|  |  | 
|  | advanceToNextCallback(); | 
|  | mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000}); | 
|  |  | 
|  | mMockClock.advanceBy(800); | 
|  | mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, lateModifications) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 850)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1800)).InSequence(seq); | 
|  |  | 
|  | CountingCallback cb0(mDispatch); | 
|  | CountingCallback cb1(mDispatch); | 
|  |  | 
|  | mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | mDispatch.schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); | 
|  |  | 
|  | advanceToNextCallback(); | 
|  | mDispatch.schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000}); | 
|  | mDispatch.schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000}); | 
|  |  | 
|  | advanceToNextCallback(); | 
|  | advanceToNextCallback(); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); | 
|  |  | 
|  | CountingCallback cb0(mDispatch); | 
|  | CountingCallback cb1(mDispatch); | 
|  | mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000}); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq); | 
|  |  | 
|  | CountingCallback cb0(mDispatch); | 
|  | mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | mDispatch.cancel(cb0); | 
|  | mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) { | 
|  | VSyncDispatch::CallbackToken token(100); | 
|  | EXPECT_FALSE(mDispatch | 
|  | .schedule(token, | 
|  | {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}) | 
|  | .has_value()); | 
|  | EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) { | 
|  | CountingCallback cb0(mDispatch); | 
|  | auto result = | 
|  | mDispatch.schedule(cb0, | 
|  | {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(500, *result); | 
|  | result = mDispatch.schedule(cb0, | 
|  | {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(900, *result); | 
|  | } | 
|  |  | 
|  | // b/1450138150 | 
|  | TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) { | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 500)); | 
|  | CountingCallback cb(mDispatch); | 
|  | auto result = | 
|  | mDispatch.schedule(cb, | 
|  | {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(500, *result); | 
|  | mMockClock.advanceBy(400); | 
|  |  | 
|  | result = mDispatch.schedule(cb, | 
|  | {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(1200, *result); | 
|  | advanceToNextCallback(); | 
|  | ASSERT_THAT(cb.mCalls.size(), Eq(1)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) { | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)) | 
|  | .Times(2) | 
|  | .WillOnce(Return(1000)) | 
|  | .WillOnce(Return(1002)); | 
|  | CountingCallback cb(mDispatch); | 
|  | auto result = | 
|  | mDispatch.schedule(cb, | 
|  | {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(500, *result); | 
|  | mMockClock.advanceBy(400); | 
|  | result = mDispatch.schedule(cb, | 
|  | {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(602, *result); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) { | 
|  | CountingCallback cb0(mDispatch); | 
|  | auto result = | 
|  | mDispatch.schedule(cb0, | 
|  | {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(500, *result); | 
|  | advanceToNextCallback(); | 
|  | result = mDispatch.schedule(cb0, | 
|  | {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(900, *result); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq); | 
|  | CountingCallback cb0(mDispatch); | 
|  | auto result = | 
|  | mDispatch.schedule(cb0, | 
|  | {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(500, *result); | 
|  | advanceToNextCallback(); | 
|  | result = mDispatch.schedule(cb0, | 
|  | {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(1100, *result); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) { | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 600)); | 
|  |  | 
|  | CountingCallback cb(mDispatch); | 
|  | auto result = | 
|  | mDispatch.schedule(cb, | 
|  | {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(600, *result); | 
|  |  | 
|  | result = mDispatch.schedule(cb, | 
|  | {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(600, *result); | 
|  |  | 
|  | advanceToNextCallback(); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, helperMove) { | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1); | 
|  | EXPECT_CALL(mMockClock, alarmCancel()).Times(1); | 
|  |  | 
|  | VSyncCallbackRegistration cb( | 
|  | mDispatch, [](auto, auto, auto) {}, ""); | 
|  | VSyncCallbackRegistration cb1(std::move(cb)); | 
|  | cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | cb.cancel(); | 
|  |  | 
|  | cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | cb1.cancel(); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) { | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1); | 
|  | EXPECT_CALL(mMockClock, alarmCancel()).Times(1); | 
|  |  | 
|  | VSyncCallbackRegistration cb( | 
|  | mDispatch, [](auto, auto, auto) {}, ""); | 
|  | VSyncCallbackRegistration cb1( | 
|  | mDispatch, [](auto, auto, auto) {}, ""); | 
|  | cb1 = std::move(cb); | 
|  | cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | cb.cancel(); | 
|  |  | 
|  | cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | cb1.cancel(); | 
|  | } | 
|  |  | 
|  | // b/154303580 | 
|  | TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq); | 
|  | CountingCallback cb1(mDispatch); | 
|  | CountingCallback cb2(mDispatch); | 
|  |  | 
|  | auto result = | 
|  | mDispatch.schedule(cb1, | 
|  | {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(600, *result); | 
|  |  | 
|  | mMockClock.setLag(100); | 
|  | mMockClock.advanceBy(620); | 
|  |  | 
|  | result = mDispatch.schedule(cb2, | 
|  | {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(1900, *result); | 
|  | mMockClock.advanceBy(80); | 
|  |  | 
|  | EXPECT_THAT(cb1.mCalls.size(), Eq(1)); | 
|  | EXPECT_THAT(cb2.mCalls.size(), Eq(0)); | 
|  | } | 
|  |  | 
|  | // b/154303580. | 
|  | // If the same callback tries to reschedule itself after it's too late, timer opts to apply the | 
|  | // update later, as opposed to blocking the calling thread. | 
|  | TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminentSameCallback) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq); | 
|  | CountingCallback cb(mDispatch); | 
|  |  | 
|  | auto result = | 
|  | mDispatch.schedule(cb, | 
|  | {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(600, *result); | 
|  |  | 
|  | mMockClock.setLag(100); | 
|  | mMockClock.advanceBy(620); | 
|  |  | 
|  | result = mDispatch.schedule(cb, | 
|  | {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(1630, *result); | 
|  | mMockClock.advanceBy(80); | 
|  |  | 
|  | EXPECT_THAT(cb.mCalls.size(), Eq(1)); | 
|  | } | 
|  |  | 
|  | // b/154303580. | 
|  | TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq); | 
|  | CountingCallback cb1(mDispatch); | 
|  | CountingCallback cb2(mDispatch); | 
|  |  | 
|  | auto result = | 
|  | mDispatch.schedule(cb1, | 
|  | {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(600, *result); | 
|  | result = mDispatch.schedule(cb2, | 
|  | {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(1900, *result); | 
|  |  | 
|  | mMockClock.setLag(100); | 
|  | mMockClock.advanceBy(620); | 
|  |  | 
|  | EXPECT_EQ(mDispatch.cancel(cb2), CancelResult::Cancelled); | 
|  |  | 
|  | mMockClock.advanceBy(80); | 
|  |  | 
|  | EXPECT_THAT(cb1.mCalls.size(), Eq(1)); | 
|  | EXPECT_THAT(cb2.mCalls.size(), Eq(0)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) { | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq); | 
|  | EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq); | 
|  | CountingCallback cb1(mDispatch); | 
|  | CountingCallback cb2(mDispatch); | 
|  |  | 
|  | auto result = | 
|  | mDispatch.schedule(cb1, | 
|  | {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(600, *result); | 
|  | result = mDispatch.schedule(cb2, | 
|  | {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(1900, *result); | 
|  |  | 
|  | mMockClock.setLag(100); | 
|  | mMockClock.advanceBy(620); | 
|  |  | 
|  | EXPECT_EQ(mDispatch.cancel(cb1), CancelResult::Cancelled); | 
|  |  | 
|  | EXPECT_THAT(cb1.mCalls.size(), Eq(0)); | 
|  | EXPECT_THAT(cb2.mCalls.size(), Eq(0)); | 
|  | mMockClock.advanceToNextCallback(); | 
|  |  | 
|  | EXPECT_THAT(cb1.mCalls.size(), Eq(0)); | 
|  | EXPECT_THAT(cb2.mCalls.size(), Eq(1)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, laggedTimerGroupsCallbacksWithinLag) { | 
|  | CountingCallback cb1(mDispatch); | 
|  | CountingCallback cb2(mDispatch); | 
|  |  | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)) | 
|  | .InSequence(seq) | 
|  | .WillOnce(Return(1000)); | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)) | 
|  | .InSequence(seq) | 
|  | .WillOnce(Return(1000)); | 
|  |  | 
|  | auto result = | 
|  | mDispatch.schedule(cb1, | 
|  | {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(600, *result); | 
|  | result = mDispatch.schedule(cb2, | 
|  | {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(610, *result); | 
|  |  | 
|  | mMockClock.setLag(100); | 
|  | mMockClock.advanceBy(700); | 
|  |  | 
|  | ASSERT_THAT(cb1.mWakeupTime.size(), Eq(1)); | 
|  | EXPECT_THAT(cb1.mWakeupTime[0], Eq(600)); | 
|  | ASSERT_THAT(cb1.mReadyTime.size(), Eq(1)); | 
|  | EXPECT_THAT(cb1.mReadyTime[0], Eq(1000)); | 
|  | ASSERT_THAT(cb2.mWakeupTime.size(), Eq(1)); | 
|  | EXPECT_THAT(cb2.mWakeupTime[0], Eq(610)); | 
|  | ASSERT_THAT(cb2.mReadyTime.size(), Eq(1)); | 
|  | EXPECT_THAT(cb2.mReadyTime[0], Eq(1000)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) { | 
|  | auto intended = mPeriod - 230; | 
|  | EXPECT_CALL(mMockClock, alarmAt(_, 900)); | 
|  |  | 
|  | CountingCallback cb(mDispatch); | 
|  | const auto result = mDispatch.schedule(cb, | 
|  | {.workDuration = 70, | 
|  | .readyDuration = 30, | 
|  | .earliestVsync = intended}); | 
|  | EXPECT_TRUE(result.has_value()); | 
|  | EXPECT_EQ(900, *result); | 
|  | advanceToNextCallback(); | 
|  |  | 
|  | ASSERT_THAT(cb.mCalls.size(), Eq(1)); | 
|  | EXPECT_THAT(cb.mCalls[0], Eq(mPeriod)); | 
|  | ASSERT_THAT(cb.mWakeupTime.size(), Eq(1)); | 
|  | EXPECT_THAT(cb.mWakeupTime[0], 900); | 
|  | ASSERT_THAT(cb.mReadyTime.size(), Eq(1)); | 
|  | EXPECT_THAT(cb.mReadyTime[0], 970); | 
|  | } | 
|  |  | 
|  | class VSyncDispatchTimerQueueEntryTest : public testing::Test { | 
|  | protected: | 
|  | nsecs_t const mPeriod = 1000; | 
|  | nsecs_t const mVsyncMoveThreshold = 200; | 
|  | NiceMock<MockVSyncTracker> mStubTracker{mPeriod}; | 
|  | }; | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) { | 
|  | std::string name("basicname"); | 
|  | VSyncDispatchTimerQueueEntry entry( | 
|  | name, [](auto, auto, auto) {}, mVsyncMoveThreshold); | 
|  | EXPECT_THAT(entry.name(), Eq(name)); | 
|  | EXPECT_FALSE(entry.lastExecutedVsyncTarget()); | 
|  | EXPECT_FALSE(entry.wakeupTime()); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) { | 
|  | VSyncDispatchTimerQueueEntry entry( | 
|  | "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); | 
|  |  | 
|  | EXPECT_FALSE(entry.wakeupTime()); | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | auto const wakeup = entry.wakeupTime(); | 
|  | ASSERT_TRUE(wakeup); | 
|  | EXPECT_THAT(*wakeup, Eq(900)); | 
|  |  | 
|  | entry.disarm(); | 
|  | EXPECT_FALSE(entry.wakeupTime()); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) { | 
|  | auto const duration = 500; | 
|  | auto const now = 8750; | 
|  |  | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration)) | 
|  | .Times(1) | 
|  | .WillOnce(Return(10000)); | 
|  | VSyncDispatchTimerQueueEntry entry( | 
|  | "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); | 
|  |  | 
|  | EXPECT_FALSE(entry.wakeupTime()); | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994}, | 
|  | mStubTracker, now) | 
|  | .has_value()); | 
|  | auto const wakeup = entry.wakeupTime(); | 
|  | ASSERT_TRUE(wakeup); | 
|  | EXPECT_THAT(*wakeup, Eq(9500)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) { | 
|  | auto callCount = 0; | 
|  | auto vsyncCalledTime = 0; | 
|  | auto wakeupCalledTime = 0; | 
|  | auto readyCalledTime = 0; | 
|  | VSyncDispatchTimerQueueEntry entry( | 
|  | "test", | 
|  | [&](auto vsyncTime, auto wakeupTime, auto readyTime) { | 
|  | callCount++; | 
|  | vsyncCalledTime = vsyncTime; | 
|  | wakeupCalledTime = wakeupTime; | 
|  | readyCalledTime = readyTime; | 
|  | }, | 
|  | mVsyncMoveThreshold); | 
|  |  | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | auto const wakeup = entry.wakeupTime(); | 
|  | ASSERT_TRUE(wakeup); | 
|  | EXPECT_THAT(*wakeup, Eq(900)); | 
|  |  | 
|  | auto const ready = entry.readyTime(); | 
|  | ASSERT_TRUE(ready); | 
|  | EXPECT_THAT(*ready, Eq(1000)); | 
|  |  | 
|  | entry.callback(entry.executing(), *wakeup, *ready); | 
|  |  | 
|  | EXPECT_THAT(callCount, Eq(1)); | 
|  | EXPECT_THAT(vsyncCalledTime, Eq(mPeriod)); | 
|  | EXPECT_THAT(wakeupCalledTime, Eq(*wakeup)); | 
|  | EXPECT_FALSE(entry.wakeupTime()); | 
|  | auto lastCalledTarget = entry.lastExecutedVsyncTarget(); | 
|  | ASSERT_TRUE(lastCalledTarget); | 
|  | EXPECT_THAT(*lastCalledTarget, Eq(mPeriod)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) | 
|  | .Times(2) | 
|  | .WillOnce(Return(1000)) | 
|  | .WillOnce(Return(1020)); | 
|  |  | 
|  | VSyncDispatchTimerQueueEntry entry( | 
|  | "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); | 
|  |  | 
|  | EXPECT_FALSE(entry.wakeupTime()); | 
|  | entry.update(mStubTracker, 0); | 
|  | EXPECT_FALSE(entry.wakeupTime()); | 
|  |  | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | auto wakeup = entry.wakeupTime(); | 
|  | ASSERT_TRUE(wakeup); | 
|  | EXPECT_THAT(wakeup, Eq(900)); | 
|  |  | 
|  | entry.update(mStubTracker, 0); | 
|  | wakeup = entry.wakeupTime(); | 
|  | ASSERT_TRUE(wakeup); | 
|  | EXPECT_THAT(*wakeup, Eq(920)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) { | 
|  | VSyncDispatchTimerQueueEntry entry( | 
|  | "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | entry.update(mStubTracker, 0); | 
|  |  | 
|  | auto const wakeup = entry.wakeupTime(); | 
|  | ASSERT_TRUE(wakeup); | 
|  | EXPECT_THAT(*wakeup, Eq(wakeup)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) { | 
|  | VSyncDispatchTimerQueueEntry entry( | 
|  | "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | entry.executing(); // 1000 is executing | 
|  | // had 1000 not been executing, this could have been scheduled for time 800. | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); | 
|  | EXPECT_THAT(*entry.readyTime(), Eq(2000)); | 
|  |  | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | EXPECT_THAT(*entry.wakeupTime(), Eq(1950)); | 
|  | EXPECT_THAT(*entry.readyTime(), Eq(2000)); | 
|  |  | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); | 
|  | EXPECT_THAT(*entry.readyTime(), Eq(2000)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueEntryTest, | 
|  | willRequestNextEstimateWhenSnappingToNextTargettableVSync) { | 
|  | VSyncDispatchTimerQueueEntry entry( | 
|  | "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); | 
|  |  | 
|  | Sequence seq; | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500)) | 
|  | .InSequence(seq) | 
|  | .WillOnce(Return(1000)); | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500)) | 
|  | .InSequence(seq) | 
|  | .WillOnce(Return(1000)); | 
|  | EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold)) | 
|  | .InSequence(seq) | 
|  | .WillOnce(Return(2000)); | 
|  |  | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  |  | 
|  | entry.executing(); // 1000 is executing | 
|  |  | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) { | 
|  | VSyncDispatchTimerQueueEntry entry( | 
|  | "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) { | 
|  | static constexpr auto effectualOffset = 200; | 
|  | VSyncDispatchTimerQueueEntry entry( | 
|  | "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); | 
|  | EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); | 
|  | entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .earliestVsync = 400}); | 
|  | entry.addPendingWorkloadUpdate( | 
|  | {.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400}); | 
|  | EXPECT_TRUE(entry.hasPendingWorkloadUpdate()); | 
|  | entry.update(mStubTracker, 0); | 
|  | EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); | 
|  | EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset)); | 
|  | } | 
|  |  | 
|  | TEST_F(VSyncDispatchTimerQueueEntryTest, runCallbackWithReadyDuration) { | 
|  | auto callCount = 0; | 
|  | auto vsyncCalledTime = 0; | 
|  | auto wakeupCalledTime = 0; | 
|  | auto readyCalledTime = 0; | 
|  | VSyncDispatchTimerQueueEntry entry( | 
|  | "test", | 
|  | [&](auto vsyncTime, auto wakeupTime, auto readyTime) { | 
|  | callCount++; | 
|  | vsyncCalledTime = vsyncTime; | 
|  | wakeupCalledTime = wakeupTime; | 
|  | readyCalledTime = readyTime; | 
|  | }, | 
|  | mVsyncMoveThreshold); | 
|  |  | 
|  | EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500}, | 
|  | mStubTracker, 0) | 
|  | .has_value()); | 
|  | auto const wakeup = entry.wakeupTime(); | 
|  | ASSERT_TRUE(wakeup); | 
|  | EXPECT_THAT(*wakeup, Eq(900)); | 
|  |  | 
|  | auto const ready = entry.readyTime(); | 
|  | ASSERT_TRUE(ready); | 
|  | EXPECT_THAT(*ready, Eq(970)); | 
|  |  | 
|  | entry.callback(entry.executing(), *wakeup, *ready); | 
|  |  | 
|  | EXPECT_THAT(callCount, Eq(1)); | 
|  | EXPECT_THAT(vsyncCalledTime, Eq(mPeriod)); | 
|  | EXPECT_THAT(wakeupCalledTime, Eq(*wakeup)); | 
|  | EXPECT_FALSE(entry.wakeupTime()); | 
|  | auto lastCalledTarget = entry.lastExecutedVsyncTarget(); | 
|  | ASSERT_TRUE(lastCalledTarget); | 
|  | EXPECT_THAT(*lastCalledTarget, Eq(mPeriod)); | 
|  | } | 
|  |  | 
|  | } // namespace android::scheduler | 
|  |  | 
|  | // TODO(b/129481165): remove the #pragma below and fix conversion issues | 
|  | #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" |