/*
 * Copyright 2020 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 "DisplayTransactionTestHelpers.h"

namespace android {
namespace {

struct DisplayTransactionCommitTest : DisplayTransactionTest {
    template <typename Case>
    void setupCommonPreconditions();

    template <typename Case, bool connected>
    static void expectHotplugReceived(mock::EventThread*);

    template <typename Case>
    void setupCommonCallExpectationsForConnectProcessing();

    template <typename Case>
    void setupCommonCallExpectationsForDisconnectProcessing();

    template <typename Case>
    void processesHotplugConnectCommon();

    template <typename Case>
    void ignoresHotplugConnectCommon();

    template <typename Case>
    void processesHotplugDisconnectCommon();

    template <typename Case>
    void verifyDisplayIsConnected(const sp<IBinder>& displayToken);

    template <typename Case>
    void verifyPhysicalDisplayIsConnected();

    void verifyDisplayIsNotConnected(const sp<IBinder>& displayToken);
};

template <typename Case>
void DisplayTransactionCommitTest::setupCommonPreconditions() {
    // Wide color displays support is configured appropriately
    Case::WideColorSupport::injectConfigChange(this);

    // SurfaceFlinger will use a test-controlled factory for BufferQueues
    injectFakeBufferQueueFactory();

    // SurfaceFlinger will use a test-controlled factory for native window
    // surfaces.
    injectFakeNativeWindowSurfaceFactory();
}

template <typename Case, bool connected>
void DisplayTransactionCommitTest::expectHotplugReceived(mock::EventThread* eventThread) {
    const auto convert = [](auto physicalDisplayId) {
        return std::make_optional(DisplayId{physicalDisplayId});
    };

    EXPECT_CALL(*eventThread,
                onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
            .Times(1);
}

template <typename Case>
void DisplayTransactionCommitTest::setupCommonCallExpectationsForConnectProcessing() {
    Case::Display::setupHwcHotplugCallExpectations(this);

    Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
    Case::Display::setupFramebufferProducerBufferQueueCallExpectations(this);
    Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
    Case::Display::setupHwcGetActiveConfigCallExpectations(this);

    Case::WideColorSupport::setupComposerCallExpectations(this);
    Case::HdrSupport::setupComposerCallExpectations(this);
    Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);

    expectHotplugReceived<Case, true>(mEventThread);
    expectHotplugReceived<Case, true>(mSFEventThread);
}

template <typename Case>
void DisplayTransactionCommitTest::setupCommonCallExpectationsForDisconnectProcessing() {
    expectHotplugReceived<Case, false>(mEventThread);
    expectHotplugReceived<Case, false>(mSFEventThread);
}

template <typename Case>
void DisplayTransactionCommitTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
    // The display device should have been set up in the list of displays.
    ASSERT_TRUE(hasDisplayDevice(displayToken));
    const auto& display = getDisplayDevice(displayToken);

    EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), display.isSecure());
    EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), display.isPrimary());

    std::optional<DisplayDeviceState::Physical> expectedPhysical;
    if (Case::Display::CONNECTION_TYPE::value) {
        const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
        ASSERT_TRUE(displayId);
        const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
        ASSERT_TRUE(hwcDisplayId);
        expectedPhysical = {.id = *displayId, .hwcDisplayId = *hwcDisplayId};
    }

    // The display should have been set up in the current display state
    ASSERT_TRUE(hasCurrentDisplayState(displayToken));
    const auto& current = getCurrentDisplayState(displayToken);
    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
    EXPECT_EQ(expectedPhysical, current.physical);

    // The display should have been set up in the drawing display state
    ASSERT_TRUE(hasDrawingDisplayState(displayToken));
    const auto& draw = getDrawingDisplayState(displayToken);
    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
    EXPECT_EQ(expectedPhysical, draw.physical);
}

template <typename Case>
void DisplayTransactionCommitTest::verifyPhysicalDisplayIsConnected() {
    // HWComposer should have an entry for the display
    EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));

    // SF should have a display token.
    const auto displayId = Case::Display::DISPLAY_ID::get();
    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));

    const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
    ASSERT_TRUE(displayOpt);

    const auto& display = displayOpt->get();
    EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, display.snapshot().connectionType());

    verifyDisplayIsConnected<Case>(display.token());
}

void DisplayTransactionCommitTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
    EXPECT_FALSE(hasDisplayDevice(displayToken));
    EXPECT_FALSE(hasCurrentDisplayState(displayToken));
    EXPECT_FALSE(hasDrawingDisplayState(displayToken));
}

template <typename Case>
void DisplayTransactionCommitTest::processesHotplugConnectCommon() {
    // --------------------------------------------------------------------
    // Preconditions

    setupCommonPreconditions<Case>();

    // A hotplug connect event is enqueued for a display
    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);

    // --------------------------------------------------------------------
    // Call Expectations

    setupCommonCallExpectationsForConnectProcessing<Case>();

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.configureAndCommit();

    // --------------------------------------------------------------------
    // Postconditions

    verifyPhysicalDisplayIsConnected<Case>();

    // --------------------------------------------------------------------
    // Cleanup conditions

    EXPECT_CALL(*mComposer,
                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
            .WillOnce(Return(Error::NONE));
    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
}

template <typename Case>
void DisplayTransactionCommitTest::ignoresHotplugConnectCommon() {
    // --------------------------------------------------------------------
    // Preconditions

    setupCommonPreconditions<Case>();

    // A hotplug connect event is enqueued for a display
    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.configureAndCommit();

    // --------------------------------------------------------------------
    // Postconditions

    // HWComposer should not have an entry for the display
    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
}

template <typename Case>
void DisplayTransactionCommitTest::processesHotplugDisconnectCommon() {
    // --------------------------------------------------------------------
    // Preconditions

    setupCommonPreconditions<Case>();

    // A hotplug disconnect event is enqueued for a display
    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);

    // The display is already completely set up.
    Case::Display::injectHwcDisplay(this);
    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
    existing.inject();

    // --------------------------------------------------------------------
    // Call Expectations

    EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
            .Times(0);

    setupCommonCallExpectationsForDisconnectProcessing<Case>();

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.configureAndCommit();

    // --------------------------------------------------------------------
    // Postconditions

    // HWComposer should not have an entry for the display
    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));

    // SF should not have a PhysicalDisplay.
    const auto displayId = Case::Display::DISPLAY_ID::get();
    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
    ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));

    // The existing token should have been removed.
    verifyDisplayIsNotConnected(existing.token());
}

TEST_F(DisplayTransactionCommitTest, processesHotplugConnectPrimaryDisplay) {
    processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
}

TEST_F(DisplayTransactionCommitTest, processesHotplugConnectExternalDisplay) {
    // Inject a primary display.
    PrimaryDisplayVariant::injectHwcDisplay(this);

    processesHotplugConnectCommon<SimpleExternalDisplayCase>();
}

TEST_F(DisplayTransactionCommitTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
    // Inject both a primary and external display.
    PrimaryDisplayVariant::injectHwcDisplay(this);
    ExternalDisplayVariant::injectHwcDisplay(this);

    // TODO: This is an unnecessary call.
    EXPECT_CALL(*mComposer,
                getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
            .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
                            SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
                            Return(Error::NONE)));

    ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
}

TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectPrimaryDisplay) {
    EXPECT_EXIT(processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>(),
                testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
}

TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectExternalDisplay) {
    processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
}

TEST_F(DisplayTransactionCommitTest, processesHotplugConnectThenDisconnectPrimary) {
    EXPECT_EXIT(
            [this] {
                using Case = SimplePrimaryDisplayCase;

                // --------------------------------------------------------------------
                // Preconditions

                setupCommonPreconditions<Case>();

                // A hotplug connect event is enqueued for a display
                Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
                // A hotplug disconnect event is also enqueued for the same display
                Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);

                // --------------------------------------------------------------------
                // Call Expectations

                setupCommonCallExpectationsForConnectProcessing<Case>();
                setupCommonCallExpectationsForDisconnectProcessing<Case>();

                EXPECT_CALL(*mComposer,
                            setVsyncEnabled(Case::Display::HWC_DISPLAY_ID,
                                            IComposerClient::Vsync::DISABLE))
                        .WillOnce(Return(Error::NONE));
                EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));

                // --------------------------------------------------------------------
                // Invocation

                mFlinger.configureAndCommit();

                // --------------------------------------------------------------------
                // Postconditions

                // HWComposer should not have an entry for the display
                EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));

                // SF should not have a PhysicalDisplay.
                const auto displayId = Case::Display::DISPLAY_ID::get();
                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
                ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
            }(),
            testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
}

TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectThenConnectPrimary) {
    EXPECT_EXIT(
            [this] {
                using Case = SimplePrimaryDisplayCase;

                // --------------------------------------------------------------------
                // Preconditions

                setupCommonPreconditions<Case>();

                // The display is already completely set up.
                Case::Display::injectHwcDisplay(this);
                auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
                existing.inject();

                // A hotplug disconnect event is enqueued for a display
                Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
                // A hotplug connect event is also enqueued for the same display
                Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);

                // --------------------------------------------------------------------
                // Call Expectations

                setupCommonCallExpectationsForConnectProcessing<Case>();
                setupCommonCallExpectationsForDisconnectProcessing<Case>();

                // --------------------------------------------------------------------
                // Invocation

                mFlinger.configureAndCommit();

                // --------------------------------------------------------------------
                // Postconditions

                // The existing token should have been removed.
                verifyDisplayIsNotConnected(existing.token());
                const auto displayId = Case::Display::DISPLAY_ID::get();
                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));

                const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
                ASSERT_TRUE(displayOpt);
                EXPECT_NE(existing.token(), displayOpt->get().token());

                // A new display should be connected in its place.
                verifyPhysicalDisplayIsConnected<Case>();

                // --------------------------------------------------------------------
                // Cleanup conditions

                EXPECT_CALL(*mComposer,
                            setVsyncEnabled(Case::Display::HWC_DISPLAY_ID,
                                            IComposerClient::Vsync::DISABLE))
                        .WillOnce(Return(Error::NONE));
                EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
            }(),
            testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
}

TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAdded) {
    using Case = HwcVirtualDisplayCase;

    // --------------------------------------------------------------------
    // Preconditions

    // The HWC supports at least one virtual display
    injectMockComposer(1);

    setupCommonPreconditions<Case>();

    // A virtual display was added to the current state, and it has a
    // surface(producer)
    sp<BBinder> displayToken = sp<BBinder>::make();

    DisplayDeviceState state;
    state.isSecure = static_cast<bool>(Case::Display::SECURE);

    sp<mock::GraphicBufferProducer> surface{sp<mock::GraphicBufferProducer>::make()};
    state.surface = surface;
    mFlinger.mutableCurrentState().displays.add(displayToken, state);

    // --------------------------------------------------------------------
    // Call Expectations

    Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
    Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);

    EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _))
            .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR)));
    EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _))
            .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::HEIGHT), Return(NO_ERROR)));
    EXPECT_CALL(*surface, query(NATIVE_WINDOW_FORMAT, _))
            .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT),
                                  Return(NO_ERROR)));
    EXPECT_CALL(*surface, query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, _))
            .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));

    EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);

    EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1);
    EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);

    Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
    Case::WideColorSupport::setupComposerCallExpectations(this);
    Case::HdrSupport::setupComposerCallExpectations(this);
    Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);

    // --------------------------------------------------------------------
    // Postconditions

    // The display device should have been set up in the list of displays.
    verifyDisplayIsConnected<Case>(displayToken);

    // --------------------------------------------------------------------
    // Cleanup conditions

    EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID))
            .WillOnce(Return(Error::NONE));
    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));

    // Cleanup
    mFlinger.mutableCurrentState().displays.removeItem(displayToken);
    mFlinger.mutableDrawingState().displays.removeItem(displayToken);
}

TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAddedWithNoSurface) {
    using Case = HwcVirtualDisplayCase;

    // --------------------------------------------------------------------
    // Preconditions

    // The HWC supports at least one virtual display
    injectMockComposer(1);

    setupCommonPreconditions<Case>();

    // A virtual display was added to the current state, but it does not have a
    // surface.
    sp<BBinder> displayToken = sp<BBinder>::make();

    DisplayDeviceState state;
    state.isSecure = static_cast<bool>(Case::Display::SECURE);

    mFlinger.mutableCurrentState().displays.add(displayToken, state);

    // --------------------------------------------------------------------
    // Call Expectations

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);

    // --------------------------------------------------------------------
    // Postconditions

    // There will not be a display device set up.
    EXPECT_FALSE(hasDisplayDevice(displayToken));

    // The drawing display state will be set from the current display state.
    ASSERT_TRUE(hasDrawingDisplayState(displayToken));
    const auto& draw = getDrawingDisplayState(displayToken);
    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
}

TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayRemoval) {
    using Case = HwcVirtualDisplayCase;

    // --------------------------------------------------------------------
    // Preconditions

    // A virtual display is set up but is removed from the current state.
    const auto displayId = Case::Display::DISPLAY_ID::get();
    ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
    mFlinger.mutableHwcDisplayData().try_emplace(displayId);
    Case::Display::injectHwcDisplay(this);
    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
    existing.inject();
    mFlinger.mutableCurrentState().displays.removeItem(existing.token());

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);

    // --------------------------------------------------------------------
    // Postconditions

    // The existing token should have been removed
    verifyDisplayIsNotConnected(existing.token());
}

TEST_F(DisplayTransactionCommitTest, processesDisplayLayerStackChanges) {
    using Case = NonHwcVirtualDisplayCase;

    constexpr ui::LayerStack oldLayerStack = ui::DEFAULT_LAYER_STACK;
    constexpr ui::LayerStack newLayerStack{123u};

    // --------------------------------------------------------------------
    // Preconditions

    // A display is set up
    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
    display.inject();

    // There is a change to the layerStack state
    display.mutableDrawingDisplayState().layerStack = oldLayerStack;
    display.mutableCurrentDisplayState().layerStack = newLayerStack;

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);

    // --------------------------------------------------------------------
    // Postconditions

    EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
}

TEST_F(DisplayTransactionCommitTest, processesDisplayTransformChanges) {
    using Case = NonHwcVirtualDisplayCase;

    constexpr ui::Rotation oldTransform = ui::ROTATION_0;
    constexpr ui::Rotation newTransform = ui::ROTATION_180;

    // --------------------------------------------------------------------
    // Preconditions

    // A display is set up
    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
    display.inject();

    // There is a change to the orientation state
    display.mutableDrawingDisplayState().orientation = oldTransform;
    display.mutableCurrentDisplayState().orientation = newTransform;

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);

    // --------------------------------------------------------------------
    // Postconditions

    EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
}

TEST_F(DisplayTransactionCommitTest, processesDisplayLayerStackRectChanges) {
    using Case = NonHwcVirtualDisplayCase;

    const Rect oldLayerStackRect(0, 0, 0, 0);
    const Rect newLayerStackRect(0, 0, 123, 456);

    // --------------------------------------------------------------------
    // Preconditions

    // A display is set up
    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
    display.inject();

    // There is a change to the layerStackSpaceRect state
    display.mutableDrawingDisplayState().layerStackSpaceRect = oldLayerStackRect;
    display.mutableCurrentDisplayState().layerStackSpaceRect = newLayerStackRect;

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);

    // --------------------------------------------------------------------
    // Postconditions

    EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
}

TEST_F(DisplayTransactionCommitTest, processesDisplayRectChanges) {
    using Case = NonHwcVirtualDisplayCase;

    const Rect oldDisplayRect(0, 0);
    const Rect newDisplayRect(123, 456);

    // --------------------------------------------------------------------
    // Preconditions

    // A display is set up
    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
    display.inject();

    // There is a change to the layerStackSpaceRect state
    display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldDisplayRect;
    display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newDisplayRect;

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);

    // --------------------------------------------------------------------
    // Postconditions

    EXPECT_EQ(newDisplayRect, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
}

TEST_F(DisplayTransactionCommitTest, processesDisplayWidthChanges) {
    using Case = NonHwcVirtualDisplayCase;

    constexpr int oldWidth = 0;
    constexpr int oldHeight = 10;
    constexpr int newWidth = 123;

    // --------------------------------------------------------------------
    // Preconditions

    // A display is set up
    auto nativeWindow = sp<mock::NativeWindow>::make();
    auto displaySurface = sp<compositionengine::mock::DisplaySurface>::make();
    sp<GraphicBuffer> buf =

            sp<GraphicBuffer>::make();
    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
    display.setNativeWindow(nativeWindow);
    display.setDisplaySurface(displaySurface);
    // Setup injection expectations
    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
            .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
            .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
    display.inject();

    // There is a change to the layerStackSpaceRect state
    display.mutableDrawingDisplayState().width = oldWidth;
    display.mutableDrawingDisplayState().height = oldHeight;
    display.mutableCurrentDisplayState().width = newWidth;
    display.mutableCurrentDisplayState().height = oldHeight;

    // --------------------------------------------------------------------
    // Call Expectations

    EXPECT_CALL(*displaySurface, resizeBuffers(ui::Size(newWidth, oldHeight))).Times(1);

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
}

TEST_F(DisplayTransactionCommitTest, processesDisplayHeightChanges) {
    using Case = NonHwcVirtualDisplayCase;

    constexpr int oldWidth = 0;
    constexpr int oldHeight = 10;
    constexpr int newHeight = 123;

    // --------------------------------------------------------------------
    // Preconditions

    // A display is set up
    auto nativeWindow = sp<mock::NativeWindow>::make();
    auto displaySurface = sp<compositionengine::mock::DisplaySurface>::make();
    sp<GraphicBuffer> buf = sp<GraphicBuffer>::make();
    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
    display.setNativeWindow(nativeWindow);
    display.setDisplaySurface(displaySurface);
    // Setup injection expectations
    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
            .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
            .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
    EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
    display.inject();

    // There is a change to the layerStackSpaceRect state
    display.mutableDrawingDisplayState().width = oldWidth;
    display.mutableDrawingDisplayState().height = oldHeight;
    display.mutableCurrentDisplayState().width = oldWidth;
    display.mutableCurrentDisplayState().height = newHeight;

    // --------------------------------------------------------------------
    // Call Expectations

    EXPECT_CALL(*displaySurface, resizeBuffers(ui::Size(oldWidth, newHeight))).Times(1);

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
}

TEST_F(DisplayTransactionCommitTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) {
    using Case = NonHwcVirtualDisplayCase;

    constexpr uint32_t kOldWidth = 567;
    constexpr uint32_t kOldHeight = 456;
    const auto kOldSize = Rect(kOldWidth, kOldHeight);

    constexpr uint32_t kNewWidth = 234;
    constexpr uint32_t kNewHeight = 123;
    const auto kNewSize = Rect(kNewWidth, kNewHeight);

    // --------------------------------------------------------------------
    // Preconditions

    // A display is set up
    auto nativeWindow = sp<mock::NativeWindow>::make();
    auto displaySurface = sp<compositionengine::mock::DisplaySurface>::make();
    sp<GraphicBuffer> buf = sp<GraphicBuffer>::make();
    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
    display.setNativeWindow(nativeWindow);
    display.setDisplaySurface(displaySurface);
    // Setup injection expectations
    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
            .WillOnce(DoAll(SetArgPointee<1>(kOldWidth), Return(0)));
    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
            .WillOnce(DoAll(SetArgPointee<1>(kOldHeight), Return(0)));
    display.inject();

    // There is a change to the layerStackSpaceRect state
    display.mutableDrawingDisplayState().width = kOldWidth;
    display.mutableDrawingDisplayState().height = kOldHeight;
    display.mutableDrawingDisplayState().layerStackSpaceRect = kOldSize;
    display.mutableDrawingDisplayState().orientedDisplaySpaceRect = kOldSize;

    display.mutableCurrentDisplayState().width = kNewWidth;
    display.mutableCurrentDisplayState().height = kNewHeight;
    display.mutableCurrentDisplayState().layerStackSpaceRect = kNewSize;
    display.mutableCurrentDisplayState().orientedDisplaySpaceRect = kNewSize;

    // --------------------------------------------------------------------
    // Call Expectations

    EXPECT_CALL(*displaySurface, resizeBuffers(kNewSize.getSize())).Times(1);

    // --------------------------------------------------------------------
    // Invocation

    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);

    EXPECT_EQ(display.mutableDisplayDevice()->getBounds(), kNewSize);
    EXPECT_EQ(display.mutableDisplayDevice()->getWidth(), kNewWidth);
    EXPECT_EQ(display.mutableDisplayDevice()->getHeight(), kNewHeight);
    EXPECT_EQ(display.mutableDisplayDevice()->getOrientedDisplaySpaceRect(), kNewSize);
    EXPECT_EQ(display.mutableDisplayDevice()->getLayerStackSpaceRect(), kNewSize);
}

} // namespace
} // namespace android