blob: bf6559ebe73aab12ab4192f4e05015533f2f5177 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/display/refresh_rate_throttle_controller.h"
#include <memory>
#include <vector>
#include "ash/shell.h"
#include "ash/system/power/power_status.h"
#include "ash/test/ash_test_base.h"
#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
#include "ui/display/fake/fake_display_snapshot.h"
#include "ui/display/manager/display_configurator.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/manager/test/action_logger.h"
#include "ui/display/manager/test/action_logger_util.h"
#include "ui/display/manager/test/test_native_display_delegate.h"
#include "ui/display/types/display_mode.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/display/types/native_display_delegate.h"
namespace ash {
namespace {
using display::DisplayMode;
using display::DisplaySnapshot;
using display::FakeDisplaySnapshot;
using display::NativeDisplayDelegate;
using display::test::ActionLogger;
using display::test::TestNativeDisplayDelegate;
using power_manager::PowerSupplyProperties;
std::unique_ptr<DisplayMode> MakeDisplayMode(int width,
int height,
bool is_interlaced,
float refresh_rate) {
return std::make_unique<DisplayMode>(gfx::Size(width, height), is_interlaced,
refresh_rate);
}
std::unique_ptr<DisplaySnapshot> BuildDualRefreshPanelSnapshot(
int64_t id,
display::DisplayConnectionType type) {
return FakeDisplaySnapshot::Builder()
.SetId(id)
.SetType(type)
.SetNativeMode(MakeDisplayMode(1920, 1200, false, 120.f))
.AddMode(MakeDisplayMode(1920, 1200, false, 60.f))
.SetCurrentMode(MakeDisplayMode(1920, 1200, false, 120.f))
.Build();
}
PowerSupplyProperties BuildFakePowerSupplyProperties(
PowerSupplyProperties::ExternalPower charger_state,
double battery_percent) {
PowerSupplyProperties fake_power;
fake_power.set_external_power(charger_state);
fake_power.set_battery_percent(battery_percent);
return fake_power;
}
class RefreshRateThrottleControllerTest : public AshTestBase {
public:
RefreshRateThrottleControllerTest() = default;
RefreshRateThrottleControllerTest(const RefreshRateThrottleControllerTest&) =
delete;
RefreshRateThrottleControllerTest& operator=(
const RefreshRateThrottleControllerTest&) = delete;
~RefreshRateThrottleControllerTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
logger_ = std::make_unique<ActionLogger>();
native_display_delegate_ = new TestNativeDisplayDelegate(logger_.get());
display_manager()->configurator()->SetDelegateForTesting(
std::unique_ptr<NativeDisplayDelegate>(native_display_delegate_));
controller_ = std::make_unique<RefreshRateThrottleController>(
Shell::Get()->display_configurator(), PowerStatus::Get());
}
void TearDown() override {
controller_.reset();
AshTestBase::TearDown();
}
protected:
void SetUpDisplays(
const std::vector<std::unique_ptr<DisplaySnapshot>>& snapshots) {
std::vector<DisplaySnapshot*> outputs;
for (const std::unique_ptr<DisplaySnapshot>& snapshot : snapshots)
outputs.push_back(snapshot.get());
display::DisplayConfigurator::TestApi test_api(
display_manager()->configurator());
native_display_delegate_->set_outputs(outputs);
display_manager()->configurator()->OnConfigurationChanged();
display_manager()->configurator()->ForceInitialConfigure();
ASSERT_TRUE(test_api.TriggerConfigureTimeout());
}
const DisplaySnapshot* GetDisplaySnapshot(int64_t display_id) {
for (const DisplaySnapshot* snapshot :
display_manager()->configurator()->cached_displays()) {
if (snapshot->display_id() == display_id)
return snapshot;
}
return nullptr;
}
std::unique_ptr<ActionLogger> logger_;
std::unique_ptr<RefreshRateThrottleController> controller_;
// Owned by DisplayConfigurator.
TestNativeDisplayDelegate* native_display_delegate_;
};
TEST_F(RefreshRateThrottleControllerTest, ShouldNotThrottleOnAC) {
constexpr int64_t kDisplayId = 12345;
std::vector<std::unique_ptr<DisplaySnapshot>> snapshots;
snapshots.push_back(BuildDualRefreshPanelSnapshot(
kDisplayId, display::DISPLAY_CONNECTION_TYPE_INTERNAL));
SetUpDisplays(snapshots);
// Expect the initial state to be 120 Hz.
{
const DisplaySnapshot* snapshot = GetDisplaySnapshot(kDisplayId);
ASSERT_NE(snapshot, nullptr);
ASSERT_NE(snapshot->current_mode(), nullptr);
EXPECT_EQ(snapshot->current_mode()->refresh_rate(), 120.f);
}
// Set power state to indicate the device is on AC.
PowerStatus::Get()->SetProtoForTesting(
BuildFakePowerSupplyProperties(PowerSupplyProperties::AC, 100.0));
controller_->OnPowerStatusChanged();
// Expect the new state to be unchanged.
{
const DisplaySnapshot* snapshot = GetDisplaySnapshot(kDisplayId);
ASSERT_NE(snapshot, nullptr);
ASSERT_NE(snapshot->current_mode(), nullptr);
EXPECT_EQ(snapshot->current_mode()->refresh_rate(), 120.f);
}
}
TEST_F(RefreshRateThrottleControllerTest, ShouldThrottleOnBattery) {
constexpr int64_t kDisplayId = 12345;
std::vector<std::unique_ptr<DisplaySnapshot>> snapshots;
snapshots.push_back(BuildDualRefreshPanelSnapshot(
kDisplayId, display::DISPLAY_CONNECTION_TYPE_INTERNAL));
SetUpDisplays(snapshots);
// Expect the initial state to be 120 Hz.
{
const DisplaySnapshot* snapshot = GetDisplaySnapshot(kDisplayId);
ASSERT_NE(snapshot, nullptr);
ASSERT_NE(snapshot->current_mode(), nullptr);
EXPECT_EQ(snapshot->current_mode()->refresh_rate(), 120.f);
}
// Set power state to indicate the device is on battery.
PowerStatus::Get()->SetProtoForTesting(BuildFakePowerSupplyProperties(
PowerSupplyProperties::DISCONNECTED, 100.0));
controller_->OnPowerStatusChanged();
// Expect the new state to be 60 Hz.
{
const DisplaySnapshot* snapshot = GetDisplaySnapshot(kDisplayId);
ASSERT_NE(snapshot, nullptr);
ASSERT_NE(snapshot->current_mode(), nullptr);
EXPECT_EQ(snapshot->current_mode()->refresh_rate(), 60.f);
}
}
TEST_F(RefreshRateThrottleControllerTest, ShouldNotAffectExternalDisplay) {
constexpr int64_t kDisplayId = 12345;
std::vector<std::unique_ptr<DisplaySnapshot>> snapshots;
snapshots.push_back(BuildDualRefreshPanelSnapshot(
kDisplayId, display::DISPLAY_CONNECTION_TYPE_HDMI));
SetUpDisplays(snapshots);
// Expect the initial state to be 120 Hz.
{
const DisplaySnapshot* snapshot = GetDisplaySnapshot(kDisplayId);
ASSERT_NE(snapshot, nullptr);
ASSERT_NE(snapshot->current_mode(), nullptr);
EXPECT_EQ(snapshot->current_mode()->refresh_rate(), 120.f);
}
// Set power state to indicate the device is on battery.
PowerStatus::Get()->SetProtoForTesting(BuildFakePowerSupplyProperties(
PowerSupplyProperties::DISCONNECTED, 100.0));
controller_->OnPowerStatusChanged();
// Expect the state to be unchanged.
{
const DisplaySnapshot* snapshot = GetDisplaySnapshot(kDisplayId);
ASSERT_NE(snapshot, nullptr);
ASSERT_NE(snapshot->current_mode(), nullptr);
EXPECT_EQ(snapshot->current_mode()->refresh_rate(), 120.f);
}
}
TEST_F(RefreshRateThrottleControllerTest, ShouldThrottleOnUSBCharger) {
constexpr int64_t kDisplayId = 12345;
std::vector<std::unique_ptr<DisplaySnapshot>> snapshots;
snapshots.push_back(BuildDualRefreshPanelSnapshot(
kDisplayId, display::DISPLAY_CONNECTION_TYPE_INTERNAL));
SetUpDisplays(snapshots);
// Expect the initial state to be 120 Hz.
{
const DisplaySnapshot* snapshot = GetDisplaySnapshot(kDisplayId);
ASSERT_NE(snapshot, nullptr);
ASSERT_NE(snapshot->current_mode(), nullptr);
EXPECT_EQ(snapshot->current_mode()->refresh_rate(), 120.f);
}
// Set power state to indicate the device is on a low powered charger.
PowerStatus::Get()->SetProtoForTesting(
BuildFakePowerSupplyProperties(PowerSupplyProperties::USB, 100.0));
controller_->OnPowerStatusChanged();
// Expect the new state to be 60 Hz.
{
const DisplaySnapshot* snapshot = GetDisplaySnapshot(kDisplayId);
ASSERT_NE(snapshot, nullptr);
ASSERT_NE(snapshot->current_mode(), nullptr);
EXPECT_EQ(snapshot->current_mode()->refresh_rate(), 60.f);
}
}
TEST_F(RefreshRateThrottleControllerTest, ShouldThrottleOnLowBattery) {
constexpr int64_t kDisplayId = 12345;
std::vector<std::unique_ptr<DisplaySnapshot>> snapshots;
snapshots.push_back(BuildDualRefreshPanelSnapshot(
kDisplayId, display::DISPLAY_CONNECTION_TYPE_INTERNAL));
SetUpDisplays(snapshots);
// Expect the initial state to be 120 Hz.
{
const DisplaySnapshot* snapshot = GetDisplaySnapshot(kDisplayId);
ASSERT_NE(snapshot, nullptr);
ASSERT_NE(snapshot->current_mode(), nullptr);
EXPECT_EQ(snapshot->current_mode()->refresh_rate(), 120.f);
}
// Set power state to indicate the device is on a high-powered charger,
// but the battery is critically low.
PowerStatus::Get()->SetProtoForTesting(
BuildFakePowerSupplyProperties(PowerSupplyProperties::AC, 4.0));
controller_->OnPowerStatusChanged();
// Expect the new state to be 60 Hz.
{
const DisplaySnapshot* snapshot = GetDisplaySnapshot(kDisplayId);
ASSERT_NE(snapshot, nullptr);
ASSERT_NE(snapshot->current_mode(), nullptr);
EXPECT_EQ(snapshot->current_mode()->refresh_rate(), 60.f);
}
// Set the power state to indicate the device has charged above
// critical level.
PowerStatus::Get()->SetProtoForTesting(
BuildFakePowerSupplyProperties(PowerSupplyProperties::AC, 6.0));
controller_->OnPowerStatusChanged();
// Expect the new state to be 120 Hz.
{
const DisplaySnapshot* snapshot = GetDisplaySnapshot(kDisplayId);
ASSERT_NE(snapshot, nullptr);
ASSERT_NE(snapshot->current_mode(), nullptr);
EXPECT_EQ(snapshot->current_mode()->refresh_rate(), 120.f);
}
}
} // namespace
} // namespace ash