blob: a97256203d521555ace9da5d39daba9e985d2bec [file] [log] [blame]
/*
* 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.
*/
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
#define LOG_NDEBUG 0
#include "Scheduler/TimeKeeper.h"
#include "Scheduler/VSyncDispatch.h"
#include "Scheduler/VSyncReactor.h"
#include "Scheduler/VSyncTracker.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
#include <array>
using namespace testing;
using namespace std::literals;
namespace android::scheduler {
class MockVSyncTracker : public VSyncTracker {
public:
MockVSyncTracker() { 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_METHOD1(dump, void(std::string&));
};
class VSyncTrackerWrapper : public VSyncTracker {
public:
VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
bool addVsyncTimestamp(nsecs_t timestamp) final {
return mTracker->addVsyncTimestamp(timestamp);
}
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
}
nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
void resetModel() final { mTracker->resetModel(); }
void dump(std::string& result) const final { mTracker->dump(result); }
private:
std::shared_ptr<VSyncTracker> const mTracker;
};
class MockClock : public Clock {
public:
MOCK_CONST_METHOD0(now, nsecs_t());
};
class ClockWrapper : public Clock {
public:
ClockWrapper(std::shared_ptr<Clock> const& clock) : mClock(clock) {}
nsecs_t now() const { return mClock->now(); }
private:
std::shared_ptr<Clock> const mClock;
};
class MockVSyncDispatch : public VSyncDispatch {
public:
MOCK_METHOD2(registerCallback,
CallbackToken(std::function<void(nsecs_t, nsecs_t)> const&, std::string));
MOCK_METHOD1(unregisterCallback, void(CallbackToken));
MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
class VSyncDispatchWrapper : public VSyncDispatch {
public:
VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
CallbackToken registerCallback(std::function<void(nsecs_t, nsecs_t)> const& callbackFn,
std::string callbackName) final {
return mDispatch->registerCallback(callbackFn, callbackName);
}
void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }
ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
nsecs_t earliestVsync) final {
return mDispatch->schedule(token, workDuration, earliestVsync);
}
CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
void dump(std::string& result) const final { return mDispatch->dump(result); }
private:
std::shared_ptr<VSyncDispatch> const mDispatch;
};
std::shared_ptr<FenceTime> generateInvalidFence() {
sp<Fence> fence = new Fence();
return std::make_shared<FenceTime>(fence);
}
std::shared_ptr<FenceTime> generatePendingFence() {
sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
return std::make_shared<FenceTime>(fence);
}
void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
FenceTime::Snapshot snap(time);
fence->applyTrustedSnapshot(snap);
}
std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
signalFenceWithTime(ft, time);
return ft;
}
class StubCallback : public DispSync::Callback {
public:
void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final {
std::lock_guard<std::mutex> lk(mMutex);
mLastCallTime = when;
}
std::optional<nsecs_t> lastCallTime() const {
std::lock_guard<std::mutex> lk(mMutex);
return mLastCallTime;
}
private:
std::mutex mutable mMutex;
std::optional<nsecs_t> mLastCallTime GUARDED_BY(mMutex);
};
class VSyncReactorTest : public testing::Test {
protected:
VSyncReactorTest()
: mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()),
mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
mMockClock(std::make_shared<NiceMock<MockClock>>()),
mReactor(std::make_unique<ClockWrapper>(mMockClock),
std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit,
false /* supportKernelIdleTimer */) {
ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
}
std::shared_ptr<MockVSyncDispatch> mMockDispatch;
std::shared_ptr<MockVSyncTracker> mMockTracker;
std::shared_ptr<MockClock> mMockClock;
static constexpr size_t kPendingLimit = 3;
static constexpr nsecs_t mDummyTime = 47;
static constexpr nsecs_t mPhase = 3000;
static constexpr nsecs_t mAnotherPhase = 5200;
static constexpr nsecs_t period = 10000;
static constexpr nsecs_t mFakeVSyncTime = 2093;
static constexpr nsecs_t mFakeWakeupTime = 1892;
static constexpr nsecs_t mFakeNow = 2214;
static constexpr const char mName[] = "callbacky";
VSyncDispatch::CallbackToken const mFakeToken{2398};
nsecs_t lastCallbackTime = 0;
StubCallback outerCb;
std::function<void(nsecs_t, nsecs_t)> innerCb;
VSyncReactor mReactor;
};
TEST_F(VSyncReactorTest, addingNullFenceCheck) {
EXPECT_FALSE(mReactor.addPresentFence(nullptr));
}
TEST_F(VSyncReactorTest, addingInvalidFenceSignalsNeedsMoreInfo) {
EXPECT_TRUE(mReactor.addPresentFence(generateInvalidFence()));
}
TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
}
TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
nsecs_t anotherDummyTime = 2919019201;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(0);
auto pendingFence = generatePendingFence();
EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
Mock::VerifyAndClearExpectations(mMockTracker.get());
signalFenceWithTime(pendingFence, mDummyTime);
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
}
TEST_F(VSyncReactorTest, limitsPendingFences) {
std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
std::array<nsecs_t, fences.size()> fakeTimes;
std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
i++;
return i * i;
});
for (auto const& fence : fences) {
mReactor.addPresentFence(fence);
}
for (auto i = fences.size() - kPendingLimit; i < fences.size(); i++) {
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimes[i]));
}
for (auto i = 0u; i < fences.size(); i++) {
signalFenceWithTime(fences[i], fakeTimes[i]);
}
mReactor.addPresentFence(generatePendingFence());
}
TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
static constexpr size_t aFewTimes = 8;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime)).Times(1);
mReactor.setIgnorePresentFences(true);
for (auto i = 0; i < aFewTimes; i++) {
mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime));
}
mReactor.setIgnorePresentFences(false);
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
}
TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) {
bool periodFlushed = true;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
mReactor.setPeriod(newPeriod);
EXPECT_TRUE(mReactor.addResyncSample(0, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
EXPECT_FALSE(mReactor.addResyncSample(newPeriod, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
nsecs_t const fakeTimestamp = 4839;
EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
.Times(1)
.WillOnce(Return(fakeTimestamp));
EXPECT_THAT(mReactor.computeNextRefresh(0, mMockClock->now()), Eq(fakeTimestamp));
}
TEST_F(VSyncReactorTest, queriesTrackerForExpectedPresentTime) {
nsecs_t const fakeTimestamp = 4839;
EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
.Times(1)
.WillOnce(Return(fakeTimestamp));
EXPECT_THAT(mReactor.expectedPresentTime(mMockClock->now()), Eq(fakeTimestamp));
}
TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
nsecs_t const fakeTimestamp = 4839;
nsecs_t const fakePeriod = 1010;
nsecs_t const mFakeNow = 2214;
int const numPeriodsOut = 3;
EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow));
EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod))
.WillOnce(Return(fakeTimestamp));
EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut, mMockClock->now()), Eq(fakeTimestamp));
}
TEST_F(VSyncReactorTest, getPeriod) {
nsecs_t const fakePeriod = 1010;
EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
}
TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
nsecs_t const newPeriod = 5000;
EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
mReactor.setPeriod(newPeriod);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
EXPECT_TRUE(mReactor.addResyncSample(20000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
Mock::VerifyAndClearExpectations(mMockTracker.get());
EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
EXPECT_FALSE(mReactor.addResyncSample(25000, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) {
nsecs_t sampleTime = 0;
nsecs_t const newPeriod = 5000;
mReactor.setPeriod(newPeriod);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
mReactor.setPeriod(period);
EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
TEST_F(VSyncReactorTest, changingToAThirdPeriodWillWaitForLastPeriod) {
nsecs_t sampleTime = 0;
nsecs_t const secondPeriod = 5000;
nsecs_t const thirdPeriod = 2000;
mReactor.setPeriod(secondPeriod);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
mReactor.setPeriod(thirdPeriod);
EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSync) {
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
.WillOnce(Return(false))
.WillOnce(Return(true))
.WillOnce(Return(true));
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
nsecs_t skewyPeriod = period >> 1;
bool periodFlushed = false;
nsecs_t sampleTime = 0;
EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncPendingFence) {
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
.Times(2)
.WillOnce(Return(false))
.WillOnce(Return(true));
auto fence = generatePendingFence();
EXPECT_FALSE(mReactor.addPresentFence(fence));
signalFenceWithTime(fence, period >> 1);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
nsecs_t const newPeriod = 5000;
mReactor.setPeriod(newPeriod);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
nsecs_t const newPeriod = 5000;
EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
mReactor.setPeriod(newPeriod);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addResyncSample(5000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
Mock::VerifyAndClearExpectations(mMockTracker.get());
EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
EXPECT_FALSE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
TEST_F(VSyncReactorTest, addResyncSampleTypical) {
nsecs_t const fakeTimestamp = 3032;
bool periodFlushed = false;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) {
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
mReactor.setPeriod(newPeriod);
auto time = 0;
auto constexpr numTimestampSubmissions = 10;
for (auto i = 0; i < numTimestampSubmissions; i++) {
time += period;
EXPECT_TRUE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
time += newPeriod;
EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
for (auto i = 0; i < numTimestampSubmissions; i++) {
time += newPeriod;
EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
}
TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) {
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
mReactor.setPeriod(newPeriod);
time += period;
mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
time += newPeriod;
mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
return period - phase;
}
TEST_F(VSyncReactorTest, addEventListener) {
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
.InSequence(seq)
.WillOnce(Return(mFakeToken));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
.InSequence(seq);
EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
mReactor.removeEventListener(&outerCb, &lastCallbackTime);
}
TEST_F(VSyncReactorTest, addEventListenerTwiceChangesPhase) {
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
.InSequence(seq)
.WillOnce(Return(mFakeToken));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
.InSequence(seq);
EXPECT_CALL(*mMockDispatch,
schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) // mFakeNow))
.InSequence(seq);
EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
}
TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) {
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
.InSequence(seq)
.WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
.InSequence(seq);
EXPECT_CALL(*mMockDispatch,
schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
.Times(2)
.InSequence(seq);
EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
ASSERT_TRUE(innerCb);
innerCb(mFakeVSyncTime, mFakeWakeupTime);
innerCb(mFakeVSyncTime, mFakeWakeupTime);
}
TEST_F(VSyncReactorTest, callbackTimestampDistributedIsWakeupTime) {
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, _))
.InSequence(seq)
.WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
.InSequence(seq);
EXPECT_CALL(*mMockDispatch,
schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
.InSequence(seq);
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
ASSERT_TRUE(innerCb);
innerCb(mFakeVSyncTime, mFakeWakeupTime);
EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeWakeupTime));
}
TEST_F(VSyncReactorTest, eventListenersRemovedOnDestruction) {
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
.InSequence(seq)
.WillOnce(Return(mFakeToken));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
.InSequence(seq);
EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
}
// b/149221293
TEST_F(VSyncReactorTest, selfRemovingEventListenerStopsCallbacks) {
class SelfRemovingCallback : public DispSync::Callback {
public:
SelfRemovingCallback(VSyncReactor& vsr) : mVsr(vsr) {}
void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final {
mVsr.removeEventListener(this, &when);
}
private:
VSyncReactor& mVsr;
} selfRemover(mReactor);
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
.InSequence(seq)
.WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
.InSequence(seq);
EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
mReactor.addEventListener(mName, mPhase, &selfRemover, lastCallbackTime);
innerCb(0, 0);
}
TEST_F(VSyncReactorTest, addEventListenerChangePeriod) {
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
.InSequence(seq)
.WillOnce(Return(mFakeToken));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
.InSequence(seq);
EXPECT_CALL(*mMockDispatch,
schedule(mFakeToken, computeWorkload(period, mAnotherPhase), mFakeNow))
.InSequence(seq);
EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
}
TEST_F(VSyncReactorTest, changingPeriodChangesOffsetsOnNextCb) {
static constexpr nsecs_t anotherPeriod = 23333;
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
.InSequence(seq)
.WillOnce(Return(mFakeToken));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
.InSequence(seq);
EXPECT_CALL(*mMockTracker, setPeriod(anotherPeriod));
EXPECT_CALL(*mMockDispatch,
schedule(mFakeToken, computeWorkload(anotherPeriod, mPhase), mFakeNow))
.InSequence(seq);
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
bool periodFlushed = false;
mReactor.setPeriod(anotherPeriod);
EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, std::nullopt, &periodFlushed));
EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, std::nullopt, &periodFlushed));
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
}
TEST_F(VSyncReactorTest, offsetsAppliedOnNextOpportunity) {
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
.InSequence(seq)
.WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), _))
.InSequence(seq)
.WillOnce(Return(ScheduleResult::Scheduled));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
.InSequence(seq)
.WillOnce(Return(ScheduleResult::Scheduled));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
.InSequence(seq)
.WillOnce(Return(ScheduleResult::Scheduled));
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
mReactor.changePhaseOffset(&outerCb, mAnotherPhase);
ASSERT_TRUE(innerCb);
innerCb(mFakeVSyncTime, mFakeWakeupTime);
}
TEST_F(VSyncReactorTest, negativeOffsetsApplied) {
nsecs_t const negativePhase = -4000;
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
.InSequence(seq)
.WillOnce(Return(mFakeToken));
EXPECT_CALL(*mMockDispatch,
schedule(mFakeToken, computeWorkload(period, negativePhase), mFakeNow))
.InSequence(seq);
mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime);
}
TEST_F(VSyncReactorTest, beginResyncResetsModel) {
EXPECT_CALL(*mMockTracker, resetModel());
mReactor.beginResync();
}
TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
bool periodFlushed = true;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3);
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
mReactor.setPeriod(newPeriod);
EXPECT_TRUE(mReactor.addResyncSample(0, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
EXPECT_TRUE(mReactor.addResyncSample(newPeriod, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
EXPECT_FALSE(mReactor.addResyncSample(newPeriod, newPeriod, &periodFlushed));
EXPECT_TRUE(periodFlushed);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
// Create a reactor which supports the kernel idle timer
auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock),
std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
std::make_unique<VSyncTrackerWrapper>(mMockTracker),
kPendingLimit, true /* supportKernelIdleTimer */);
bool periodFlushed = true;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(5);
idleReactor.setIgnorePresentFences(true);
// First, set the same period, which should only be confirmed when we receive two
// matching callbacks
idleReactor.setPeriod(10000);
EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
// Correct period but incorrect timestamp delta
EXPECT_TRUE(idleReactor.addResyncSample(0, 10000, &periodFlushed));
EXPECT_FALSE(periodFlushed);
// Correct period and correct timestamp delta
EXPECT_FALSE(idleReactor.addResyncSample(10000, 10000, &periodFlushed));
EXPECT_TRUE(periodFlushed);
// Then, set a new period, which should be confirmed as soon as we receive a callback
// reporting the new period
nsecs_t const newPeriod = 5000;
idleReactor.setPeriod(newPeriod);
// Incorrect timestamp delta and period
EXPECT_TRUE(idleReactor.addResyncSample(20000, 10000, &periodFlushed));
EXPECT_FALSE(periodFlushed);
// Incorrect timestamp delta but correct period
EXPECT_FALSE(idleReactor.addResyncSample(20000, 5000, &periodFlushed));
EXPECT_TRUE(periodFlushed);
EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
using VSyncReactorDeathTest = VSyncReactorTest;
TEST_F(VSyncReactorDeathTest, invalidRemoval) {
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
mReactor.removeEventListener(&outerCb, &lastCallbackTime);
EXPECT_DEATH(mReactor.removeEventListener(&outerCb, &lastCallbackTime), ".*");
}
TEST_F(VSyncReactorDeathTest, invalidChange) {
EXPECT_DEATH(mReactor.changePhaseOffset(&outerCb, mPhase), ".*");
// the current DispSync-interface usage pattern has evolved around an implementation quirk,
// which is a callback is assumed to always exist, and it is valid api usage to change the
// offset of an object that is in the removed state.
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
mReactor.removeEventListener(&outerCb, &lastCallbackTime);
mReactor.changePhaseOffset(&outerCb, mPhase);
}
TEST_F(VSyncReactorDeathTest, cannotScheduleOnRegistration) {
ON_CALL(*mMockDispatch, schedule(_, _, _))
.WillByDefault(Return(ScheduleResult::CannotSchedule));
EXPECT_DEATH(mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime), ".*");
}
TEST_F(VSyncReactorDeathTest, cannotScheduleOnCallback) {
EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
.WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
EXPECT_CALL(*mMockDispatch, schedule(_, _, _)).WillOnce(Return(ScheduleResult::Scheduled));
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
ASSERT_TRUE(innerCb);
Mock::VerifyAndClearExpectations(mMockDispatch.get());
ON_CALL(*mMockDispatch, schedule(_, _, _))
.WillByDefault(Return(ScheduleResult::CannotSchedule));
EXPECT_DEATH(innerCb(mFakeVSyncTime, mFakeWakeupTime), ".*");
}
} // namespace android::scheduler