diff options
author | 2018-03-09 18:52:26 -0800 | |
---|---|---|
committer | 2018-05-14 15:21:31 -0700 | |
commit | 24b0a485fb58afd16a42de6378c0b743d7aca58a (patch) | |
tree | 5eafbf0cd56c76cb1f4753566f1f64e389acc6ad | |
parent | 7d4aa6c6ca5baad017a127287f1f2be33844705d (diff) |
SF: Test coverage for EventThread
Add a unit test to cover EventThread.cpp
Test: atest libsurfaceflinger_unittest
Bug: 74827900
Change-Id: If9479cd9deedff836068cb53e7da2cb64041aea1
-rw-r--r-- | services/surfaceflinger/EventThread.cpp | 18 | ||||
-rw-r--r-- | services/surfaceflinger/EventThread.h | 22 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 21 | ||||
-rw-r--r-- | services/surfaceflinger/tests/unittests/Android.bp | 1 | ||||
-rw-r--r-- | services/surfaceflinger/tests/unittests/AsyncCallRecorder.h | 165 | ||||
-rw-r--r-- | services/surfaceflinger/tests/unittests/EventThreadTest.cpp | 422 |
6 files changed, 627 insertions, 22 deletions
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp index bb9c0703ed..bc271c8ec5 100644 --- a/services/surfaceflinger/EventThread.cpp +++ b/services/surfaceflinger/EventThread.cpp @@ -26,14 +26,12 @@ #include <cutils/sched_policy.h> #include <gui/DisplayEventReceiver.h> -#include <gui/IDisplayEventConnection.h> #include <utils/Errors.h> #include <utils/String8.h> #include <utils/Trace.h> #include "EventThread.h" -#include "SurfaceFlinger.h" using namespace std::chrono_literals; @@ -47,9 +45,11 @@ EventThread::~EventThread() = default; namespace impl { -EventThread::EventThread(VSyncSource* src, SurfaceFlinger& flinger, bool interceptVSyncs, - const char* threadName) - : mVSyncSource(src), mFlinger(flinger), mInterceptVSyncs(interceptVSyncs) { +EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) + : mVSyncSource(src), + mResyncWithRateLimitCallback(resyncWithRateLimitCallback), + mInterceptVSyncsCallback(interceptVSyncsCallback) { for (auto& event : mVSyncEvent) { event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; event.header.id = 0; @@ -118,7 +118,9 @@ void EventThread::setVsyncRate(uint32_t count, const sp<EventThread::Connection> void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) { std::lock_guard<std::mutex> lock(mMutex); - mFlinger.resyncWithRateLimit(); + if (mResyncWithRateLimitCallback) { + mResyncWithRateLimitCallback(); + } if (connection->count < 0) { connection->count = 0; @@ -216,8 +218,8 @@ Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked( timestamp = mVSyncEvent[i].header.timestamp; if (timestamp) { // we have a vsync event to dispatch - if (mInterceptVSyncs) { - mFlinger.mInterceptor->saveVSyncEvent(timestamp); + if (mInterceptVSyncsCallback) { + mInterceptVSyncsCallback(timestamp); } *event = mVSyncEvent[i]; mVSyncEvent[i].header.timestamp = 0; diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h index 97f0a359e2..9c13ed2755 100644 --- a/services/surfaceflinger/EventThread.h +++ b/services/surfaceflinger/EventThread.h @@ -37,6 +37,7 @@ namespace android { // --------------------------------------------------------------------------- +class EventThreadTest; class SurfaceFlinger; class String8; @@ -82,7 +83,9 @@ class EventThread : public android::EventThread, private VSyncSource::Callback { class Connection : public BnDisplayEventConnection { public: explicit Connection(EventThread* eventThread); - status_t postEvent(const DisplayEventReceiver::Event& event); + virtual ~Connection(); + + virtual status_t postEvent(const DisplayEventReceiver::Event& event); // count >= 1 : continuous event. count is the vsync rate // count == 0 : one-shot event that has not fired @@ -90,7 +93,6 @@ class EventThread : public android::EventThread, private VSyncSource::Callback { int32_t count; private: - virtual ~Connection(); virtual void onFirstRef(); status_t stealReceiveChannel(gui::BitTube* outChannel) override; status_t setVsyncRate(uint32_t count) override; @@ -100,8 +102,11 @@ class EventThread : public android::EventThread, private VSyncSource::Callback { }; public: - EventThread(VSyncSource* src, SurfaceFlinger& flinger, bool interceptVSyncs, - const char* threadName); + using ResyncWithRateLimitCallback = std::function<void()>; + using InterceptVSyncsCallback = std::function<void(nsecs_t)>; + + EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName); ~EventThread(); sp<BnDisplayEventConnection> createEventConnection() const override; @@ -124,6 +129,8 @@ public: void setPhaseOffset(nsecs_t phaseOffset) override; private: + friend EventThreadTest; + void threadMain(); Vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* event) @@ -137,8 +144,9 @@ private: void onVSyncEvent(nsecs_t timestamp) override; // constants - VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr; - SurfaceFlinger& mFlinger; + VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr; + const ResyncWithRateLimitCallback mResyncWithRateLimitCallback; + const InterceptVSyncsCallback mInterceptVSyncsCallback; std::thread mThread; mutable std::mutex mMutex; @@ -155,8 +163,6 @@ private: // for debugging bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false; - - const bool mInterceptVSyncs = false; }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 588d24c45f..a837d01d80 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -636,14 +636,21 @@ void SurfaceFlinger::init() { mEventThreadSource = std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs, true, "app"); - mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(), *this, false, + mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(), + [this]() { resyncWithRateLimit(); }, + impl::EventThread::InterceptVSyncsCallback(), "appEventThread"); mSfEventThreadSource = std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf"); - mSFEventThread = std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), *this, true, - "sfEventThread"); + mSFEventThread = + std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), + [this]() { resyncWithRateLimit(); }, + [this](nsecs_t timestamp) { + mInterceptor->saveVSyncEvent(timestamp); + }, + "sfEventThread"); mEventQueue->setEventThread(mSFEventThread.get()); mVsyncModulator.setEventThread(mSFEventThread.get()); @@ -1132,9 +1139,11 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { ALOGV("VSync Injections enabled"); if (mVSyncInjector.get() == nullptr) { mVSyncInjector = std::make_unique<InjectVSyncSource>(); - mInjectorEventThread = - std::make_unique<impl::EventThread>(mVSyncInjector.get(), *this, false, - "injEventThread"); + mInjectorEventThread = std::make_unique< + impl::EventThread>(mVSyncInjector.get(), + [this]() { resyncWithRateLimit(); }, + impl::EventThread::InterceptVSyncsCallback(), + "injEventThread"); } mEventQueue->setEventThread(mInjectorEventThread.get()); } else { diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index bcabe0dffc..3e9f6a425c 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -20,6 +20,7 @@ cc_test { srcs: [ ":libsurfaceflinger_sources", "DisplayTransactionTest.cpp", + "EventThreadTest.cpp", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockDisplaySurface.cpp", "mock/gui/MockGraphicBufferConsumer.cpp", diff --git a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h new file mode 100644 index 0000000000..2245ee1a8a --- /dev/null +++ b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include <chrono> +#include <deque> +#include <mutex> +#include <optional> +#include <thread> +#include <tuple> +#include <type_traits> +#include <utility> + +#include <android-base/thread_annotations.h> + +namespace android { + +// This class helps record calls made by another thread when they are made +// asynchronously, with no other way for the tests to verify that the calls have +// been made. +// +// A normal Google Mock recorder, while thread safe, does not allow you to wait +// for asynchronous calls to be made. +// +// Usage: +// +// In the test, use a Google Mock expectation to invoke an instance of the +// recorder: +// +// AsyncCallRecorder<void(int)> recorder; +// +// EXPECT_CALL(someMock, someFunction(_)). +// .WillRepeatedly(Invoke(recorder.getInvocable())); +// +// Then you can invoke the functionality being tested: +// +// threadUnderTest.doSomethingAsync() +// +// And afterwards make a number of assertions using the recorder: +// +// // Wait for one call (with reasonable default timeout), and get the args +// // as a std::tuple inside a std::optional. +// auto args = recorder.waitForCall(); +// // The returned std::optional will have a value if the recorder function +// // was called. +// ASSERT_TRUE(args.has_value()); +// // The arguments can be checked if needed using standard tuple +// // operations. +// EXPECT_EQ(123, std::get<0>(args.value())); +// +// Alternatively maybe you want to assert that a call was not made. +// +// EXPECT_FALSE(recorder.waitForUnexpectedCall().has_value()); +// +// However this check uses a really short timeout so as not to block the test +// unnecessarily. And it could be possible for the check to return false and +// then the recorder could observe a call being made after. +template <typename Func> +class AsyncCallRecorder; + +template <typename... Args> +class AsyncCallRecorder<void (*)(Args...)> { +public: + // For the tests, we expect the wait for an expected change to be signaled + // to be much shorter than this. + static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{10}; + + // The wait here is tricky. We don't expect a change, but we don't want to + // wait forever (or for longer than the typical test function runtime). As + // even the simplest Google Test can take 1ms (1000us) to run, we wait for + // half that time. + static constexpr std::chrono::microseconds UNEXPECTED_CALL_TIMEOUT{500}; + + using ArgTuple = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>; + + void recordCall(Args... args) { + std::lock_guard<std::mutex> lock(mMutex); + mCalls.emplace_back(std::make_tuple(args...)); + mCondition.notify_all(); + } + + // Returns a functor which can be used with the Google Mock Invoke() + // function, or as a std::function to record calls. + auto getInvocable() { + return [this](Args... args) { recordCall(args...); }; + } + + // Returns a set of arguments as a std::optional<std::tuple<...>> for the + // oldest call, waiting for the given timeout if necessary if there are no + // arguments in the FIFO. + std::optional<ArgTuple> waitForCall( + std::chrono::microseconds timeout = DEFAULT_CALL_EXPECTED_TIMEOUT) + NO_THREAD_SAFETY_ANALYSIS { + std::unique_lock<std::mutex> lock(mMutex); + + // Wait if necessary for us to have a record from a call. + mCondition.wait_for(lock, timeout, + [this]() NO_THREAD_SAFETY_ANALYSIS { return !mCalls.empty(); }); + + // Return the arguments from the oldest call, if one was made + bool called = !mCalls.empty(); + std::optional<ArgTuple> result; + if (called) { + result.emplace(std::move(mCalls.front())); + mCalls.pop_front(); + } + return result; + } + + // Waits using a small default timeout for when a call is not expected to be + // made. The returned std::optional<std:tuple<...>> should not have a value + // except if a set of arguments was unexpectedly received because a call was + // actually made. + // + // Note this function uses a small timeout to not block test execution, and + // it is possible the code under test could make the call AFTER the timeout + // expires. + std::optional<ArgTuple> waitForUnexpectedCall() { return waitForCall(UNEXPECTED_CALL_TIMEOUT); } + +private: + std::mutex mMutex; + std::condition_variable mCondition; + std::deque<ArgTuple> mCalls GUARDED_BY(mMutex); +}; + +// Like AsyncCallRecorder, but for when the function being invoked +// asynchronously is expected to return a value. +// +// This helper allows a single constant return value to be set to be returned by +// all calls that were made. +template <typename Func> +class AsyncCallRecorderWithCannedReturn; + +template <typename Ret, typename... Args> +class AsyncCallRecorderWithCannedReturn<Ret (*)(Args...)> + : public AsyncCallRecorder<void (*)(Args...)> { +public: + explicit AsyncCallRecorderWithCannedReturn(Ret returnvalue) : mReturnValue(returnvalue) {} + + auto getInvocable() { + return [this](Args... args) { + this->recordCall(args...); + return mReturnValue; + }; + } + +private: + const Ret mReturnValue; +}; + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp new file mode 100644 index 0000000000..80fdb80264 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2018 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" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <log/log.h> + +#include <utils/Errors.h> + +#include "AsyncCallRecorder.h" +#include "EventThread.h" + +using namespace std::chrono_literals; +using namespace std::placeholders; + +using testing::_; +using testing::Invoke; + +namespace android { +namespace { + +class MockVSyncSource : public VSyncSource { +public: + MOCK_METHOD1(setVSyncEnabled, void(bool)); + MOCK_METHOD1(setCallback, void(VSyncSource::Callback*)); + MOCK_METHOD1(setPhaseOffset, void(nsecs_t)); +}; + +} // namespace + +class EventThreadTest : public testing::Test { +protected: + class MockEventThreadConnection : public android::impl::EventThread::Connection { + public: + explicit MockEventThreadConnection(android::impl::EventThread* eventThread) + : android::impl::EventThread::Connection(eventThread) {} + MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event)); + }; + + using ConnectionEventRecorder = + AsyncCallRecorderWithCannedReturn<status_t (*)(const DisplayEventReceiver::Event&)>; + + EventThreadTest(); + ~EventThreadTest() override; + + void createThread(); + sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder); + + void expectVSyncSetEnabledCallReceived(bool expectedState); + void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset); + VSyncSource::Callback* expectVSyncSetCallbackCallReceived(); + void expectInterceptCallReceived(nsecs_t expectedTimestamp); + void expectVsyncEventReceivedByConnection(const char* name, + ConnectionEventRecorder& connectionEventRecorder, + nsecs_t expectedTimestamp, unsigned expectedCount); + void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount); + void expectHotplugEventReceivedByConnection(int expectedDisplayType, bool expectedConnected); + + AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder; + AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder; + AsyncCallRecorder<void (*)()> mResyncCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder; + ConnectionEventRecorder mConnectionEventCallRecorder{0}; + + MockVSyncSource mVSyncSource; + std::unique_ptr<android::impl::EventThread> mThread; + sp<MockEventThreadConnection> mConnection; +}; + +EventThreadTest::EventThreadTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + + EXPECT_CALL(mVSyncSource, setVSyncEnabled(_)) + .WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable())); + + EXPECT_CALL(mVSyncSource, setCallback(_)) + .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable())); + + EXPECT_CALL(mVSyncSource, setPhaseOffset(_)) + .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable())); + + createThread(); + mConnection = createConnection(mConnectionEventCallRecorder); +} + +EventThreadTest::~EventThreadTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +void EventThreadTest::createThread() { + mThread = + std::make_unique<android::impl::EventThread>(&mVSyncSource, + mResyncCallRecorder.getInvocable(), + mInterceptVSyncCallRecorder.getInvocable(), + "unit-test-event-thread"); +} + +sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection( + ConnectionEventRecorder& recorder) { + sp<MockEventThreadConnection> connection = new MockEventThreadConnection(mThread.get()); + EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable())); + return connection; +} + +void EventThreadTest::expectVSyncSetEnabledCallReceived(bool expectedState) { + auto args = mVSyncSetEnabledCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedState, std::get<0>(args.value())); +} + +void EventThreadTest::expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset) { + auto args = mVSyncSetPhaseOffsetCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedPhaseOffset, std::get<0>(args.value())); +} + +VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() { + auto callbackSet = mVSyncSetCallbackCallRecorder.waitForCall(); + return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr; +} + +void EventThreadTest::expectInterceptCallReceived(nsecs_t expectedTimestamp) { + auto args = mInterceptVSyncCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedTimestamp, std::get<0>(args.value())); +} + +void EventThreadTest::expectVsyncEventReceivedByConnection( + const char* name, ConnectionEventRecorder& connectionEventRecorder, + nsecs_t expectedTimestamp, unsigned expectedCount) { + auto args = connectionEventRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()) << name << " did not receive an event for timestamp " + << expectedTimestamp; + const auto& event = std::get<0>(args.value()); + EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_VSYNC, event.header.type) + << name << " did not get the correct event for timestamp " << expectedTimestamp; + EXPECT_EQ(expectedTimestamp, event.header.timestamp) + << name << " did not get the expected timestamp for timestamp " << expectedTimestamp; + EXPECT_EQ(expectedCount, event.vsync.count) + << name << " did not get the expected count for timestamp " << expectedTimestamp; +} + +void EventThreadTest::expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, + unsigned expectedCount) { + expectVsyncEventReceivedByConnection("mConnectionEventCallRecorder", + mConnectionEventCallRecorder, expectedTimestamp, + expectedCount); +} + +void EventThreadTest::expectHotplugEventReceivedByConnection(int expectedDisplayType, + bool expectedConnected) { + auto args = mConnectionEventCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + const auto& event = std::get<0>(args.value()); + EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, event.header.type); + EXPECT_EQ(static_cast<unsigned>(expectedDisplayType), event.header.id); + EXPECT_EQ(expectedConnected, event.hotplug.connected); +} + +namespace { + +/* ------------------------------------------------------------------------ + * Test cases + */ + +TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) { + EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); + EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mVSyncSetPhaseOffsetCallRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value()); +} + +TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) { + // Signal that we want the next vsync event to be posted to the connection + mThread->requestNextVsync(mConnection); + + // EventThread should immediately request a resync. + EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value()); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // Use the received callback to signal a first vsync event. + // The interceptor should receive the event, as well as the connection. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + expectVsyncEventReceivedByConnection(123, 1u); + + // Use the received callback to signal a second vsync event. + // The interceptor should receive the event, but the the connection should + // not as it was only interested in the first. + callback->onVSyncEvent(456); + expectInterceptCallReceived(456); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + + // EventThread should also detect that at this point that it does not need + // any more vsync events, and should disable their generation. + expectVSyncSetEnabledCallReceived(false); +} + +TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) { + // Create a first connection, register it, and request a vsync rate of zero. + ConnectionEventRecorder firstConnectionEventRecorder{0}; + sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder); + mThread->setVsyncRate(0, firstConnection); + + // By itself, this should not enable vsync events + EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); + EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value()); + + // However if there is another connection which wants events at a nonzero rate..... + ConnectionEventRecorder secondConnectionEventRecorder{0}; + sp<MockEventThreadConnection> secondConnection = + createConnection(secondConnectionEventRecorder); + mThread->setVsyncRate(1, secondConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // Send a vsync event. EventThread should then make a call to the + // interceptor, and the second connection. The first connection should not + // get the event. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value()); + expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123, + 1u); +} + +TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) { + mThread->setVsyncRate(1, mConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // Send a vsync event. EventThread should then make a call to the + // interceptor, and the connection. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + expectVsyncEventReceivedByConnection(123, 1u); + + // A second event should go to the same places. + callback->onVSyncEvent(456); + expectInterceptCallReceived(456); + expectVsyncEventReceivedByConnection(456, 2u); + + // A third event should go to the same places. + callback->onVSyncEvent(789); + expectInterceptCallReceived(789); + expectVsyncEventReceivedByConnection(789, 3u); +} + +TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) { + mThread->setVsyncRate(2, mConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // The first event will be seen by the interceptor, and not the connection. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + + // The second event will be seen by the interceptor and the connection. + callback->onVSyncEvent(456); + expectInterceptCallReceived(456); + expectVsyncEventReceivedByConnection(456, 2u); + + // The third event will be seen by the interceptor, and not the connection. + callback->onVSyncEvent(789); + expectInterceptCallReceived(789); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + + // The fourth event will be seen by the interceptor and the connection. + callback->onVSyncEvent(101112); + expectInterceptCallReceived(101112); + expectVsyncEventReceivedByConnection(101112, 4u); +} + +TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) { + mThread->setVsyncRate(1, mConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // Destroy the only (strong) reference to the connection. + mConnection = nullptr; + + // The first event will be seen by the interceptor, and not the connection. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + + // EventThread should disable vsync callbacks + expectVSyncSetEnabledCallReceived(false); +} + +TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) { + ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY}; + sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder); + mThread->setVsyncRate(1, errorConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // The first event will be seen by the interceptor, and by the connection, + // which then returns an error. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); + + // A subsequent event will be seen by the interceptor and not by the + // connection. + callback->onVSyncEvent(456); + expectInterceptCallReceived(456); + EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value()); + + // EventThread should disable vsync callbacks with the second event + expectVSyncSetEnabledCallReceived(false); +} + +TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) { + ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK}; + sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder); + mThread->setVsyncRate(1, errorConnection); + + // EventThread should enable vsync callbacks, and set a callback interface + // pointer to use them with the VSync source. + expectVSyncSetEnabledCallReceived(true); + auto callback = expectVSyncSetCallbackCallReceived(); + ASSERT_TRUE(callback); + + // The first event will be seen by the interceptor, and by the connection, + // which then returns an non-fatal error. + callback->onVSyncEvent(123); + expectInterceptCallReceived(123); + expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); + + // A subsequent event will be seen by the interceptor, and by the connection, + // which still then returns an non-fatal error. + callback->onVSyncEvent(456); + expectInterceptCallReceived(456); + expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u); + + // EventThread will not disable vsync callbacks as the errors are non-fatal. + EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); +} + +TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) { + mThread->setPhaseOffset(321); + expectVSyncSetPhaseOffsetCallReceived(321); +} + +TEST_F(EventThreadTest, postHotplugPrimaryDisconnect) { + mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, false); + expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, false); +} + +TEST_F(EventThreadTest, postHotplugPrimaryConnect) { + mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true); + expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, true); +} + +TEST_F(EventThreadTest, postHotplugExternalDisconnect) { + mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, false); + expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, false); +} + +TEST_F(EventThreadTest, postHotplugExternalConnect) { + mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, true); + expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, true); +} + +TEST_F(EventThreadTest, postHotplugVirtualDisconnectIsFilteredOut) { + mThread->onHotplugReceived(DisplayDevice::DISPLAY_VIRTUAL, false); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); +} + +} // namespace +} // namespace android |