1
0
Fork 0
android_external_drm_hwcomp.../drm/DrmDisplayPipelineTest.cpp
Andrew Wolfers 5a9cc2cba3 Cleanup includes in drm/ per IWYU style
Change-Id: I613cb6656d887aab5221be3d3b2923f1f4ba9ab9
2025-11-11 19:14:37 +00:00

345 lines
10 KiB
C++

/*
* Copyright (C) 2025 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 <cstdint>
#include <memory>
#include <gtest/gtest.h>
#include <xf86drmMode.h>
#include "drm/DrmConnector.h"
#include "drm/DrmCrtc.h"
#include "drm/DrmDevice.h"
#include "drm/DrmDisplayPipeline.h"
#include "drm/DrmEncoder.h"
#include "drm/DrmPlane.h"
namespace android::drm_hwcomposer {
class NoOpBindable : public PipelineBindable<NoOpBindable> {};
class FakeDrmConnector : public DrmConnector {
public:
FakeDrmConnector(DrmDevice* dev, uint32_t encoder_id)
: DrmConnector(nullptr, dev, 0), encoder_id_(encoder_id) {
}
uint32_t GetCurrentEncoderId() const override {
return encoder_id_;
}
private:
const uint32_t encoder_id_;
};
class FakeDrmEncoder : public DrmEncoder {
public:
FakeDrmEncoder(uint32_t id, uint32_t crtc_id)
: DrmEncoder(nullptr, 0), id_(id), crtc_id_(crtc_id) {
}
uint32_t GetId() const override {
return id_;
}
uint32_t GetCurrentCrtcId() const override {
return crtc_id_;
}
private:
const uint32_t id_;
const uint32_t crtc_id_;
};
class FakeDrmCrtc : public DrmCrtc {
public:
explicit FakeDrmCrtc(uint32_t id) : DrmCrtc(nullptr, 0), id_(id) {
}
uint32_t GetId() const override {
return id_;
}
private:
const uint32_t id_;
};
class FakeDrmPlane : public DrmPlane {
public:
FakeDrmPlane(DrmDevice& dev, uint32_t type) : DrmPlane(dev, nullptr) {
type_ = type;
}
bool IsCrtcSupported(const DrmCrtc& crtc) const override {
return true;
}
};
class FakeDrmDevice : public DrmDevice {
public:
FakeDrmDevice() : DrmDevice(nullptr, 0) {
}
void AddConnector(std::unique_ptr<DrmConnector> connector) {
connectors_.emplace_back(std::move(connector));
}
void AddEncoder(std::unique_ptr<DrmEncoder> encoder) {
encoders_.emplace_back(std::move(encoder));
}
void AddCrtc(std::unique_ptr<DrmCrtc> crtc) {
crtcs_.emplace_back(std::move(crtc));
}
void AddPlane(std::unique_ptr<DrmPlane> plane) {
planes_.emplace_back(std::move(plane));
}
void AddPipelineResources() {
AddEncoder(std::make_unique<FakeDrmEncoder>(/*id=*/next_id_,
/*crtc_id=*/next_id_));
AddCrtc(std::make_unique<FakeDrmCrtc>(/*id=*/next_id_));
AddConnector(std::make_unique<FakeDrmConnector>(this,
/*encoder_id=*/next_id_));
AddPlane(std::make_unique<FakeDrmPlane>(*this, DRM_PLANE_TYPE_PRIMARY));
++next_id_;
}
private:
uint32_t next_id_ = 0;
};
class DrmDisplayPipelineTest : public ::testing::Test {
public:
void SetUp() override {
fake_device_ = std::make_unique<FakeDrmDevice>();
}
void TearDown() override {
fake_device_.reset();
}
std::unique_ptr<DrmDisplayPipeline> CreatePipeline() {
fake_device_->AddPipelineResources();
return DrmDisplayPipeline::CreatePipeline(
*fake_device_->GetConnectors().back());
}
void AddPlane(uint32_t type) {
fake_device_->AddPlane(std::make_unique<FakeDrmPlane>(*fake_device_, type));
}
private:
std::unique_ptr<FakeDrmDevice> fake_device_ = nullptr;
};
TEST_F(DrmDisplayPipelineTest, BindPipeline_Success) {
const auto pipeline = CreatePipeline();
NoOpBindable bindable;
const auto binding = bindable.BindPipeline(pipeline.get(),
/*return_object_if_bound=*/true);
ASSERT_NE(binding, nullptr);
EXPECT_EQ(binding->Get()->GetPipeline(), pipeline.get());
EXPECT_EQ(binding->Get(), &bindable);
}
TEST_F(DrmDisplayPipelineTest, BindPipeline_SamePipelineReturnsSameBinding) {
const auto pipeline = CreatePipeline();
NoOpBindable bindable;
const auto binding1 = bindable.BindPipeline(pipeline.get(),
/*return_object_if_bound=*/true);
ASSERT_NE(binding1, nullptr);
EXPECT_EQ(binding1->Get()->GetPipeline(), pipeline.get());
EXPECT_EQ(binding1->Get(), &bindable);
const auto binding2 = bindable.BindPipeline(pipeline.get(),
/*return_object_if_bound=*/true);
EXPECT_EQ(binding1, binding2);
}
TEST_F(DrmDisplayPipelineTest,
BindPipeline_DifferentPipelineReturnsNullBinding) {
const auto pipeline1 = CreatePipeline();
const auto pipeline2 = CreatePipeline();
NoOpBindable bindable;
const auto binding1 = bindable.BindPipeline(pipeline1.get(),
/*return_object_if_bound=*/true);
ASSERT_NE(binding1, nullptr);
EXPECT_EQ(binding1->Get()->GetPipeline(), pipeline1.get());
EXPECT_EQ(binding1->Get(), &bindable);
const auto binding2 = bindable.BindPipeline(pipeline2.get(),
/*return_object_if_bound=*/true);
EXPECT_EQ(binding2, nullptr);
}
TEST_F(DrmDisplayPipelineTest,
BindPipeline_ReleasedBindingDoesntPreventNewBinding) {
const auto pipeline1 = CreatePipeline();
const auto pipeline2 = CreatePipeline();
NoOpBindable bindable;
auto binding1 = bindable.BindPipeline(pipeline1.get(),
/*return_object_if_bound=*/true);
ASSERT_NE(binding1, nullptr);
EXPECT_EQ(binding1->Get()->GetPipeline(), pipeline1.get());
EXPECT_EQ(binding1->Get(), &bindable);
binding1.reset();
const auto binding2 = bindable.BindPipeline(pipeline2.get(),
/*return_object_if_bound=*/true);
ASSERT_NE(binding2, nullptr);
EXPECT_EQ(binding2->Get()->GetPipeline(), pipeline2.get());
EXPECT_EQ(binding2->Get(), &bindable);
}
TEST_F(DrmDisplayPipelineTest, CreatePipeline_Success) {
const auto fake_device = std::make_unique<FakeDrmDevice>();
fake_device->AddPipelineResources();
EXPECT_NE(DrmDisplayPipeline::CreatePipeline(
*fake_device->GetConnectors().back()),
nullptr);
}
TEST_F(DrmDisplayPipelineTest, CreatePipeline_TwoBindingSuccess) {
const auto fake_device = std::make_unique<FakeDrmDevice>();
fake_device->AddPipelineResources();
const auto pipeline1 = DrmDisplayPipeline::CreatePipeline(
*fake_device->GetConnectors().back());
EXPECT_NE(pipeline1, nullptr);
fake_device->AddPipelineResources();
const auto pipeline2 = DrmDisplayPipeline::CreatePipeline(
*fake_device->GetConnectors().back());
EXPECT_NE(pipeline2, nullptr);
EXPECT_NE(pipeline1, pipeline2);
}
TEST_F(DrmDisplayPipelineTest, CreatePipeline_AlreadyBoundFailure) {
const auto fake_device = std::make_unique<FakeDrmDevice>();
fake_device->AddPipelineResources();
const auto pipeline1 = DrmDisplayPipeline::CreatePipeline(
*fake_device->GetConnectors().back());
EXPECT_NE(pipeline1, nullptr);
EXPECT_DEATH(DrmDisplayPipeline::CreatePipeline(
*fake_device->GetConnectors().back()),
"");
}
TEST_F(DrmDisplayPipelineTest, CreatePipeline_ReleasedBindingSuccess) {
const auto fake_device = std::make_unique<FakeDrmDevice>();
fake_device->AddPipelineResources();
auto pipeline1 = DrmDisplayPipeline::CreatePipeline(
*fake_device->GetConnectors().back());
EXPECT_NE(pipeline1, nullptr);
pipeline1.reset();
EXPECT_NE(DrmDisplayPipeline::CreatePipeline(
*fake_device->GetConnectors().back()),
nullptr);
}
TEST_F(DrmDisplayPipelineTest, CreatePipeline_NoPrimaryPlaneFailure) {
const auto fake_device = std::make_unique<FakeDrmDevice>();
fake_device->AddEncoder(std::make_unique<FakeDrmEncoder>(/*id=*/0,
/*crtc_id=*/0));
fake_device->AddCrtc(std::make_unique<FakeDrmCrtc>(/*id=*/0));
fake_device->AddConnector(
std::make_unique<FakeDrmConnector>(fake_device.get(),
/*encoder_id=*/0));
fake_device->AddPlane(
std::make_unique<FakeDrmPlane>(*fake_device, DRM_PLANE_TYPE_OVERLAY));
EXPECT_DEATH(DrmDisplayPipeline::CreatePipeline(
*fake_device->GetConnectors().back()),
"Primary plane for CRTC 0 not found");
}
TEST_F(DrmDisplayPipelineTest, GetUsablePlanes_Success) {
const auto pipeline = CreatePipeline();
ASSERT_NE(pipeline, nullptr);
AddPlane(DRM_PLANE_TYPE_OVERLAY);
AddPlane(DRM_PLANE_TYPE_CURSOR);
const auto [usable_planes, cursor_plane] = pipeline->GetUsablePlanes();
EXPECT_EQ(usable_planes.size(), 2);
EXPECT_NE(cursor_plane, nullptr);
}
TEST_F(DrmDisplayPipelineTest, GetUsablePlanes_SamePipeline) {
const auto pipeline = CreatePipeline();
ASSERT_NE(pipeline, nullptr);
AddPlane(DRM_PLANE_TYPE_OVERLAY);
AddPlane(DRM_PLANE_TYPE_CURSOR);
const auto usable_planes_1 = pipeline->GetUsablePlanes();
const auto usable_planes_2 = pipeline->GetUsablePlanes();
EXPECT_EQ(usable_planes_1, usable_planes_2);
}
TEST_F(DrmDisplayPipelineTest, GetUsablePlanes_SharedPlanesAlreadyBound) {
const auto pipeline1 = CreatePipeline();
ASSERT_NE(pipeline1, nullptr);
const auto pipeline2 = CreatePipeline();
ASSERT_NE(pipeline2, nullptr);
AddPlane(DRM_PLANE_TYPE_OVERLAY);
AddPlane(DRM_PLANE_TYPE_CURSOR);
const auto [usable_planes_1, cursor_plane_1] = pipeline1->GetUsablePlanes();
EXPECT_EQ(usable_planes_1.size(), 2);
EXPECT_NE(cursor_plane_1, nullptr);
const auto [usable_planes_2, cursor_plane_2] = pipeline2->GetUsablePlanes();
ASSERT_EQ(usable_planes_2.size(), 1);
EXPECT_EQ(usable_planes_2[0]->Get()->GetType(), DRM_PLANE_TYPE_PRIMARY);
EXPECT_EQ(cursor_plane_2, nullptr);
}
TEST_F(DrmDisplayPipelineTest, GetUsablePlanes_SharedPlaneBindingsReleased) {
const auto pipeline1 = CreatePipeline();
ASSERT_NE(pipeline1, nullptr);
const auto pipeline2 = CreatePipeline();
ASSERT_NE(pipeline2, nullptr);
AddPlane(DRM_PLANE_TYPE_OVERLAY);
AddPlane(DRM_PLANE_TYPE_CURSOR);
{
const auto [usable_planes, cursor_plane] = pipeline1->GetUsablePlanes();
EXPECT_EQ(usable_planes.size(), 2);
EXPECT_NE(cursor_plane, nullptr);
}
{
const auto [usable_planes, cursor_plane] = pipeline2->GetUsablePlanes();
EXPECT_EQ(usable_planes.size(), 2);
EXPECT_NE(cursor_plane, nullptr);
}
}
} // namespace android::drm_hwcomposer