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

Commit 322467f3 authored by Matt Buckley's avatar Matt Buckley Committed by Automerger Merge Worker
Browse files

Add unit tests for HintSessionWrapper am: 87765575

parents 72e621c8 87765575
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -717,6 +717,7 @@ cc_test {
        "tests/unit/EglManagerTests.cpp",
        "tests/unit/FatVectorTests.cpp",
        "tests/unit/GraphicsStatsServiceTests.cpp",
        "tests/unit/HintSessionWrapperTests.cpp",
        "tests/unit/JankTrackerTests.cpp",
        "tests/unit/FrameMetricsReporterTests.cpp",
        "tests/unit/LayerUpdateQueueTests.cpp",
+26 −60
Original line number Diff line number Diff line
@@ -32,65 +32,30 @@ namespace android {
namespace uirenderer {
namespace renderthread {

namespace {

typedef APerformanceHintManager* (*APH_getManager)();
typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
                                                      size_t, int64_t);
typedef void (*APH_closeSession)(APerformanceHintSession* session);
typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);

bool gAPerformanceHintBindingInitialized = false;
APH_getManager gAPH_getManagerFn = nullptr;
APH_createSession gAPH_createSessionFn = nullptr;
APH_closeSession gAPH_closeSessionFn = nullptr;
APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
APH_sendHint gAPH_sendHintFn = nullptr;

void ensureAPerformanceHintBindingInitialized() {
    if (gAPerformanceHintBindingInitialized) return;
#define BIND_APH_METHOD(name)                                         \
    name = (decltype(name))dlsym(handle_, "APerformanceHint_" #name); \
    LOG_ALWAYS_FATAL_IF(name == nullptr, "Failed to find required symbol APerformanceHint_" #name)

void HintSessionWrapper::HintSessionBinding::init() {
    if (mInitialized) return;

    void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
    LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");

    gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
    LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
                        "Failed to find required symbol APerformanceHint_getManager!");

    gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
    LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
                        "Failed to find required symbol APerformanceHint_createSession!");

    gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
    LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
                        "Failed to find required symbol APerformanceHint_closeSession!");

    gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
            handle_, "APerformanceHint_updateTargetWorkDuration");
    LOG_ALWAYS_FATAL_IF(
            gAPH_updateTargetWorkDurationFn == nullptr,
            "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
    BIND_APH_METHOD(getManager);
    BIND_APH_METHOD(createSession);
    BIND_APH_METHOD(closeSession);
    BIND_APH_METHOD(updateTargetWorkDuration);
    BIND_APH_METHOD(reportActualWorkDuration);
    BIND_APH_METHOD(sendHint);

    gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
            handle_, "APerformanceHint_reportActualWorkDuration");
    LOG_ALWAYS_FATAL_IF(
            gAPH_reportActualWorkDurationFn == nullptr,
            "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");

    gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
    LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
                        "Failed to find required symbol APerformanceHint_sendHint!");

    gAPerformanceHintBindingInitialized = true;
    mInitialized = true;
}

}  // namespace

HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
        : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {}
        : mUiThreadId(uiThreadId)
        , mRenderThreadId(renderThreadId)
        , mBinding(std::make_shared<HintSessionBinding>()) {}

HintSessionWrapper::~HintSessionWrapper() {
    destroy();
@@ -101,7 +66,7 @@ void HintSessionWrapper::destroy() {
        mHintSession = mHintSessionFuture.get();
    }
    if (mHintSession) {
        gAPH_closeSessionFn(mHintSession);
        mBinding->closeSession(mHintSession);
        mSessionValid = true;
        mHintSession = nullptr;
    }
@@ -133,9 +98,9 @@ bool HintSessionWrapper::init() {
    // Assume that if we return before the end, it broke
    mSessionValid = false;

    ensureAPerformanceHintBindingInitialized();
    mBinding->init();

    APerformanceHintManager* manager = gAPH_getManagerFn();
    APerformanceHintManager* manager = mBinding->getManager();
    if (!manager) return false;

    std::vector<pid_t> tids = CommonPool::getThreadIds();
@@ -145,8 +110,9 @@ bool HintSessionWrapper::init() {
    // Use a placeholder target value to initialize,
    // this will always be replaced elsewhere before it gets used
    int64_t defaultTargetDurationNanos = 16666667;
    mHintSessionFuture = CommonPool::async([=, tids = std::move(tids)] {
        return gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos);
    mHintSessionFuture = CommonPool::async([=, this, tids = std::move(tids)] {
        return mBinding->createSession(manager, tids.data(), tids.size(),
                                       defaultTargetDurationNanos);
    });
    return false;
}
@@ -158,7 +124,7 @@ void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos)
        targetWorkDurationNanos > kSanityCheckLowerBound &&
        targetWorkDurationNanos < kSanityCheckUpperBound) {
        mLastTargetWorkDuration = targetWorkDurationNanos;
        gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos);
        mBinding->updateTargetWorkDuration(mHintSession, targetWorkDurationNanos);
    }
    mLastFrameNotification = systemTime();
}
@@ -168,7 +134,7 @@ void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
    mResetsSinceLastReport = 0;
    if (actualDurationNanos > kSanityCheckLowerBound &&
        actualDurationNanos < kSanityCheckUpperBound) {
        gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
        mBinding->reportActualWorkDuration(mHintSession, actualDurationNanos);
    }
}

@@ -179,14 +145,14 @@ void HintSessionWrapper::sendLoadResetHint() {
    if (now - mLastFrameNotification > kResetHintTimeout &&
        mResetsSinceLastReport <= kMaxResetsSinceLastReport) {
        ++mResetsSinceLastReport;
        gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET));
        mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET));
    }
    mLastFrameNotification = now;
}

void HintSessionWrapper::sendLoadIncreaseHint() {
    if (!init()) return;
    gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_UP));
    mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_UP));
}

} /* namespace renderthread */
+24 −0
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ namespace renderthread {

class HintSessionWrapper {
public:
    friend class HintSessionWrapperTests;

    HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId);
    ~HintSessionWrapper();

@@ -55,6 +57,28 @@ private:
    static constexpr nsecs_t kResetHintTimeout = 100_ms;
    static constexpr int64_t kSanityCheckLowerBound = 100_us;
    static constexpr int64_t kSanityCheckUpperBound = 10_s;

    // Allows easier stub when testing
    class HintSessionBinding {
    public:
        virtual ~HintSessionBinding() = default;
        virtual void init();
        APerformanceHintManager* (*getManager)();
        APerformanceHintSession* (*createSession)(APerformanceHintManager* manager,
                                                  const int32_t* tids, size_t tidCount,
                                                  int64_t defaultTarget) = nullptr;
        void (*closeSession)(APerformanceHintSession* session) = nullptr;
        void (*updateTargetWorkDuration)(APerformanceHintSession* session,
                                         int64_t targetDuration) = nullptr;
        void (*reportActualWorkDuration)(APerformanceHintSession* session,
                                         int64_t actualDuration) = nullptr;
        void (*sendHint)(APerformanceHintSession* session, int32_t hintId) = nullptr;

    private:
        bool mInitialized = false;
    };

    std::shared_ptr<HintSessionBinding> mBinding;
};

} /* namespace renderthread */
+151 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include <private/performance_hint_private.h>
#include <renderthread/HintSessionWrapper.h>
#include <utils/Log.h>

#include <chrono>

#include "Properties.h"

using namespace testing;
using namespace std::chrono_literals;

APerformanceHintManager* managerPtr = reinterpret_cast<APerformanceHintManager*>(123);
APerformanceHintSession* sessionPtr = reinterpret_cast<APerformanceHintSession*>(456);
int uiThreadId = 1;
int renderThreadId = 2;

namespace android::uirenderer::renderthread {

class HintSessionWrapperTests : public testing::Test {
public:
    void SetUp() override;
    void TearDown() override;

protected:
    std::shared_ptr<HintSessionWrapper> mWrapper;

    class MockHintSessionBinding : public HintSessionWrapper::HintSessionBinding {
    public:
        void init() override;

        MOCK_METHOD(APerformanceHintManager*, fakeGetManager, ());
        MOCK_METHOD(APerformanceHintSession*, fakeCreateSession,
                    (APerformanceHintManager*, const int32_t*, size_t, int64_t));
        MOCK_METHOD(void, fakeCloseSession, (APerformanceHintSession*));
        MOCK_METHOD(void, fakeUpdateTargetWorkDuration, (APerformanceHintSession*, int64_t));
        MOCK_METHOD(void, fakeReportActualWorkDuration, (APerformanceHintSession*, int64_t));
        MOCK_METHOD(void, fakeSendHint, (APerformanceHintSession*, int32_t));
    };

    // Must be static so it can have function pointers we can point to with static methods
    static std::shared_ptr<MockHintSessionBinding> sMockBinding;

    // Must be static so we can point to them as normal fn pointers with HintSessionBinding
    static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); };
    static APerformanceHintSession* stubCreateSession(APerformanceHintManager* manager,
                                                      const int32_t* ids, size_t idsSize,
                                                      int64_t initialTarget) {
        return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
    }
    static APerformanceHintSession* stubSlowCreateSession(APerformanceHintManager* manager,
                                                          const int32_t* ids, size_t idsSize,
                                                          int64_t initialTarget) {
        std::this_thread::sleep_for(50ms);
        return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
    }
    static void stubCloseSession(APerformanceHintSession* session) {
        sMockBinding->fakeCloseSession(session);
    };
    static void stubUpdateTargetWorkDuration(APerformanceHintSession* session,
                                             int64_t workDuration) {
        sMockBinding->fakeUpdateTargetWorkDuration(session, workDuration);
    }
    static void stubReportActualWorkDuration(APerformanceHintSession* session,
                                             int64_t workDuration) {
        sMockBinding->fakeReportActualWorkDuration(session, workDuration);
    }
    static void stubSendHint(APerformanceHintSession* session, int32_t hintId) {
        sMockBinding->fakeSendHint(session, hintId);
    };
    void waitForWrapperReady() { mWrapper->mHintSessionFuture.wait(); }
};

std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding>
        HintSessionWrapperTests::sMockBinding;

void HintSessionWrapperTests::SetUp() {
    // Pretend it's supported even if we're in an emulator
    Properties::useHintManager = true;
    sMockBinding = std::make_shared<NiceMock<MockHintSessionBinding>>();
    mWrapper = std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId);
    mWrapper->mBinding = sMockBinding;
    EXPECT_CALL(*sMockBinding, fakeGetManager).WillOnce(Return(managerPtr));
    ON_CALL(*sMockBinding, fakeCreateSession).WillByDefault(Return(sessionPtr));
}

void HintSessionWrapperTests::MockHintSessionBinding::init() {
    sMockBinding->getManager = &stubGetManager;
    if (sMockBinding->createSession == nullptr) {
        sMockBinding->createSession = &stubCreateSession;
    }
    sMockBinding->closeSession = &stubCloseSession;
    sMockBinding->updateTargetWorkDuration = &stubUpdateTargetWorkDuration;
    sMockBinding->reportActualWorkDuration = &stubReportActualWorkDuration;
    sMockBinding->sendHint = &stubSendHint;
}

void HintSessionWrapperTests::TearDown() {
    mWrapper = nullptr;
    sMockBinding = nullptr;
}

TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) {
    EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
    sMockBinding->createSession = stubSlowCreateSession;
    mWrapper->init();
    mWrapper = nullptr;
}

TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) {
    EXPECT_CALL(*sMockBinding, fakeCreateSession(managerPtr, _, Gt(1), _)).Times(1);
    mWrapper->init();
    waitForWrapperReady();
}

TEST_F(HintSessionWrapperTests, loadUpHintsSendCorrectly) {
    EXPECT_CALL(*sMockBinding,
                fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
            .Times(1);
    mWrapper->init();
    waitForWrapperReady();
    mWrapper->sendLoadIncreaseHint();
}

TEST_F(HintSessionWrapperTests, loadResetHintsSendCorrectly) {
    EXPECT_CALL(*sMockBinding,
                fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET)))
            .Times(1);
    mWrapper->init();
    waitForWrapperReady();
    mWrapper->sendLoadResetHint();
}

}  // namespace android::uirenderer::renderthread
 No newline at end of file