|  | #undef LOG_TAG | 
|  | #define LOG_TAG "SchedulerUnittests" | 
|  |  | 
|  | #include <gmock/gmock.h> | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include <log/log.h> | 
|  |  | 
|  | #include "AsyncCallRecorder.h" | 
|  | #include "Scheduler/DispSync.h" | 
|  | #include "Scheduler/EventThread.h" | 
|  | #include "Scheduler/Scheduler.h" | 
|  | #include "mock/MockDispSync.h" | 
|  | #include "mock/MockEventThread.h" | 
|  |  | 
|  | using testing::_; | 
|  | using testing::Return; | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | class SchedulerTest : public testing::Test { | 
|  | protected: | 
|  | class MockEventThreadConnection : public BnDisplayEventConnection { | 
|  | public: | 
|  | MockEventThreadConnection() = default; | 
|  | ~MockEventThreadConnection() = default; | 
|  |  | 
|  | MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel)); | 
|  | MOCK_METHOD1(setVsyncRate, status_t(uint32_t count)); | 
|  | MOCK_METHOD0(requestNextVsync, void()); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * This mock Scheduler class uses implementation of mock::EventThread but keeps everything else | 
|  | * the same. | 
|  | */ | 
|  | class MockScheduler : public android::Scheduler { | 
|  | public: | 
|  | MockScheduler(std::unique_ptr<EventThread> eventThread) | 
|  | : mEventThread(std::move(eventThread)) {} | 
|  |  | 
|  | std::unique_ptr<EventThread> makeEventThread( | 
|  | const std::string& /* connectionName */, DispSync* /* dispSync */, | 
|  | nsecs_t /* phaseOffsetNs */, | 
|  | impl::EventThread::ResyncWithRateLimitCallback /* resyncCallback */, | 
|  | impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override { | 
|  | return std::move(mEventThread); | 
|  | } | 
|  |  | 
|  | MockScheduler() = default; | 
|  | ~MockScheduler() override = default; | 
|  |  | 
|  | std::unique_ptr<EventThread> mEventThread; | 
|  | }; | 
|  |  | 
|  | SchedulerTest(); | 
|  | ~SchedulerTest() override; | 
|  |  | 
|  | sp<Scheduler::ConnectionHandle> mConnectionHandle; | 
|  | mock::DispSync* mPrimaryDispSync = new mock::DispSync(); | 
|  | mock::EventThread* mEventThread; | 
|  | std::unique_ptr<MockScheduler> mScheduler; | 
|  | sp<MockEventThreadConnection> mEventThreadConnection; | 
|  |  | 
|  | AsyncCallRecorder<void (*)()> mResyncCallRecorder; | 
|  | AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder; | 
|  | }; | 
|  |  | 
|  | SchedulerTest::SchedulerTest() { | 
|  | 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()); | 
|  |  | 
|  | std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>(); | 
|  | mEventThread = eventThread.get(); | 
|  | mScheduler = std::make_unique<MockScheduler>(std::move(eventThread)); | 
|  | mEventThreadConnection = new MockEventThreadConnection(); | 
|  |  | 
|  | // createConnection call to scheduler makes a createEventConnection call to EventThread. Make | 
|  | // sure that call gets executed and returns an EventThread::Connection object. | 
|  | EXPECT_CALL(*mEventThread, createEventConnection()) | 
|  | .WillRepeatedly(Return(mEventThreadConnection)); | 
|  |  | 
|  | mConnectionHandle = mScheduler->createConnection("appConnection", mPrimaryDispSync, 16, | 
|  | mResyncCallRecorder.getInvocable(), | 
|  | mInterceptVSyncCallRecorder.getInvocable()); | 
|  | } | 
|  |  | 
|  | SchedulerTest::~SchedulerTest() { | 
|  | 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()); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | /* ------------------------------------------------------------------------ | 
|  | * Test cases | 
|  | */ | 
|  | TEST_F(SchedulerTest, canCreateAndDestroyTest) { | 
|  | EXPECT_FALSE(mResyncCallRecorder.waitForCall().has_value()); | 
|  | EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall().has_value()); | 
|  | EXPECT_EQ(0, mConnectionHandle->id); | 
|  | } | 
|  |  | 
|  | TEST_F(SchedulerTest, testNullPtr) { | 
|  | // Passing a null pointer for ConnectionHandle is a valid argument. The code doesn't throw any | 
|  | // exceptions, just gracefully continues. | 
|  | sp<IDisplayEventConnection> returnedValue; | 
|  | ASSERT_NO_FATAL_FAILURE(returnedValue = mScheduler->createDisplayEventConnection(nullptr)); | 
|  | EXPECT_TRUE(returnedValue == nullptr); | 
|  | EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr); | 
|  | EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr); | 
|  | ASSERT_NO_FATAL_FAILURE( | 
|  | mScheduler->hotplugReceived(nullptr, EventThread::DisplayType::Primary, false)); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(nullptr)); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(nullptr)); | 
|  | String8 testString; | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->dump(nullptr, testString)); | 
|  | EXPECT_TRUE(testString == ""); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(nullptr, 10)); | 
|  | } | 
|  |  | 
|  | TEST_F(SchedulerTest, invalidConnectionHandle) { | 
|  | // Passing an invalid ConnectionHandle is a valid argument. The code doesn't throw any | 
|  | // exceptions, just gracefully continues. | 
|  | sp<Scheduler::ConnectionHandle> connectionHandle = new Scheduler::ConnectionHandle(20); | 
|  |  | 
|  | sp<IDisplayEventConnection> returnedValue; | 
|  | ASSERT_NO_FATAL_FAILURE(returnedValue = | 
|  | mScheduler->createDisplayEventConnection(connectionHandle)); | 
|  | EXPECT_TRUE(returnedValue == nullptr); | 
|  | EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr); | 
|  | EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr); | 
|  |  | 
|  | // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads. | 
|  | EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(connectionHandle, | 
|  | EventThread::DisplayType::Primary, false)); | 
|  |  | 
|  | EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(connectionHandle)); | 
|  |  | 
|  | EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(connectionHandle)); | 
|  |  | 
|  | String8 testString; | 
|  | EXPECT_CALL(*mEventThread, dump(_)).Times(0); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->dump(connectionHandle, testString)); | 
|  | EXPECT_TRUE(testString == ""); | 
|  |  | 
|  | EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(connectionHandle, 10)); | 
|  | } | 
|  |  | 
|  | TEST_F(SchedulerTest, validConnectionHandle) { | 
|  | sp<IDisplayEventConnection> returnedValue; | 
|  | ASSERT_NO_FATAL_FAILURE(returnedValue = | 
|  | mScheduler->createDisplayEventConnection(mConnectionHandle)); | 
|  | EXPECT_TRUE(returnedValue != nullptr); | 
|  | ASSERT_EQ(returnedValue, mEventThreadConnection); | 
|  |  | 
|  | EXPECT_TRUE(mScheduler->getEventThread(mConnectionHandle) != nullptr); | 
|  | EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle) != nullptr); | 
|  |  | 
|  | EXPECT_CALL(*mEventThread, onHotplugReceived(EventThread::DisplayType::Primary, false)) | 
|  | .Times(1); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(mConnectionHandle, | 
|  | EventThread::DisplayType::Primary, false)); | 
|  |  | 
|  | EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle)); | 
|  |  | 
|  | EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle)); | 
|  |  | 
|  | String8 testString("dump"); | 
|  | EXPECT_CALL(*mEventThread, dump(testString)).Times(1); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, testString)); | 
|  | EXPECT_TRUE(testString != ""); | 
|  |  | 
|  | EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1); | 
|  | ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10)); | 
|  | } | 
|  |  | 
|  | } // namespace | 
|  | } // namespace android |