Loading services/surfaceflinger/DisplayHardware/HWComposer.h +4 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ class GraphicBuffer; class NativeHandle; class Region; class String8; class TestableSurfaceFlinger; namespace Hwc2 { class Composer; Loading Loading @@ -170,6 +171,9 @@ public: std::optional<hwc2_display_t> getHwcDisplayId(int32_t displayId) const; private: // For unit tests friend TestableSurfaceFlinger; static const int32_t VIRTUAL_DISPLAY_ID_BASE = 2; bool isValidDisplay(int32_t displayId) const; Loading services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +115 −6 Original line number Diff line number Diff line Loading @@ -22,11 +22,29 @@ #include <log/log.h> #include "MockComposer.h" #include "MockEventThread.h" #include "MockRenderEngine.h" #include "TestableSurfaceFlinger.h" namespace android { namespace { using testing::_; using testing::ByMove; using testing::DoAll; using testing::Mock; using testing::Return; using testing::SetArgPointee; using android::hardware::graphics::common::V1_0::Hdr; using android::Hwc2::Error; using android::Hwc2::IComposer; using android::Hwc2::IComposerClient; constexpr int32_t DEFAULT_REFRESH_RATE = 1666666666; constexpr int32_t DEFAULT_DPI = 320; class DisplayTransactionTest : public testing::Test { protected: DisplayTransactionTest(); Loading @@ -36,12 +54,24 @@ protected: void setupPrimaryDisplay(int width, int height); TestableSurfaceFlinger mFlinger; mock::EventThread* mEventThread = new mock::EventThread(); // These mocks are created by the test, but are destroyed by SurfaceFlinger // by virtue of being stored into a std::unique_ptr. However we still need // to keep a reference to them for use in setting up call expectations. RE::mock::RenderEngine* mRenderEngine = new RE::mock::RenderEngine(); Hwc2::mock::Composer* mComposer = new Hwc2::mock::Composer(); }; DisplayTransactionTest::DisplayTransactionTest() { 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()); mFlinger.mutableEventThread().reset(mEventThread); mFlinger.setupRenderEngine(std::unique_ptr<RE::RenderEngine>(mRenderEngine)); setupComposer(0); } DisplayTransactionTest::~DisplayTransactionTest() { Loading @@ -50,13 +80,92 @@ DisplayTransactionTest::~DisplayTransactionTest() { ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } TEST_F(DisplayTransactionTest, PlaceholderTrivialTest) { auto result = mFlinger.getDefaultDisplayDeviceLocked(); EXPECT_EQ(nullptr, result.get()); void DisplayTransactionTest::setupComposer(int virtualDisplayCount) { EXPECT_CALL(*mComposer, getCapabilities()) .WillOnce(Return(std::vector<IComposer::Capability>())); EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount)); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); Mock::VerifyAndClear(mComposer); } void DisplayTransactionTest::setupPrimaryDisplay(int width, int height) { EXPECT_CALL(*mComposer, getDisplayType(DisplayDevice::DISPLAY_PRIMARY, _)) .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), Return(Error::NONE))); EXPECT_CALL(*mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE)); EXPECT_CALL(*mComposer, getDisplayConfigs(_, _)) .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{0}), Return(Error::NONE))); EXPECT_CALL(*mComposer, getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0, IComposerClient::Attribute::WIDTH, _)) .WillOnce(DoAll(SetArgPointee<3>(width), Return(Error::NONE))); EXPECT_CALL(*mComposer, getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0, IComposerClient::Attribute::HEIGHT, _)) .WillOnce(DoAll(SetArgPointee<3>(height), Return(Error::NONE))); EXPECT_CALL(*mComposer, getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0, IComposerClient::Attribute::VSYNC_PERIOD, _)) .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE))); EXPECT_CALL(*mComposer, getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0, IComposerClient::Attribute::DPI_X, _)) .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE))); EXPECT_CALL(*mComposer, getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0, IComposerClient::Attribute::DPI_Y, _)) .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE))); mFlinger.setupPrimaryDisplay(); Mock::VerifyAndClear(mComposer); } TEST_F(DisplayTransactionTest, processDisplayChangesLockedProcessesPrimaryDisplayConnected) { using android::hardware::graphics::common::V1_0::ColorMode; setupPrimaryDisplay(1920, 1080); sp<BBinder> token = new BBinder(); mFlinger.mutableCurrentState().displays.add(token, {DisplayDevice::DISPLAY_PRIMARY, true}); EXPECT_CALL(*mComposer, getActiveConfig(DisplayDevice::DISPLAY_PRIMARY, _)) .WillOnce(DoAll(SetArgPointee<1>(0), Return(Error::NONE))); EXPECT_CALL(*mComposer, getColorModes(DisplayDevice::DISPLAY_PRIMARY, _)) .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::NATIVE})), Return(Error::NONE))); EXPECT_CALL(*mComposer, getHdrCapabilities(DisplayDevice::DISPLAY_PRIMARY, _, _, _, _)) .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE))); auto reSurface = new RE::mock::Surface(); EXPECT_CALL(*mRenderEngine, createSurface()) .WillOnce(Return(ByMove(std::unique_ptr<RE::Surface>(reSurface)))); EXPECT_CALL(*reSurface, setAsync(false)).Times(1); EXPECT_CALL(*reSurface, setCritical(true)).Times(1); EXPECT_CALL(*reSurface, setNativeWindow(_)).Times(1); EXPECT_CALL(*reSurface, queryWidth()).WillOnce(Return(1920)); EXPECT_CALL(*reSurface, queryHeight()).WillOnce(Return(1080)); EXPECT_CALL(*mEventThread, onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true)).Times(1); mFlinger.processDisplayChangesLocked(); ASSERT_TRUE(mFlinger.mutableDisplays().indexOfKey(token) >= 0); const auto& device = mFlinger.mutableDisplays().valueFor(token); ASSERT_TRUE(device.get()); EXPECT_TRUE(device->isSecure()); EXPECT_TRUE(device->isPrimary()); ssize_t i = mFlinger.mutableDrawingState().displays.indexOfKey(token); ASSERT_GE(0, i); const auto& draw = mFlinger.mutableDrawingState().displays[i]; EXPECT_EQ(DisplayDevice::DISPLAY_PRIMARY, draw.type); EXPECT_EQ(nullptr, mFlinger.mutableBuiltinDisplays()[0].get()); mFlinger.mutableBuiltinDisplays()[0] = new BBinder(); EXPECT_NE(nullptr, mFlinger.mutableBuiltinDisplays()[0].get()); EXPECT_CALL(*mComposer, setVsyncEnabled(0, IComposerClient::Vsync::DISABLE)) .WillOnce(Return(Error::NONE)); } } // namespace Loading services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +40 −2 Original line number Diff line number Diff line Loading @@ -21,16 +21,38 @@ namespace android { class EventThread; namespace RE { class RenderEngine; } namespace Hwc2 { class Composer; } class TestableSurfaceFlinger { public: // Extend this as needed for accessing SurfaceFlinger private (and public) // functions. void setupRenderEngine(std::unique_ptr<RE::RenderEngine> renderEngine) { mFlinger->getBE().mRenderEngine = std::move(renderEngine); } void setupComposer(std::unique_ptr<Hwc2::Composer> composer) { mFlinger->getBE().mHwc.reset(new HWComposer(std::move(composer))); } void setupPrimaryDisplay() { mFlinger->getBE().mHwc->mHwcDevice->onHotplug(0, HWC2::Connection::Connected); mFlinger->getBE().mHwc->onHotplug(0, DisplayDevice::DISPLAY_PRIMARY, HWC2::Connection::Connected); } /* ------------------------------------------------------------------------ * Forwarding for functions being tested */ auto getDefaultDisplayDeviceLocked() const { return mFlinger->getDefaultDisplayDeviceLocked(); } auto processDisplayChangesLocked() { return mFlinger->processDisplayChangesLocked(); } /* ------------------------------------------------------------------------ Loading @@ -38,6 +60,22 @@ public: * post-conditions. */ auto& mutableBuiltinDisplays() { return mFlinger->mBuiltinDisplays; } auto& mutableDisplays() { return mFlinger->mDisplays; } auto& mutableCurrentState() { return mFlinger->mCurrentState; } auto& mutableDrawingState() { return mFlinger->mDrawingState; } auto& mutableEventThread() { return mFlinger->mEventThread; } auto& mutableEventQueue() { return mFlinger->mEventQueue; } ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does // not report a leaked object, since the SurfaceFlinger instance may // still be referenced by something despite our best efforts to destroy // it after each test is done. mutableDisplays().clear(); mutableEventThread().reset(); mFlinger->getBE().mHwc.reset(); mFlinger->getBE().mRenderEngine.reset(); } sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(); }; Loading Loading
services/surfaceflinger/DisplayHardware/HWComposer.h +4 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ class GraphicBuffer; class NativeHandle; class Region; class String8; class TestableSurfaceFlinger; namespace Hwc2 { class Composer; Loading Loading @@ -170,6 +171,9 @@ public: std::optional<hwc2_display_t> getHwcDisplayId(int32_t displayId) const; private: // For unit tests friend TestableSurfaceFlinger; static const int32_t VIRTUAL_DISPLAY_ID_BASE = 2; bool isValidDisplay(int32_t displayId) const; Loading
services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +115 −6 Original line number Diff line number Diff line Loading @@ -22,11 +22,29 @@ #include <log/log.h> #include "MockComposer.h" #include "MockEventThread.h" #include "MockRenderEngine.h" #include "TestableSurfaceFlinger.h" namespace android { namespace { using testing::_; using testing::ByMove; using testing::DoAll; using testing::Mock; using testing::Return; using testing::SetArgPointee; using android::hardware::graphics::common::V1_0::Hdr; using android::Hwc2::Error; using android::Hwc2::IComposer; using android::Hwc2::IComposerClient; constexpr int32_t DEFAULT_REFRESH_RATE = 1666666666; constexpr int32_t DEFAULT_DPI = 320; class DisplayTransactionTest : public testing::Test { protected: DisplayTransactionTest(); Loading @@ -36,12 +54,24 @@ protected: void setupPrimaryDisplay(int width, int height); TestableSurfaceFlinger mFlinger; mock::EventThread* mEventThread = new mock::EventThread(); // These mocks are created by the test, but are destroyed by SurfaceFlinger // by virtue of being stored into a std::unique_ptr. However we still need // to keep a reference to them for use in setting up call expectations. RE::mock::RenderEngine* mRenderEngine = new RE::mock::RenderEngine(); Hwc2::mock::Composer* mComposer = new Hwc2::mock::Composer(); }; DisplayTransactionTest::DisplayTransactionTest() { 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()); mFlinger.mutableEventThread().reset(mEventThread); mFlinger.setupRenderEngine(std::unique_ptr<RE::RenderEngine>(mRenderEngine)); setupComposer(0); } DisplayTransactionTest::~DisplayTransactionTest() { Loading @@ -50,13 +80,92 @@ DisplayTransactionTest::~DisplayTransactionTest() { ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } TEST_F(DisplayTransactionTest, PlaceholderTrivialTest) { auto result = mFlinger.getDefaultDisplayDeviceLocked(); EXPECT_EQ(nullptr, result.get()); void DisplayTransactionTest::setupComposer(int virtualDisplayCount) { EXPECT_CALL(*mComposer, getCapabilities()) .WillOnce(Return(std::vector<IComposer::Capability>())); EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount)); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); Mock::VerifyAndClear(mComposer); } void DisplayTransactionTest::setupPrimaryDisplay(int width, int height) { EXPECT_CALL(*mComposer, getDisplayType(DisplayDevice::DISPLAY_PRIMARY, _)) .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), Return(Error::NONE))); EXPECT_CALL(*mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE)); EXPECT_CALL(*mComposer, getDisplayConfigs(_, _)) .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{0}), Return(Error::NONE))); EXPECT_CALL(*mComposer, getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0, IComposerClient::Attribute::WIDTH, _)) .WillOnce(DoAll(SetArgPointee<3>(width), Return(Error::NONE))); EXPECT_CALL(*mComposer, getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0, IComposerClient::Attribute::HEIGHT, _)) .WillOnce(DoAll(SetArgPointee<3>(height), Return(Error::NONE))); EXPECT_CALL(*mComposer, getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0, IComposerClient::Attribute::VSYNC_PERIOD, _)) .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE))); EXPECT_CALL(*mComposer, getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0, IComposerClient::Attribute::DPI_X, _)) .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE))); EXPECT_CALL(*mComposer, getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0, IComposerClient::Attribute::DPI_Y, _)) .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE))); mFlinger.setupPrimaryDisplay(); Mock::VerifyAndClear(mComposer); } TEST_F(DisplayTransactionTest, processDisplayChangesLockedProcessesPrimaryDisplayConnected) { using android::hardware::graphics::common::V1_0::ColorMode; setupPrimaryDisplay(1920, 1080); sp<BBinder> token = new BBinder(); mFlinger.mutableCurrentState().displays.add(token, {DisplayDevice::DISPLAY_PRIMARY, true}); EXPECT_CALL(*mComposer, getActiveConfig(DisplayDevice::DISPLAY_PRIMARY, _)) .WillOnce(DoAll(SetArgPointee<1>(0), Return(Error::NONE))); EXPECT_CALL(*mComposer, getColorModes(DisplayDevice::DISPLAY_PRIMARY, _)) .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::NATIVE})), Return(Error::NONE))); EXPECT_CALL(*mComposer, getHdrCapabilities(DisplayDevice::DISPLAY_PRIMARY, _, _, _, _)) .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE))); auto reSurface = new RE::mock::Surface(); EXPECT_CALL(*mRenderEngine, createSurface()) .WillOnce(Return(ByMove(std::unique_ptr<RE::Surface>(reSurface)))); EXPECT_CALL(*reSurface, setAsync(false)).Times(1); EXPECT_CALL(*reSurface, setCritical(true)).Times(1); EXPECT_CALL(*reSurface, setNativeWindow(_)).Times(1); EXPECT_CALL(*reSurface, queryWidth()).WillOnce(Return(1920)); EXPECT_CALL(*reSurface, queryHeight()).WillOnce(Return(1080)); EXPECT_CALL(*mEventThread, onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true)).Times(1); mFlinger.processDisplayChangesLocked(); ASSERT_TRUE(mFlinger.mutableDisplays().indexOfKey(token) >= 0); const auto& device = mFlinger.mutableDisplays().valueFor(token); ASSERT_TRUE(device.get()); EXPECT_TRUE(device->isSecure()); EXPECT_TRUE(device->isPrimary()); ssize_t i = mFlinger.mutableDrawingState().displays.indexOfKey(token); ASSERT_GE(0, i); const auto& draw = mFlinger.mutableDrawingState().displays[i]; EXPECT_EQ(DisplayDevice::DISPLAY_PRIMARY, draw.type); EXPECT_EQ(nullptr, mFlinger.mutableBuiltinDisplays()[0].get()); mFlinger.mutableBuiltinDisplays()[0] = new BBinder(); EXPECT_NE(nullptr, mFlinger.mutableBuiltinDisplays()[0].get()); EXPECT_CALL(*mComposer, setVsyncEnabled(0, IComposerClient::Vsync::DISABLE)) .WillOnce(Return(Error::NONE)); } } // namespace Loading
services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +40 −2 Original line number Diff line number Diff line Loading @@ -21,16 +21,38 @@ namespace android { class EventThread; namespace RE { class RenderEngine; } namespace Hwc2 { class Composer; } class TestableSurfaceFlinger { public: // Extend this as needed for accessing SurfaceFlinger private (and public) // functions. void setupRenderEngine(std::unique_ptr<RE::RenderEngine> renderEngine) { mFlinger->getBE().mRenderEngine = std::move(renderEngine); } void setupComposer(std::unique_ptr<Hwc2::Composer> composer) { mFlinger->getBE().mHwc.reset(new HWComposer(std::move(composer))); } void setupPrimaryDisplay() { mFlinger->getBE().mHwc->mHwcDevice->onHotplug(0, HWC2::Connection::Connected); mFlinger->getBE().mHwc->onHotplug(0, DisplayDevice::DISPLAY_PRIMARY, HWC2::Connection::Connected); } /* ------------------------------------------------------------------------ * Forwarding for functions being tested */ auto getDefaultDisplayDeviceLocked() const { return mFlinger->getDefaultDisplayDeviceLocked(); } auto processDisplayChangesLocked() { return mFlinger->processDisplayChangesLocked(); } /* ------------------------------------------------------------------------ Loading @@ -38,6 +60,22 @@ public: * post-conditions. */ auto& mutableBuiltinDisplays() { return mFlinger->mBuiltinDisplays; } auto& mutableDisplays() { return mFlinger->mDisplays; } auto& mutableCurrentState() { return mFlinger->mCurrentState; } auto& mutableDrawingState() { return mFlinger->mDrawingState; } auto& mutableEventThread() { return mFlinger->mEventThread; } auto& mutableEventQueue() { return mFlinger->mEventQueue; } ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does // not report a leaked object, since the SurfaceFlinger instance may // still be referenced by something despite our best efforts to destroy // it after each test is done. mutableDisplays().clear(); mutableEventThread().reset(); mFlinger->getBE().mHwc.reset(); mFlinger->getBE().mRenderEngine.reset(); } sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(); }; Loading