/*
 * Copyright (C) 2016 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.
 */

#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>

#include <gtest/gtest.h>

#include <android/native_window.h>

#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>

#include <private/gui/ComposerService.h>
#include <ui/DisplayInfo.h>

#include <fstream>
#include <random>
#include <thread>

namespace android {

using Transaction = SurfaceComposerClient::Transaction;

constexpr int32_t SCALING_UPDATE = 1;
constexpr uint32_t BUFFER_UPDATES = 18;
constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
constexpr uint32_t SIZE_UPDATE = 134;
constexpr uint32_t STACK_UPDATE = 1;
constexpr uint64_t DEFERRED_UPDATE = 0;
constexpr float ALPHA_UPDATE = 0.29f;
constexpr float CORNER_RADIUS_UPDATE = 0.2f;
constexpr float POSITION_UPDATE = 121;
const Rect CROP_UPDATE(16, 16, 32, 32);

const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
constexpr auto TEST_SURFACE_NAME = "BG Interceptor Test Surface";
constexpr auto UNIQUE_TEST_SURFACE_NAME = "BG Interceptor Test Surface#0";
constexpr auto LAYER_NAME = "Layer Create and Delete Test";
constexpr auto UNIQUE_LAYER_NAME = "Layer Create and Delete Test#0";

constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";

// Fill an RGBA_8888 formatted surface with a single color.
static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
    ANativeWindow_Buffer outBuffer;
    sp<Surface> s = sc->getSurface();
    ASSERT_TRUE(s != nullptr);
    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
    uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
    for (int y = 0; y < outBuffer.height; y++) {
        for (int x = 0; x < outBuffer.width; x++) {
            uint8_t* pixel = img + (4 * (y*outBuffer.stride + x));
            pixel[0] = r;
            pixel[1] = g;
            pixel[2] = b;
            pixel[3] = 255;
        }
    }
    ASSERT_EQ(NO_ERROR, s->unlockAndPost());
}

static status_t readProtoFile(Trace* trace) {
    status_t err = NO_ERROR;

    int fd = open(DEFAULT_FILENAME, O_RDONLY);
    {
        google::protobuf::io::FileInputStream f(fd);
        if (fd && !trace->ParseFromZeroCopyStream(&f)) {
            err = PERMISSION_DENIED;
        }
    }
    close(fd);

    return err;
}

static void enableInterceptor() {
    system("service call SurfaceFlinger 1020 i32 1 > /dev/null");
}

static void disableInterceptor() {
    system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
}

int32_t getSurfaceId(const Trace& capturedTrace, const std::string& surfaceName) {
    int32_t layerId = 0;
    for (const auto& increment : capturedTrace.increment()) {
        if (increment.increment_case() == increment.kSurfaceCreation) {
            if (increment.surface_creation().name() == surfaceName) {
                layerId = increment.surface_creation().id();
            }
        }
    }
    return layerId;
}

int32_t getDisplayId(const Trace& capturedTrace, const std::string& displayName) {
    int32_t displayId = 0;
    for (const auto& increment : capturedTrace.increment()) {
        if (increment.increment_case() == increment.kDisplayCreation) {
            if (increment.display_creation().name() == displayName) {
                displayId = increment.display_creation().id();
                break;
            }
        }
    }
    return displayId;
}

class SurfaceInterceptorTest : public ::testing::Test {
protected:
    void SetUp() override {
        // Allow SurfaceInterceptor write to /data
        system("setenforce 0");

        mComposerClient = new SurfaceComposerClient;
        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
    }

    void TearDown() override {
        mComposerClient->dispose();
        mBGSurfaceControl.clear();
        mComposerClient.clear();
    }

    sp<SurfaceComposerClient> mComposerClient;
    sp<SurfaceControl> mBGSurfaceControl;
    int32_t mBGLayerId;

public:
    using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&);
    using TestAction = void (SurfaceInterceptorTest::*)();
    using TestBooleanVerification = bool (SurfaceInterceptorTest::*)(const Trace&);
    using TestVerification = void (SurfaceInterceptorTest::*)(const Trace&);

    void setupBackgroundSurface();
    void preProcessTrace(const Trace& trace);

    // captureTest will enable SurfaceInterceptor, setup background surface,
    // disable SurfaceInterceptor, collect the trace and process the trace for
    // id of background surface before further verification.
    void captureTest(TestTransactionAction action, TestBooleanVerification verification);
    void captureTest(TestTransactionAction action, SurfaceChange::SurfaceChangeCase changeCase);
    void captureTest(TestTransactionAction action, Increment::IncrementCase incrementCase);
    void captureTest(TestAction action, TestBooleanVerification verification);
    void captureTest(TestAction action, TestVerification verification);
    void runInTransaction(TestTransactionAction action);

    // Verification of changes to a surface
    bool positionUpdateFound(const SurfaceChange& change, bool foundPosition);
    bool sizeUpdateFound(const SurfaceChange& change, bool foundSize);
    bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
    bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
    bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
    bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
    bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
    bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
    bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
    bool layerStackUpdateFound(const SurfaceChange& change, bool foundLayerStack);
    bool hiddenFlagUpdateFound(const SurfaceChange& change, bool foundHiddenFlag);
    bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
    bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
    bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred);
    bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);

    // Find all of the updates in the single trace
    void assertAllUpdatesFound(const Trace& trace);

    // Verification of creation and deletion of a surface
    bool surfaceCreationFound(const Increment& increment, bool foundSurface);
    bool surfaceDeletionFound(const Increment& increment, const int32_t targetId,
            bool foundSurface);
    bool displayCreationFound(const Increment& increment, bool foundDisplay);
    bool displayDeletionFound(const Increment& increment, const int32_t targetId,
            bool foundDisplay);
    bool singleIncrementFound(const Trace& trace, Increment::IncrementCase incrementCase);

    // Verification of buffer updates
    bool bufferUpdatesFound(const Trace& trace);

    // Perform each of the possible changes to a surface
    void positionUpdate(Transaction&);
    void sizeUpdate(Transaction&);
    void alphaUpdate(Transaction&);
    void layerUpdate(Transaction&);
    void cropUpdate(Transaction&);
    void cornerRadiusUpdate(Transaction&);
    void matrixUpdate(Transaction&);
    void overrideScalingModeUpdate(Transaction&);
    void transparentRegionHintUpdate(Transaction&);
    void layerStackUpdate(Transaction&);
    void hiddenFlagUpdate(Transaction&);
    void opaqueFlagUpdate(Transaction&);
    void secureFlagUpdate(Transaction&);
    void deferredTransactionUpdate(Transaction&);
    void surfaceCreation(Transaction&);
    void displayCreation(Transaction&);
    void displayDeletion(Transaction&);

    void nBufferUpdates();
    void runAllUpdates();

private:
    void captureInTransaction(TestTransactionAction action, Trace*);
    void capture(TestAction action, Trace*);
};

void SurfaceInterceptorTest::captureInTransaction(TestTransactionAction action, Trace* outTrace) {
    enableInterceptor();
    setupBackgroundSurface();
    runInTransaction(action);
    disableInterceptor();
    ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
    preProcessTrace(*outTrace);
}

void SurfaceInterceptorTest::capture(TestAction action, Trace* outTrace) {
    enableInterceptor();
    setupBackgroundSurface();
    (this->*action)();
    disableInterceptor();
    ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
    preProcessTrace(*outTrace);
}

void SurfaceInterceptorTest::setupBackgroundSurface() {
    const auto display = SurfaceComposerClient::getInternalDisplayToken();
    ASSERT_FALSE(display == nullptr);

    DisplayInfo info;
    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));

    ssize_t displayWidth = info.w;
    ssize_t displayHeight = info.h;

    // Background surface
    mBGSurfaceControl = mComposerClient->createSurface(
            String8(TEST_SURFACE_NAME), displayWidth, displayHeight,
            PIXEL_FORMAT_RGBA_8888, 0);
    ASSERT_TRUE(mBGSurfaceControl != nullptr);
    ASSERT_TRUE(mBGSurfaceControl->isValid());

    Transaction t;
    t.setDisplayLayerStack(display, 0);
    ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX-3)
            .show(mBGSurfaceControl)
            .apply());
}

void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) {
    mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_SURFACE_NAME);
}

void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
        TestBooleanVerification verification) {
    Trace capturedTrace;
    captureInTransaction(action, &capturedTrace);
    ASSERT_TRUE((this->*verification)(capturedTrace));
}

void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
        Increment::IncrementCase incrementCase) {
    Trace capturedTrace;
    captureInTransaction(action, &capturedTrace);
    ASSERT_TRUE(singleIncrementFound(capturedTrace, incrementCase));
}

void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
        SurfaceChange::SurfaceChangeCase changeCase) {
    Trace capturedTrace;
    captureInTransaction(action, &capturedTrace);
    ASSERT_TRUE(surfaceUpdateFound(capturedTrace, changeCase));
}

void SurfaceInterceptorTest::captureTest(TestAction action, TestBooleanVerification verification) {
    Trace capturedTrace;
    capture(action, &capturedTrace);
    ASSERT_TRUE((this->*verification)(capturedTrace));
}

void SurfaceInterceptorTest::captureTest(TestAction action, TestVerification verification) {
    Trace capturedTrace;
    capture(action, &capturedTrace);
    (this->*verification)(capturedTrace);
}

void SurfaceInterceptorTest::runInTransaction(TestTransactionAction action) {
    Transaction t;
    (this->*action)(t);
    t.apply(true);
}

void SurfaceInterceptorTest::positionUpdate(Transaction& t) {
    t.setPosition(mBGSurfaceControl, POSITION_UPDATE, POSITION_UPDATE);
}

void SurfaceInterceptorTest::sizeUpdate(Transaction& t) {
    t.setSize(mBGSurfaceControl, SIZE_UPDATE, SIZE_UPDATE);
}

void SurfaceInterceptorTest::alphaUpdate(Transaction& t) {
    t.setAlpha(mBGSurfaceControl, ALPHA_UPDATE);
}

void SurfaceInterceptorTest::cornerRadiusUpdate(Transaction& t) {
    t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
}

void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
    t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
}

void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
    t.setCrop_legacy(mBGSurfaceControl, CROP_UPDATE);
}

void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
    t.setMatrix(mBGSurfaceControl, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
}

void SurfaceInterceptorTest::overrideScalingModeUpdate(Transaction& t) {
    t.setOverrideScalingMode(mBGSurfaceControl, SCALING_UPDATE);
}

void SurfaceInterceptorTest::transparentRegionHintUpdate(Transaction& t) {
    Region region(CROP_UPDATE);
    t.setTransparentRegionHint(mBGSurfaceControl, region);
}

void SurfaceInterceptorTest::layerStackUpdate(Transaction& t) {
    t.setLayerStack(mBGSurfaceControl, STACK_UPDATE);
}

void SurfaceInterceptorTest::hiddenFlagUpdate(Transaction& t) {
    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
}

void SurfaceInterceptorTest::opaqueFlagUpdate(Transaction& t) {
    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
}

void SurfaceInterceptorTest::secureFlagUpdate(Transaction& t) {
    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
}

void SurfaceInterceptorTest::deferredTransactionUpdate(Transaction& t) {
    t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl->getHandle(),
                                   DEFERRED_UPDATE);
}

void SurfaceInterceptorTest::displayCreation(Transaction&) {
    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
    SurfaceComposerClient::destroyDisplay(testDisplay);
}

void SurfaceInterceptorTest::displayDeletion(Transaction&) {
    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
    SurfaceComposerClient::destroyDisplay(testDisplay);
}

void SurfaceInterceptorTest::runAllUpdates() {
    runInTransaction(&SurfaceInterceptorTest::positionUpdate);
    runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
    runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
    runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
    runInTransaction(&SurfaceInterceptorTest::layerUpdate);
    runInTransaction(&SurfaceInterceptorTest::cropUpdate);
    runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
    runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate);
    runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
    runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
    runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
    runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
    runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
    runInTransaction(&SurfaceInterceptorTest::deferredTransactionUpdate);
}

void SurfaceInterceptorTest::surfaceCreation(Transaction&) {
    mComposerClient->createSurface(String8(LAYER_NAME), SIZE_UPDATE, SIZE_UPDATE,
            PIXEL_FORMAT_RGBA_8888, 0);
}

void SurfaceInterceptorTest::nBufferUpdates() {
    std::random_device rd;
    std::mt19937_64 gen(rd());
    // This makes testing fun
    std::uniform_int_distribution<uint8_t> dis;
    for (uint32_t i = 0; i < BUFFER_UPDATES; ++i) {
        fillSurfaceRGBA8(mBGSurfaceControl, dis(gen), dis(gen), dis(gen));
    }
}

bool SurfaceInterceptorTest::positionUpdateFound(const SurfaceChange& change, bool foundPosition) {
    // There should only be one position transaction with x and y = POSITION_UPDATE
    bool hasX(change.position().x() == POSITION_UPDATE);
    bool hasY(change.position().y() == POSITION_UPDATE);
    if (hasX && hasY && !foundPosition) {
        foundPosition = true;
    } else if (hasX && hasY && foundPosition) {
        // Failed because the position update was found a second time
        [] () { FAIL(); }();
    }
    return foundPosition;
}

bool SurfaceInterceptorTest::sizeUpdateFound(const SurfaceChange& change, bool foundSize) {
    bool hasWidth(change.size().h() == SIZE_UPDATE);
    bool hasHeight(change.size().w() == SIZE_UPDATE);
    if (hasWidth && hasHeight && !foundSize) {
        foundSize = true;
    } else if (hasWidth && hasHeight && foundSize) {
        [] () { FAIL(); }();
    }
    return foundSize;
}

bool SurfaceInterceptorTest::alphaUpdateFound(const SurfaceChange& change, bool foundAlpha) {
    bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE);
    if (hasAlpha && !foundAlpha) {
        foundAlpha = true;
    } else if (hasAlpha && foundAlpha) {
        [] () { FAIL(); }();
    }
    return foundAlpha;
}

bool SurfaceInterceptorTest::cornerRadiusUpdateFound(const SurfaceChange &change,
                                                     bool foundCornerRadius) {
    bool hasCornerRadius(change.corner_radius().corner_radius() == CORNER_RADIUS_UPDATE);
    if (hasCornerRadius && !foundCornerRadius) {
        foundCornerRadius = true;
    } else if (hasCornerRadius && foundCornerRadius) {
        [] () { FAIL(); }();
    }
    return foundCornerRadius;
}

bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
    bool hasLayer(change.layer().layer() == LAYER_UPDATE);
    if (hasLayer && !foundLayer) {
        foundLayer = true;
    } else if (hasLayer && foundLayer) {
        [] () { FAIL(); }();
    }
    return foundLayer;
}

bool SurfaceInterceptorTest::cropUpdateFound(const SurfaceChange& change, bool foundCrop) {
    bool hasLeft(change.crop().rectangle().left() == CROP_UPDATE.left);
    bool hasTop(change.crop().rectangle().top() == CROP_UPDATE.top);
    bool hasRight(change.crop().rectangle().right() == CROP_UPDATE.right);
    bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom);
    if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) {
        foundCrop = true;
    } else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
        [] () { FAIL(); }();
    }
    return foundCrop;
}

bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) {
    bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2);
    bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2);
    bool hasSy((float)change.matrix().dsdy() == (float)M_SQRT1_2);
    bool hasTy((float)change.matrix().dtdy() == (float)-M_SQRT1_2);
    if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) {
        foundMatrix = true;
    } else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
        [] () { FAIL(); }();
    }
    return foundMatrix;
}

bool SurfaceInterceptorTest::scalingModeUpdateFound(const SurfaceChange& change,
        bool foundScalingMode) {
    bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE);
    if (hasScalingUpdate && !foundScalingMode) {
        foundScalingMode = true;
    } else if (hasScalingUpdate && foundScalingMode) {
        [] () { FAIL(); }();
    }
    return foundScalingMode;
}

bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
        bool foundTransparentRegion) {
    auto traceRegion = change.transparent_region_hint().region(0);
    bool hasLeft(traceRegion.left() == CROP_UPDATE.left);
    bool hasTop(traceRegion.top() == CROP_UPDATE.top);
    bool hasRight(traceRegion.right() == CROP_UPDATE.right);
    bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom);
    if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) {
        foundTransparentRegion = true;
    } else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
        [] () { FAIL(); }();
    }
    return foundTransparentRegion;
}

bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change,
        bool foundLayerStack) {
    bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE);
    if (hasLayerStackUpdate && !foundLayerStack) {
        foundLayerStack = true;
    } else if (hasLayerStackUpdate && foundLayerStack) {
        [] () { FAIL(); }();
    }
    return foundLayerStack;
}

bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change,
        bool foundHiddenFlag) {
    bool hasHiddenFlag(change.hidden_flag().hidden_flag());
    if (hasHiddenFlag && !foundHiddenFlag) {
        foundHiddenFlag = true;
    } else if (hasHiddenFlag && foundHiddenFlag) {
        [] () { FAIL(); }();
    }
    return foundHiddenFlag;
}

bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change,
        bool foundOpaqueFlag) {
    bool hasOpaqueFlag(change.opaque_flag().opaque_flag());
    if (hasOpaqueFlag && !foundOpaqueFlag) {
        foundOpaqueFlag = true;
    } else if (hasOpaqueFlag && foundOpaqueFlag) {
        [] () { FAIL(); }();
    }
    return foundOpaqueFlag;
}

bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change,
        bool foundSecureFlag) {
    bool hasSecureFlag(change.secure_flag().secure_flag());
    if (hasSecureFlag && !foundSecureFlag) {
        foundSecureFlag = true;
    } else if (hasSecureFlag && foundSecureFlag) {
        [] () { FAIL(); }();
    }
    return foundSecureFlag;
}

bool SurfaceInterceptorTest::deferredTransactionUpdateFound(const SurfaceChange& change,
        bool foundDeferred) {
    bool hasId(change.deferred_transaction().layer_id() == mBGLayerId);
    bool hasFrameNumber(change.deferred_transaction().frame_number() == DEFERRED_UPDATE);
    if (hasId && hasFrameNumber && !foundDeferred) {
        foundDeferred = true;
    } else if (hasId && hasFrameNumber && foundDeferred) {
        [] () { FAIL(); }();
    }
    return foundDeferred;
}

bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
        SurfaceChange::SurfaceChangeCase changeCase) {
    bool foundUpdate = false;
    for (const auto& increment : trace.increment()) {
        if (increment.increment_case() == increment.kTransaction) {
            for (const auto& change : increment.transaction().surface_change()) {
                if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) {
                    switch (changeCase) {
                        case SurfaceChange::SurfaceChangeCase::kPosition:
                            // foundUpdate is sent for the tests to fail on duplicated increments
                            foundUpdate = positionUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kSize:
                            foundUpdate = sizeUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kAlpha:
                            foundUpdate = alphaUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kLayer:
                            foundUpdate = layerUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kCrop:
                            foundUpdate = cropUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kCornerRadius:
                            foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kMatrix:
                            foundUpdate = matrixUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
                            foundUpdate = scalingModeUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
                            foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kLayerStack:
                            foundUpdate = layerStackUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
                            foundUpdate = hiddenFlagUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
                            foundUpdate = opaqueFlagUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kSecureFlag:
                            foundUpdate = secureFlagUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
                            foundUpdate = deferredTransactionUpdateFound(change, foundUpdate);
                            break;
                        case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
                            break;
                    }
                }
            }
        }
    }
    return foundUpdate;
}

void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) {
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOverrideScalingMode));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDeferredTransaction));
}

bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
    bool isMatch(increment.surface_creation().name() == UNIQUE_LAYER_NAME &&
            increment.surface_creation().w() == SIZE_UPDATE &&
            increment.surface_creation().h() == SIZE_UPDATE);
    if (isMatch && !foundSurface) {
        foundSurface = true;
    } else if (isMatch && foundSurface) {
        [] () { FAIL(); }();
    }
    return foundSurface;
}

bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment,
        const int32_t targetId, bool foundSurface) {
    bool isMatch(increment.surface_deletion().id() == targetId);
    if (isMatch && !foundSurface) {
        foundSurface = true;
    } else if (isMatch && foundSurface) {
        [] () { FAIL(); }();
    }
    return foundSurface;
}

bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) {
    bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() &&
            increment.display_creation().is_secure());
    if (isMatch && !foundDisplay) {
        foundDisplay = true;
    } else if (isMatch && foundDisplay) {
        [] () { FAIL(); }();
    }
    return foundDisplay;
}

bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment,
        const int32_t targetId, bool foundDisplay) {
    bool isMatch(increment.display_deletion().id() == targetId);
    if (isMatch && !foundDisplay) {
        foundDisplay = true;
    } else if (isMatch && foundDisplay) {
        [] () { FAIL(); }();
    }
    return foundDisplay;
}

bool SurfaceInterceptorTest::singleIncrementFound(const Trace& trace,
        Increment::IncrementCase incrementCase) {
    bool foundIncrement = false;
    for (const auto& increment : trace.increment()) {
        if (increment.increment_case() == incrementCase) {
            int32_t targetId = 0;
            switch (incrementCase) {
                case Increment::IncrementCase::kSurfaceCreation:
                    foundIncrement = surfaceCreationFound(increment, foundIncrement);
                    break;
                case Increment::IncrementCase::kSurfaceDeletion:
                    // Find the id of created surface.
                    targetId = getSurfaceId(trace, UNIQUE_LAYER_NAME);
                    foundIncrement = surfaceDeletionFound(increment, targetId, foundIncrement);
                    break;
                case Increment::IncrementCase::kDisplayCreation:
                    foundIncrement = displayCreationFound(increment, foundIncrement);
                    break;
                case Increment::IncrementCase::kDisplayDeletion:
                    // Find the id of created display.
                    targetId = getDisplayId(trace, DISPLAY_NAME.string());
                    foundIncrement = displayDeletionFound(increment, targetId, foundIncrement);
                    break;
                default:
                    /* code */
                    break;
            }
        }
    }
    return foundIncrement;
}

bool SurfaceInterceptorTest::bufferUpdatesFound(const Trace& trace) {
    uint32_t updates = 0;
    for (const auto& inc : trace.increment()) {
        if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) {
            updates++;
        }
    }
    return updates == BUFFER_UPDATES;
}

TEST_F(SurfaceInterceptorTest, InterceptPositionUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::positionUpdate,
            SurfaceChange::SurfaceChangeCase::kPosition);
}

TEST_F(SurfaceInterceptorTest, InterceptSizeUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::sizeUpdate, SurfaceChange::SurfaceChangeCase::kSize);
}

TEST_F(SurfaceInterceptorTest, InterceptAlphaUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::alphaUpdate, SurfaceChange::SurfaceChangeCase::kAlpha);
}

TEST_F(SurfaceInterceptorTest, InterceptLayerUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::layerUpdate, SurfaceChange::SurfaceChangeCase::kLayer);
}

TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
}

TEST_F(SurfaceInterceptorTest, InterceptCornerRadiusUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::cornerRadiusUpdate,
            SurfaceChange::SurfaceChangeCase::kCornerRadius);
}

TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
}

TEST_F(SurfaceInterceptorTest, InterceptOverrideScalingModeUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::overrideScalingModeUpdate,
            SurfaceChange::SurfaceChangeCase::kOverrideScalingMode);
}

TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::transparentRegionHintUpdate,
            SurfaceChange::SurfaceChangeCase::kTransparentRegionHint);
}

TEST_F(SurfaceInterceptorTest, InterceptLayerStackUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::layerStackUpdate,
            SurfaceChange::SurfaceChangeCase::kLayerStack);
}

TEST_F(SurfaceInterceptorTest, InterceptHiddenFlagUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::hiddenFlagUpdate,
            SurfaceChange::SurfaceChangeCase::kHiddenFlag);
}

TEST_F(SurfaceInterceptorTest, InterceptOpaqueFlagUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::opaqueFlagUpdate,
            SurfaceChange::SurfaceChangeCase::kOpaqueFlag);
}

TEST_F(SurfaceInterceptorTest, InterceptSecureFlagUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::secureFlagUpdate,
            SurfaceChange::SurfaceChangeCase::kSecureFlag);
}

TEST_F(SurfaceInterceptorTest, InterceptDeferredTransactionUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::deferredTransactionUpdate,
            SurfaceChange::SurfaceChangeCase::kDeferredTransaction);
}

TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
    captureTest(&SurfaceInterceptorTest::runAllUpdates,
                &SurfaceInterceptorTest::assertAllUpdatesFound);
}

TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) {
    captureTest(&SurfaceInterceptorTest::surfaceCreation,
            Increment::IncrementCase::kSurfaceCreation);
}

TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) {
    captureTest(&SurfaceInterceptorTest::displayCreation,
            Increment::IncrementCase::kDisplayCreation);
}

TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) {
    enableInterceptor();
    runInTransaction(&SurfaceInterceptorTest::displayDeletion);
    disableInterceptor();
    Trace capturedTrace;
    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
    ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kDisplayDeletion));
}

TEST_F(SurfaceInterceptorTest, InterceptBufferUpdateWorks) {
    captureTest(&SurfaceInterceptorTest::nBufferUpdates,
            &SurfaceInterceptorTest::bufferUpdatesFound);
}

// If the interceptor is enabled while buffer updates are being pushed, the interceptor should
// first create a snapshot of the existing displays and surfaces and then start capturing
// the buffer updates
TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) {
    setupBackgroundSurface();
    std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
    enableInterceptor();
    disableInterceptor();
    bufferUpdates.join();

    Trace capturedTrace;
    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
    const auto& firstIncrement = capturedTrace.mutable_increment(0);
    ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kDisplayCreation);
}

TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) {
    enableInterceptor();
    setupBackgroundSurface();
    std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
    std::thread surfaceUpdates(&SurfaceInterceptorTest::runAllUpdates, this);
    runInTransaction(&SurfaceInterceptorTest::surfaceCreation);
    bufferUpdates.join();
    surfaceUpdates.join();
    disableInterceptor();

    Trace capturedTrace;
    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
    preProcessTrace(capturedTrace);

    assertAllUpdatesFound(capturedTrace);
    ASSERT_TRUE(bufferUpdatesFound(capturedTrace));
    ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kSurfaceCreation));
}

}