drm_hwcomposer: Add tests for FlatteningController
Add gtest unittests to validate expected behaviour of FlatteningController. Change-Id: Ia40d232c7a72912df9480bd82adfe3315baaa493
This commit is contained in:
parent
2432cb0dfa
commit
c50f6851d6
3 changed files with 203 additions and 1 deletions
|
|
@ -97,7 +97,10 @@ clean:
|
|||
# Build
|
||||
# clang-tidy picks up gtest and gmock headers
|
||||
# IStats includes are not available in aospless
|
||||
SKIP_FILES := stats/CompositionStatsTest.cpp stats/CompositionStatsAtomReporterDesktop.cpp
|
||||
SKIP_FILES := \
|
||||
compositor/FlatteningControllerTests.cpp \
|
||||
stats/CompositionStatsTest.cpp \
|
||||
stats/CompositionStatsAtomReporterDesktop.cpp \
|
||||
|
||||
BUILD_FILES_AUTO := $(shell find -L $(SRC_DIR) -not -path '*/\.*' -not -path '*/tests/test_include/*' -path '*.cpp')
|
||||
SKIP_FILES_path := $(foreach file,$(SKIP_FILES),$(SRC_DIR)/$(file))
|
||||
|
|
|
|||
|
|
@ -274,10 +274,13 @@ apex {
|
|||
cc_test_host {
|
||||
name: "drm_hwcomposer_unittests",
|
||||
srcs: [
|
||||
"compositor/FlatteningController.cpp",
|
||||
"compositor/FlatteningControllerTests.cpp",
|
||||
"stats/CompositionStats.cpp",
|
||||
"stats/CompositionStatsTest.cpp",
|
||||
],
|
||||
static_libs: [
|
||||
"libbase",
|
||||
"libgmock",
|
||||
"libgtest",
|
||||
],
|
||||
|
|
|
|||
196
compositor/FlatteningControllerTests.cpp
Normal file
196
compositor/FlatteningControllerTests.cpp
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* 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 <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "compositor/FlatteningController.h"
|
||||
|
||||
using ::testing::StrictMock;
|
||||
|
||||
namespace android::drm_hwcomposer {
|
||||
|
||||
namespace {
|
||||
constexpr auto kTestTimeout = std::chrono::milliseconds(100);
|
||||
constexpr auto kTimeoutEpsilon = std::chrono::milliseconds(50);
|
||||
} // namespace
|
||||
|
||||
class MockFlatConCallbacks {
|
||||
public:
|
||||
MOCK_METHOD(void, Trigger, ());
|
||||
};
|
||||
|
||||
class FlatteningControllerTest : public ::testing::Test {
|
||||
protected:
|
||||
static void EmptyTrigger() {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(FlatteningControllerTest, DisabledOnCreation) {
|
||||
FlatConCallbacks cbks = {.trigger = EmptyTrigger};
|
||||
auto flat_con = FlatteningController::CreateInstance(cbks, kTestTimeout);
|
||||
|
||||
EXPECT_FALSE(flat_con->ShouldFlatten());
|
||||
|
||||
std::this_thread::sleep_for(kTestTimeout + kTimeoutEpsilon);
|
||||
|
||||
EXPECT_FALSE(flat_con->ShouldFlatten());
|
||||
}
|
||||
|
||||
TEST_F(FlatteningControllerTest, EnabledAfterNewFrame) {
|
||||
FlatConCallbacks cbks = {.trigger = EmptyTrigger};
|
||||
auto flat_con = FlatteningController::CreateInstance(cbks, kTestTimeout);
|
||||
|
||||
flat_con->NewFrame();
|
||||
EXPECT_FALSE(flat_con->ShouldFlatten());
|
||||
|
||||
std::this_thread::sleep_for(kTestTimeout + kTimeoutEpsilon);
|
||||
|
||||
EXPECT_TRUE(flat_con->ShouldFlatten());
|
||||
}
|
||||
|
||||
TEST_F(FlatteningControllerTest, DisabledAfterCallingDisable) {
|
||||
FlatConCallbacks cbks = {.trigger = EmptyTrigger};
|
||||
auto flat_con = FlatteningController::CreateInstance(cbks, kTestTimeout);
|
||||
|
||||
flat_con->NewFrame();
|
||||
std::this_thread::sleep_for(kTestTimeout + kTimeoutEpsilon);
|
||||
EXPECT_TRUE(flat_con->ShouldFlatten());
|
||||
|
||||
flat_con->Disable();
|
||||
EXPECT_FALSE(flat_con->ShouldFlatten());
|
||||
|
||||
std::this_thread::sleep_for(kTestTimeout + kTimeoutEpsilon);
|
||||
EXPECT_FALSE(flat_con->ShouldFlatten());
|
||||
}
|
||||
|
||||
TEST_F(FlatteningControllerTest, TriggersCallbackAfterTimeout) {
|
||||
StrictMock<MockFlatConCallbacks> mock_cb;
|
||||
FlatConCallbacks cbks = {.trigger = [&]() { mock_cb.Trigger(); }};
|
||||
auto flat_con = FlatteningController::CreateInstance(cbks, kTestTimeout);
|
||||
|
||||
EXPECT_CALL(mock_cb, Trigger()).Times(1);
|
||||
|
||||
flat_con->NewFrame();
|
||||
|
||||
std::this_thread::sleep_for(kTestTimeout + kTimeoutEpsilon);
|
||||
}
|
||||
|
||||
TEST_F(FlatteningControllerTest, ShouldFlattenAfterFirstNewFrame) {
|
||||
StrictMock<MockFlatConCallbacks> mock_cb;
|
||||
FlatConCallbacks cbks = {.trigger = [&]() { mock_cb.Trigger(); }};
|
||||
auto flat_con = FlatteningController::CreateInstance(cbks, kTestTimeout);
|
||||
|
||||
EXPECT_CALL(mock_cb, Trigger()).Times(1);
|
||||
|
||||
// Enable the controller and start the timer.
|
||||
flat_con->NewFrame();
|
||||
|
||||
std::this_thread::sleep_for(kTestTimeout + kTimeoutEpsilon);
|
||||
EXPECT_TRUE(flat_con->ShouldFlatten());
|
||||
|
||||
// First NewFrame is in response to the refresh callback.
|
||||
flat_con->NewFrame();
|
||||
EXPECT_TRUE(flat_con->ShouldFlatten());
|
||||
}
|
||||
|
||||
TEST_F(FlatteningControllerTest, ShouldNotFlattenAfterSecondNewFrame) {
|
||||
StrictMock<MockFlatConCallbacks> mock_cb;
|
||||
FlatConCallbacks cbks = {.trigger = [&]() { mock_cb.Trigger(); }};
|
||||
auto flat_con = FlatteningController::CreateInstance(cbks, kTestTimeout);
|
||||
|
||||
EXPECT_CALL(mock_cb, Trigger()).Times(1);
|
||||
|
||||
// Enable the controller and start the timer.
|
||||
flat_con->NewFrame();
|
||||
std::this_thread::sleep_for(kTestTimeout + kTimeoutEpsilon);
|
||||
EXPECT_TRUE(flat_con->ShouldFlatten());
|
||||
|
||||
// First NewFrame is in response to the refresh callback, should stay
|
||||
// flattened.
|
||||
flat_con->NewFrame();
|
||||
EXPECT_TRUE(flat_con->ShouldFlatten());
|
||||
|
||||
// Second NewFrame should not flatten.
|
||||
flat_con->NewFrame();
|
||||
EXPECT_FALSE(flat_con->ShouldFlatten());
|
||||
}
|
||||
|
||||
TEST_F(FlatteningControllerTest, TriggersCallbackAtCorrectTime) {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
bool triggered = false;
|
||||
|
||||
std::chrono::steady_clock::time_point start_time;
|
||||
std::chrono::steady_clock::time_point trigger_time;
|
||||
|
||||
FlatConCallbacks cbks = {.trigger = [&]() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
trigger_time = std::chrono::steady_clock::now();
|
||||
triggered = true;
|
||||
cv.notify_one();
|
||||
}};
|
||||
auto flat_con = FlatteningController::CreateInstance(cbks, kTestTimeout);
|
||||
|
||||
start_time = std::chrono::steady_clock::now();
|
||||
flat_con->NewFrame();
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait_for(lock, kTestTimeout * 2, [&] { return triggered; });
|
||||
|
||||
EXPECT_TRUE(triggered);
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
trigger_time - start_time);
|
||||
EXPECT_GE(duration, kTestTimeout);
|
||||
EXPECT_LT(duration, kTestTimeout + kTimeoutEpsilon);
|
||||
}
|
||||
|
||||
TEST_F(FlatteningControllerTest, ResetsTimeoutOnNewFrame) {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
bool triggered = false;
|
||||
|
||||
std::chrono::steady_clock::time_point start_time;
|
||||
std::chrono::steady_clock::time_point trigger_time;
|
||||
|
||||
FlatConCallbacks cbks = {.trigger = [&]() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
trigger_time = std::chrono::steady_clock::now();
|
||||
triggered = true;
|
||||
cv.notify_one();
|
||||
}};
|
||||
auto flat_con = FlatteningController::CreateInstance(cbks, kTestTimeout);
|
||||
|
||||
flat_con->NewFrame();
|
||||
std::this_thread::sleep_for(kTestTimeout / 2);
|
||||
|
||||
// NewFrame is called before the original timeout has expired.
|
||||
start_time = std::chrono::steady_clock::now();
|
||||
flat_con->NewFrame();
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait_for(lock, kTestTimeout * 2, [&] { return triggered; });
|
||||
|
||||
EXPECT_TRUE(triggered);
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
trigger_time - start_time);
|
||||
EXPECT_GE(duration, kTestTimeout);
|
||||
EXPECT_LT(duration, kTestTimeout + kTimeoutEpsilon);
|
||||
}
|
||||
|
||||
} // namespace android::drm_hwcomposer
|
||||
Loading…
Add table
Add a link
Reference in a new issue