Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 24b0a485 authored by Lloyd Pique's avatar Lloyd Pique
Browse files

SF: Test coverage for EventThread

Add a unit test to cover EventThread.cpp

Test: atest libsurfaceflinger_unittest
Bug: 74827900
Change-Id: If9479cd9deedff836068cb53e7da2cb64041aea1
parent 7d4aa6c6
Loading
Loading
Loading
Loading
+10 −8
Original line number Original line Diff line number Diff line
@@ -26,14 +26,12 @@
#include <cutils/sched_policy.h>
#include <cutils/sched_policy.h>


#include <gui/DisplayEventReceiver.h>
#include <gui/DisplayEventReceiver.h>
#include <gui/IDisplayEventConnection.h>


#include <utils/Errors.h>
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/String8.h>
#include <utils/Trace.h>
#include <utils/Trace.h>


#include "EventThread.h"
#include "EventThread.h"
#include "SurfaceFlinger.h"


using namespace std::chrono_literals;
using namespace std::chrono_literals;


@@ -47,9 +45,11 @@ EventThread::~EventThread() = default;


namespace impl {
namespace impl {


EventThread::EventThread(VSyncSource* src, SurfaceFlinger& flinger, bool interceptVSyncs,
EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
                         const char* threadName)
                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
      : mVSyncSource(src), mFlinger(flinger), mInterceptVSyncs(interceptVSyncs) {
      : mVSyncSource(src),
        mResyncWithRateLimitCallback(resyncWithRateLimitCallback),
        mInterceptVSyncsCallback(interceptVSyncsCallback) {
    for (auto& event : mVSyncEvent) {
    for (auto& event : mVSyncEvent) {
        event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
        event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
        event.header.id = 0;
        event.header.id = 0;
@@ -118,7 +118,9 @@ void EventThread::setVsyncRate(uint32_t count, const sp<EventThread::Connection>
void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) {
void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) {
    std::lock_guard<std::mutex> lock(mMutex);
    std::lock_guard<std::mutex> lock(mMutex);


    mFlinger.resyncWithRateLimit();
    if (mResyncWithRateLimitCallback) {
        mResyncWithRateLimitCallback();
    }


    if (connection->count < 0) {
    if (connection->count < 0) {
        connection->count = 0;
        connection->count = 0;
@@ -216,8 +218,8 @@ Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked(
            timestamp = mVSyncEvent[i].header.timestamp;
            timestamp = mVSyncEvent[i].header.timestamp;
            if (timestamp) {
            if (timestamp) {
                // we have a vsync event to dispatch
                // we have a vsync event to dispatch
                if (mInterceptVSyncs) {
                if (mInterceptVSyncsCallback) {
                    mFlinger.mInterceptor->saveVSyncEvent(timestamp);
                    mInterceptVSyncsCallback(timestamp);
                }
                }
                *event = mVSyncEvent[i];
                *event = mVSyncEvent[i];
                mVSyncEvent[i].header.timestamp = 0;
                mVSyncEvent[i].header.timestamp = 0;
+14 −8
Original line number Original line Diff line number Diff line
@@ -37,6 +37,7 @@
namespace android {
namespace android {
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------


class EventThreadTest;
class SurfaceFlinger;
class SurfaceFlinger;
class String8;
class String8;


@@ -82,7 +83,9 @@ class EventThread : public android::EventThread, private VSyncSource::Callback {
    class Connection : public BnDisplayEventConnection {
    class Connection : public BnDisplayEventConnection {
    public:
    public:
        explicit Connection(EventThread* eventThread);
        explicit Connection(EventThread* eventThread);
        status_t postEvent(const DisplayEventReceiver::Event& event);
        virtual ~Connection();

        virtual status_t postEvent(const DisplayEventReceiver::Event& event);


        // count >= 1 : continuous event. count is the vsync rate
        // count >= 1 : continuous event. count is the vsync rate
        // count == 0 : one-shot event that has not fired
        // count == 0 : one-shot event that has not fired
@@ -90,7 +93,6 @@ class EventThread : public android::EventThread, private VSyncSource::Callback {
        int32_t count;
        int32_t count;


    private:
    private:
        virtual ~Connection();
        virtual void onFirstRef();
        virtual void onFirstRef();
        status_t stealReceiveChannel(gui::BitTube* outChannel) override;
        status_t stealReceiveChannel(gui::BitTube* outChannel) override;
        status_t setVsyncRate(uint32_t count) override;
        status_t setVsyncRate(uint32_t count) override;
@@ -100,8 +102,11 @@ class EventThread : public android::EventThread, private VSyncSource::Callback {
    };
    };


public:
public:
    EventThread(VSyncSource* src, SurfaceFlinger& flinger, bool interceptVSyncs,
    using ResyncWithRateLimitCallback = std::function<void()>;
                const char* threadName);
    using InterceptVSyncsCallback = std::function<void(nsecs_t)>;

    EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
                InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
    ~EventThread();
    ~EventThread();


    sp<BnDisplayEventConnection> createEventConnection() const override;
    sp<BnDisplayEventConnection> createEventConnection() const override;
@@ -124,6 +129,8 @@ public:
    void setPhaseOffset(nsecs_t phaseOffset) override;
    void setPhaseOffset(nsecs_t phaseOffset) override;


private:
private:
    friend EventThreadTest;

    void threadMain();
    void threadMain();
    Vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock,
    Vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock,
                                                           DisplayEventReceiver::Event* event)
                                                           DisplayEventReceiver::Event* event)
@@ -137,8 +144,9 @@ private:
    void onVSyncEvent(nsecs_t timestamp) override;
    void onVSyncEvent(nsecs_t timestamp) override;


    // constants
    // constants
    VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
    VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr;
    SurfaceFlinger& mFlinger;
    const ResyncWithRateLimitCallback mResyncWithRateLimitCallback;
    const InterceptVSyncsCallback mInterceptVSyncsCallback;


    std::thread mThread;
    std::thread mThread;
    mutable std::mutex mMutex;
    mutable std::mutex mMutex;
@@ -155,8 +163,6 @@ private:


    // for debugging
    // for debugging
    bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false;
    bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false;

    const bool mInterceptVSyncs = false;
};
};


// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
+15 −6
Original line number Original line Diff line number Diff line
@@ -636,13 +636,20 @@ void SurfaceFlinger::init() {
    mEventThreadSource =
    mEventThreadSource =
            std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs,
            std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs,
                                             true, "app");
                                             true, "app");
    mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(), *this, false,
    mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(),
                                                       [this]() { resyncWithRateLimit(); },
                                                       impl::EventThread::InterceptVSyncsCallback(),
                                                       "appEventThread");
                                                       "appEventThread");
    mSfEventThreadSource =
    mSfEventThreadSource =
            std::make_unique<DispSyncSource>(&mPrimaryDispSync,
            std::make_unique<DispSyncSource>(&mPrimaryDispSync,
                                             SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
                                             SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");


    mSFEventThread = std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), *this, true,
    mSFEventThread =
            std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
                                                [this]() { resyncWithRateLimit(); },
                                                [this](nsecs_t timestamp) {
                                                    mInterceptor->saveVSyncEvent(timestamp);
                                                },
                                                "sfEventThread");
                                                "sfEventThread");
    mEventQueue->setEventThread(mSFEventThread.get());
    mEventQueue->setEventThread(mSFEventThread.get());
    mVsyncModulator.setEventThread(mSFEventThread.get());
    mVsyncModulator.setEventThread(mSFEventThread.get());
@@ -1132,8 +1139,10 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
            ALOGV("VSync Injections enabled");
            ALOGV("VSync Injections enabled");
            if (mVSyncInjector.get() == nullptr) {
            if (mVSyncInjector.get() == nullptr) {
                mVSyncInjector = std::make_unique<InjectVSyncSource>();
                mVSyncInjector = std::make_unique<InjectVSyncSource>();
                mInjectorEventThread =
                mInjectorEventThread = std::make_unique<
                        std::make_unique<impl::EventThread>(mVSyncInjector.get(), *this, false,
                        impl::EventThread>(mVSyncInjector.get(),
                                           [this]() { resyncWithRateLimit(); },
                                           impl::EventThread::InterceptVSyncsCallback(),
                                           "injEventThread");
                                           "injEventThread");
            }
            }
            mEventQueue->setEventThread(mInjectorEventThread.get());
            mEventQueue->setEventThread(mInjectorEventThread.get());
+1 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ cc_test {
    srcs: [
    srcs: [
        ":libsurfaceflinger_sources",
        ":libsurfaceflinger_sources",
        "DisplayTransactionTest.cpp",
        "DisplayTransactionTest.cpp",
        "EventThreadTest.cpp",
        "mock/DisplayHardware/MockComposer.cpp",
        "mock/DisplayHardware/MockComposer.cpp",
        "mock/DisplayHardware/MockDisplaySurface.cpp",
        "mock/DisplayHardware/MockDisplaySurface.cpp",
        "mock/gui/MockGraphicBufferConsumer.cpp",
        "mock/gui/MockGraphicBufferConsumer.cpp",
+165 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <chrono>
#include <deque>
#include <mutex>
#include <optional>
#include <thread>
#include <tuple>
#include <type_traits>
#include <utility>

#include <android-base/thread_annotations.h>

namespace android {

// This class helps record calls made by another thread when they are made
// asynchronously, with no other way for the tests to verify that the calls have
// been made.
//
// A normal Google Mock recorder, while thread safe, does not allow you to wait
// for asynchronous calls to be made.
//
// Usage:
//
// In the test, use a Google Mock expectation to invoke an instance of the
// recorder:
//
//     AsyncCallRecorder<void(int)> recorder;
//
//     EXPECT_CALL(someMock, someFunction(_)).
//             .WillRepeatedly(Invoke(recorder.getInvocable()));
//
// Then you can invoke the functionality being tested:
//
//     threadUnderTest.doSomethingAsync()
//
// And afterwards make a number of assertions using the recorder:
//
//     // Wait for one call (with reasonable default timeout), and get the args
//     // as a std::tuple inside a std::optional.
//     auto args = recorder.waitForCall();
//     // The returned std::optional will have a value if the recorder function
//     // was called.
//     ASSERT_TRUE(args.has_value());
//     // The arguments can be checked if needed using standard tuple
//     // operations.
//     EXPECT_EQ(123, std::get<0>(args.value()));
//
// Alternatively maybe you want to assert that a call was not made.
//
//     EXPECT_FALSE(recorder.waitForUnexpectedCall().has_value());
//
// However this check uses a really short timeout so as not to block the test
// unnecessarily. And it could be possible for the check to return false and
// then the recorder could observe a call being made after.
template <typename Func>
class AsyncCallRecorder;

template <typename... Args>
class AsyncCallRecorder<void (*)(Args...)> {
public:
    // For the tests, we expect the wait for an expected change to be signaled
    // to be much shorter than this.
    static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{10};

    // The wait here is tricky. We don't expect a change, but we don't want to
    // wait forever (or for longer than the typical test function runtime). As
    // even the simplest Google Test can take 1ms (1000us) to run, we wait for
    // half that time.
    static constexpr std::chrono::microseconds UNEXPECTED_CALL_TIMEOUT{500};

    using ArgTuple = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;

    void recordCall(Args... args) {
        std::lock_guard<std::mutex> lock(mMutex);
        mCalls.emplace_back(std::make_tuple(args...));
        mCondition.notify_all();
    }

    // Returns a functor which can be used with the Google Mock Invoke()
    // function, or as a std::function to record calls.
    auto getInvocable() {
        return [this](Args... args) { recordCall(args...); };
    }

    // Returns a set of arguments as a std::optional<std::tuple<...>> for the
    // oldest call, waiting for the given timeout if necessary if there are no
    // arguments in the FIFO.
    std::optional<ArgTuple> waitForCall(
            std::chrono::microseconds timeout = DEFAULT_CALL_EXPECTED_TIMEOUT)
            NO_THREAD_SAFETY_ANALYSIS {
        std::unique_lock<std::mutex> lock(mMutex);

        // Wait if necessary for us to have a record from a call.
        mCondition.wait_for(lock, timeout,
                            [this]() NO_THREAD_SAFETY_ANALYSIS { return !mCalls.empty(); });

        // Return the arguments from the oldest call, if one was made
        bool called = !mCalls.empty();
        std::optional<ArgTuple> result;
        if (called) {
            result.emplace(std::move(mCalls.front()));
            mCalls.pop_front();
        }
        return result;
    }

    // Waits using a small default timeout for when a call is not expected to be
    // made. The returned std::optional<std:tuple<...>> should not have a value
    // except if a set of arguments was unexpectedly received because a call was
    // actually made.
    //
    // Note this function uses a small timeout to not block test execution, and
    // it is possible the code under test could make the call AFTER the timeout
    // expires.
    std::optional<ArgTuple> waitForUnexpectedCall() { return waitForCall(UNEXPECTED_CALL_TIMEOUT); }

private:
    std::mutex mMutex;
    std::condition_variable mCondition;
    std::deque<ArgTuple> mCalls GUARDED_BY(mMutex);
};

// Like AsyncCallRecorder, but for when the function being invoked
// asynchronously is expected to return a value.
//
// This helper allows a single constant return value to be set to be returned by
// all calls that were made.
template <typename Func>
class AsyncCallRecorderWithCannedReturn;

template <typename Ret, typename... Args>
class AsyncCallRecorderWithCannedReturn<Ret (*)(Args...)>
      : public AsyncCallRecorder<void (*)(Args...)> {
public:
    explicit AsyncCallRecorderWithCannedReturn(Ret returnvalue) : mReturnValue(returnvalue) {}

    auto getInvocable() {
        return [this](Args... args) {
            this->recordCall(args...);
            return mReturnValue;
        };
    }

private:
    const Ret mReturnValue;
};

} // namespace android
Loading