diff --git a/.ci/Makefile b/.ci/Makefile index 7acb30b..4ebce27 100644 --- a/.ci/Makefile +++ b/.ci/Makefile @@ -99,6 +99,7 @@ clean: # IStats includes are not available in aospless SKIP_FILES := \ compositor/FlatteningControllerTests.cpp \ + drm/DrmDisplayPipelineTest.cpp \ stats/CompositionStatsTest.cpp \ stats/CompositionStatsAtomReporterDesktop.cpp \ diff --git a/Android.bp b/Android.bp index 6064664..fe60f59 100644 --- a/Android.bp +++ b/Android.bp @@ -275,15 +275,34 @@ apex { cc_test_host { name: "drm_hwcomposer_unittests", srcs: [ + "bufferinfo/BufferInfoGetter.cpp", "compositor/FlatteningController.cpp", "compositor/FlatteningControllerTests.cpp", + "drm/DrmAtomicStateManager.cpp", + "drm/DrmConnector.cpp", + "drm/DrmCrtc.cpp", + "drm/DrmDevice.cpp", + "drm/DrmDisplayPipeline.cpp", + "drm/DrmDisplayPipelineTest.cpp", + "drm/DrmEncoder.cpp", + "drm/DrmMode.cpp", + "drm/DrmPlane.cpp", + "drm/DrmProperty.cpp", "stats/CompositionStats.cpp", "stats/CompositionStatsTest.cpp", + "utils/fd.cpp", + "utils/properties.cpp", ], static_libs: [ "libbase", + "libcutils", + "libdrm", "libgmock", "libgtest", + "liblog", + ], + shared_libs: [ + "libhardware", ], } diff --git a/drm/DrmDisplayPipeline.cpp b/drm/DrmDisplayPipeline.cpp index dee11ed..8a93f9e 100644 --- a/drm/DrmDisplayPipeline.cpp +++ b/drm/DrmDisplayPipeline.cpp @@ -29,25 +29,6 @@ namespace android::drm_hwcomposer { -template -auto PipelineBindable::BindPipeline(const DrmDisplayPipeline *pipeline, - bool return_object_if_bound) - -> std::shared_ptr> { - auto owner_object = owner_object_.lock(); - if (owner_object) { - if (bound_pipeline_ == pipeline && return_object_if_bound) { - return owner_object; - } - - return {}; - } - owner_object = std::make_shared>(static_cast(this)); - - owner_object_ = owner_object; - bound_pipeline_ = pipeline; - return owner_object; -} - static auto TryCreatePipeline(DrmDevice &dev, DrmConnector &connector, DrmEncoder &enc, DrmCrtc &crtc) -> std::unique_ptr { diff --git a/drm/DrmDisplayPipeline.h b/drm/DrmDisplayPipeline.h index c1135a3..adc003f 100644 --- a/drm/DrmDisplayPipeline.h +++ b/drm/DrmDisplayPipeline.h @@ -42,9 +42,24 @@ class PipelineBindable { return bound_pipeline_; } + // Header implementation required for template instantiation. auto BindPipeline(const DrmDisplayPipeline *pipeline, bool return_object_if_bound = false) - -> std::shared_ptr>; + -> std::shared_ptr> { + auto owner_object = owner_object_.lock(); + if (owner_object) { + if (bound_pipeline_ == pipeline && return_object_if_bound) { + return owner_object; + } + + return {}; + } + owner_object = std::make_shared>(static_cast(this)); + + owner_object_ = owner_object; + bound_pipeline_ = pipeline; + return owner_object; + } private: const DrmDisplayPipeline *bound_pipeline_; diff --git a/drm/DrmDisplayPipelineTest.cpp b/drm/DrmDisplayPipelineTest.cpp new file mode 100644 index 0000000..07a98af --- /dev/null +++ b/drm/DrmDisplayPipelineTest.cpp @@ -0,0 +1,90 @@ +/* + * 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 + +#include + +#include "DrmDisplayPipeline.h" + +namespace android::drm_hwcomposer { + +class NoOpBindable : public PipelineBindable {}; + +class DrmDisplayPipelineTest : public ::testing::Test {}; + +TEST_F(DrmDisplayPipelineTest, BindPipeline_Success) { + const auto pipeline = std::make_unique(); + 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 = std::make_unique(); + 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 = std::make_unique(); + const auto pipeline2 = std::make_unique(); + 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 = std::make_unique(); + const auto pipeline2 = std::make_unique(); + 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); +} + +} // namespace android::drm_hwcomposer