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

Commit 550dbaa5 authored by Jim Shargo's avatar Jim Shargo
Browse files

libgui: Add a test server utility for unit tests.

This is being added to make it possible to test linkToDeath callbacks,
but may serve to be generally useful.

This is a complicated little thing. But it has to be this way.

Most of the complication comes from the fact that we want ephemeral
Binder services that we can kill at will on a per-test basis. Binder
doesn't let you do any binder stuff after you've forked, however, so we
needed an intermediary, non-binder process to kick off the binder
processes that actually run the test server.

The way this works is:

- libgui_test defaults to being a normal test, and intializes a
  `TestServerHost` via fork/exec of the libgui_test binary
- The first fork runs TestServerHostMain and waits on a pipe for
  commands to create servers, at which point it will fork/exec
- Each forked server creates a new ITestService service, which the
  original test connects to.

BYPASS_IGBP_IGBC_API_REASON=warren buffers

Bug: 340933794
Flag: EXEMPT test
Test: test library
Change-Id: I37ee51b13a895a8b3387b7f2db92e5069158ff46
parent 785d2e2a
Loading
Loading
Loading
Loading
+45 −6
Original line number Diff line number Diff line
@@ -12,6 +12,34 @@ package {
    default_applicable_licenses: ["frameworks_native_license"],
}

aidl_interface {
    name: "libgui_test_server_aidl",
    unstable: true,
    srcs: ["testserver/aidl/**/*.aidl"],
    local_include_dir: "testserver/aidl",
    include_dirs: [
        "frameworks/native/aidl/gui",
    ],
    backend: {
        cpp: {
            enabled: true,
            additional_shared_libraries: [
                "libgui",
                "libui",
            ],
        },
        java: {
            enabled: false,
        },
        ndk: {
            enabled: false,
        },
        rust: {
            enabled: false,
        },
    },
}

cc_test {
    name: "libgui_test",
    test_suites: ["device-tests"],
@@ -30,7 +58,6 @@ cc_test {
    ],

    srcs: [
        "LibGuiMain.cpp", // Custom gtest entrypoint
        "BLASTBufferQueue_test.cpp",
        "BufferItemConsumer_test.cpp",
        "BufferQueue_test.cpp",
@@ -38,24 +65,29 @@ cc_test {
        "Choreographer_test.cpp",
        "CompositorTiming_test.cpp",
        "CpuConsumer_test.cpp",
        "EndToEndNativeInputTest.cpp",
        "FrameRateUtilsTest.cpp",
        "DisplayInfo_test.cpp",
        "DisplayedContentSampling_test.cpp",
        "DisplayInfo_test.cpp",
        "EndToEndNativeInputTest.cpp",
        "FillBuffer.cpp",
        "FrameRateUtilsTest.cpp",
        "GLTest.cpp",
        "IGraphicBufferProducer_test.cpp",
        "LibGuiMain.cpp", // Custom gtest entrypoint
        "Malicious.cpp",
        "MultiTextureConsumer_test.cpp",
        "RegionSampling_test.cpp",
        "StreamSplitter_test.cpp",
        "Surface_test.cpp",
        "SurfaceTextureClient_test.cpp",
        "SurfaceTextureFBO_test.cpp",
        "SurfaceTextureGL_test.cpp",
        "SurfaceTextureGLThreadToGL_test.cpp",
        "SurfaceTextureGLToGL_test.cpp",
        "SurfaceTextureGL_test.cpp",
        "SurfaceTextureMultiContextGL_test.cpp",
        "Surface_test.cpp",
        "TestServer_test.cpp",
        "testserver/TestServer.cpp",
        "testserver/TestServerClient.cpp",
        "testserver/TestServerHost.cpp",
        "TextureRenderer.cpp",
        "VsyncEventData_test.cpp",
        "WindowInfo_test.cpp",
@@ -66,10 +98,17 @@ cc_test {
        "android.hardware.configstore-utils",
        "libSurfaceFlingerProp",
        "libGLESv1_CM",
        "libgui_test_server_aidl-cpp",
        "libinput",
        "libnativedisplay",
    ],

    // This needs to get copied over for the test since it's not part of the
    // platform.
    data_libs: [
        "libgui_test_server_aidl-cpp",
    ],

    static_libs: [
        "libgmock",
    ],
+36 −2
Original line number Diff line number Diff line
@@ -14,8 +14,15 @@
 * limitations under the License.
 */

#include "gtest/gtest.h"
#include "log/log.h"
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <log/log.h>

#include "testserver/TestServer.h"
#include "testserver/TestServerClient.h"
#include "testserver/TestServerHost.h"

using namespace android;

namespace {

@@ -32,7 +39,34 @@ class TestCaseLogger : public ::testing::EmptyTestEventListener {
} // namespace

int main(int argc, char** argv) {
    // There are three modes that we can run in to support the libgui TestServer:
    //
    // - libgui_test : normal mode, runs tests and fork/execs the testserver host process
    // - libgui_test --test-server-host $recvPipeFd $sendPipeFd : TestServerHost mode, listens on
    //   $recvPipeFd for commands and sends responses over $sendPipeFd
    // - libgui_test --test-server $name : TestServer mode, starts a ITestService binder service
    //   under $name
    for (int i = 1; i < argc; i++) {
        std::string arg = argv[i];
        if (arg == "--test-server-host") {
            LOG_ALWAYS_FATAL_IF(argc < (i + 2), "--test-server-host requires two pipe fds");
            // Note that the send/recv are from our perspective.
            base::unique_fd recvPipeFd = base::unique_fd(atoi(argv[i + 1]));
            base::unique_fd sendPipeFd = base::unique_fd(atoi(argv[i + 2]));
            return TestServerHostMain(argv[0], std::move(sendPipeFd), std::move(recvPipeFd));
        }
        if (arg == "--test-server") {
            LOG_ALWAYS_FATAL_IF(argc < (i + 1), "--test-server requires a name");
            return TestServerMain(argv[i + 1]);
        }
    }
    testing::InitGoogleTest(&argc, argv);
    testing::UnitTest::GetInstance()->listeners().Append(new TestCaseLogger());

    // This has to be run *before* any test initialization, because it fork/execs a TestServerHost,
    // which will later create new binder service. You can't do that in a forked thread after you've
    // initialized any binder stuff, which some tests do.
    TestServerClient::InitializeOrDie(argv[0]);

    return RUN_ALL_TESTS();
}
 No newline at end of file
+2 −0
Original line number Diff line number Diff line
@@ -54,6 +54,8 @@
#include <limits>
#include <thread>

#include "testserver/TestServerClient.h"

namespace android {

using namespace std::chrono_literals;
+99 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 <gtest/gtest.h>

#include <SurfaceFlingerProperties.h>
#include <android/gui/IDisplayEventConnection.h>
#include <android/gui/ISurfaceComposer.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware_buffer.h>
#include <binder/ProcessState.h>
#include <com_android_graphics_libgui_flags.h>
#include <configstore/Utils.h>
#include <gui/AidlStatusUtil.h>
#include <gui/BufferItemConsumer.h>
#include <gui/BufferQueue.h>
#include <gui/CpuConsumer.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SyncScreenCaptureListener.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <sys/types.h>
#include <system/window.h>
#include <ui/BufferQueueDefs.h>
#include <ui/DisplayMode.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
#include <utils/Errors.h>
#include <utils/String8.h>

#include <cstddef>
#include <limits>
#include <thread>

#include "binder/IInterface.h"
#include "testserver/TestServerClient.h"

namespace android {

namespace {

class TestServerTest : public ::testing::Test {
protected:
    TestServerTest() { ProcessState::self()->startThreadPool(); }
};

} // namespace

TEST_F(TestServerTest, Create) {
    EXPECT_NE(nullptr, TestServerClient::Create());
}

TEST_F(TestServerTest, CreateProducer) {
    sp<TestServerClient> client = TestServerClient::Create();
    EXPECT_NE(nullptr, client->CreateProducer());
}

TEST_F(TestServerTest, KillServer) {
    class DeathWaiter : public IBinder::DeathRecipient {
    public:
        virtual void binderDied(const wp<IBinder>&) override { mPromise.set_value(true); }
        std::future<bool> getFuture() { return mPromise.get_future(); }

        std::promise<bool> mPromise;
    };

    sp<TestServerClient> client = TestServerClient::Create();
    sp<IGraphicBufferProducer> producer = client->CreateProducer();
    EXPECT_NE(nullptr, producer);

    sp<DeathWaiter> deathWaiter = sp<DeathWaiter>::make();
    EXPECT_EQ(OK, IInterface::asBinder(producer)->linkToDeath(deathWaiter));

    auto deathWaiterFuture = deathWaiter->getFuture();
    EXPECT_EQ(OK, client->Kill());
    EXPECT_EQ(nullptr, client->CreateProducer());

    EXPECT_TRUE(deathWaiterFuture.get());
}

} // namespace android
+114 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

#define LOG_TAG "TestServer"

#include <android-base/stringprintf.h>
#include <binder/IInterface.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <binder/Status.h>
#include <gui/BufferQueue.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/view/Surface.h>
#include <libgui_test_server/BnTestServer.h>
#include <log/log.h>
#include <utils/Errors.h>

#include <cstdint>
#include <cstdlib>
#include <memory>
#include <mutex>
#include <vector>

#include <fcntl.h>
#include <unistd.h>

#include "TestServer.h"

namespace android {

namespace {
class TestConsumerListener : public BnConsumerListener {
    virtual void onFrameAvailable(const BufferItem&) override {}
    virtual void onBuffersReleased() override {}
    virtual void onSidebandStreamChanged() override {}
};

class TestServiceImpl : public libgui_test_server::BnTestServer {
public:
    TestServiceImpl(const char* name) : mName(name) {}

    virtual binder::Status createProducer(view::Surface* out) override {
        std::lock_guard<std::mutex> lock(mMutex);

        BufferQueueHolder bq;
        BufferQueue::createBufferQueue(&bq.producer, &bq.consumer);
        sp<TestConsumerListener> listener = sp<TestConsumerListener>::make();
        bq.consumer->consumerConnect(listener, /*controlledByApp*/ true);

        uint64_t id = 0;
        bq.producer->getUniqueId(&id);
        std::string name = base::StringPrintf("%s-%" PRIu64, mName, id);

        out->name = String16(name.c_str());
        out->graphicBufferProducer = bq.producer;
        mBqs.push_back(std::move(bq));

        return binder::Status::ok();
    }

    virtual binder::Status killNow() override {
        ALOGE("LibGUI Test Service %s dying in response to killNow", mName);
        _exit(0);
        // Not reached:
        return binder::Status::ok();
    }

private:
    std::mutex mMutex;
    const char* mName;

    struct BufferQueueHolder {
        sp<IGraphicBufferProducer> producer;
        sp<IGraphicBufferConsumer> consumer;
    };

    std::vector<BufferQueueHolder> mBqs;
};
} // namespace

int TestServerMain(const char* name) {
    ProcessState::self()->startThreadPool();

    sp<TestServiceImpl> testService = sp<TestServiceImpl>::make(name);
    ALOGE("service");
    sp<IServiceManager> serviceManager(defaultServiceManager());
    LOG_ALWAYS_FATAL_IF(OK != serviceManager->addService(String16(name), testService));

    ALOGD("LibGUI Test Service %s STARTED", name);

    IPCThreadState::self()->joinThreadPool();

    ALOGW("LibGUI Test Service %s DIED", name);

    return 0;
}

} // namespace android
 No newline at end of file
Loading