blob: dc3f5345780012b6a43e6d69d603261abc23c532 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/media/audio/audio_core/thermal_agent.h"
#include <fuchsia/thermal/cpp/fidl.h>
#include <lib/fit/function.h>
#include <lib/gtest/test_loop_fixture.h>
#include "src/media/audio/audio_core/process_config.h"
namespace media::audio {
namespace {
class FakeThermalController : public fuchsia::thermal::Controller {
public:
FakeThermalController() : binding_(this) {}
void Bind(fidl::InterfaceRequest<fuchsia::thermal::Controller> request) {
binding_.Bind(std::move(request));
binding_.set_error_handler([this](zx_status_t status) { binding_error_ = status; });
}
void ExpectSubscribeNotCalled() const { EXPECT_FALSE(actor_.is_bound()); }
void ExpectSubscribeCalled(fuchsia::thermal::ActorType actor_type,
std::vector<uint32_t> trip_points) const {
EXPECT_TRUE(actor_.is_bound());
EXPECT_EQ(actor_type, actor_type_);
EXPECT_EQ(trip_points, trip_points_);
}
zx_status_t binding_error() const { return binding_error_; }
void SetThermalState(uint32_t state) {
EXPECT_TRUE(actor_.is_bound());
actor_->SetThermalState(state, [this]() { set_thermal_state_returned_ = true; });
}
bool& set_thermal_state_returned() { return set_thermal_state_returned_; }
// fuchsia::thermal::Controller implementation.
void Subscribe(fidl::InterfaceHandle<class fuchsia::thermal::Actor> actor,
fuchsia::thermal::ActorType actor_type, std::vector<uint32_t> trip_points,
SubscribeCallback callback) override {
FX_LOGS(INFO) << "Subscribe: " << (actor ? "actor" : "no actor");
actor_.Bind(std::move(actor));
actor_type_ = actor_type;
trip_points_ = std::move(trip_points);
callback(fuchsia::thermal::Controller_Subscribe_Result::WithResponse(
fuchsia::thermal::Controller_Subscribe_Response()));
}
private:
fidl::Binding<fuchsia::thermal::Controller> binding_;
fuchsia::thermal::ActorPtr actor_;
fuchsia::thermal::ActorType actor_type_;
std::vector<uint32_t> trip_points_;
zx_status_t binding_error_ = ZX_OK;
bool set_thermal_state_returned_ = false;
};
class ThermalAgentTest : public gtest::TestLoopFixture {
public:
ThermalAgent::SetConfigCallback ConfigCallback() {
return fit::bind_member(this, &ThermalAgentTest::HandleConfigAction);
}
void ExpectConfigAction(const std::string& target_name, const std::string& config) {
for (auto iter = config_actions_.begin(); iter != config_actions_.end(); ++iter) {
if (iter->target_name_ == target_name && iter->config_ == config) {
config_actions_.erase(iter);
return;
}
}
EXPECT_TRUE(false) << "Didn't find expected config action for target " << target_name
<< ", value " << config;
}
void ExpectNoOtherConfigAction() {
EXPECT_TRUE(config_actions_.empty());
config_actions_.clear();
}
private:
struct ConfigAction {
std::string target_name_;
std::string config_;
};
void HandleConfigAction(const std::string& target_name, const std::string& config) {
config_actions_.push_back({target_name, config});
}
std::vector<ConfigAction> config_actions_;
};
// Verifies that the thermal agent does nothing if the config contains to thermal config entries.
TEST_F(ThermalAgentTest, NoConfigEntries) {
ProcessConfig process_config = ProcessConfigBuilder()
.SetDefaultVolumeCurve(VolumeCurve::DefaultForMinGain(
VolumeCurve::kDefaultGainForMinVolume))
.Build();
fuchsia::thermal::ControllerPtr thermal_controller;
FakeThermalController fake_thermal_controller;
fake_thermal_controller.Bind(thermal_controller.NewRequest());
ThermalAgent under_test(std::move(thermal_controller), process_config.thermal_config(),
process_config.device_config(), ConfigCallback());
RunLoopUntilIdle();
EXPECT_NE(ZX_OK, fake_thermal_controller.binding_error());
ExpectNoOtherConfigAction();
fake_thermal_controller.ExpectSubscribeNotCalled();
}
const char kTargetName[] = "target_name";
const char kNominalConfig[] = "nominal_config";
constexpr uint32_t kTripPoint = 50;
const char kConfigTripPoint[] = "config";
// Verifies that the thermal agent works properly with a single thermal config entry with one trip
// point.
TEST_F(ThermalAgentTest, OneConfigEntry) {
PipelineConfig pipeline_config(
{"mixgroup", {}, {{"lib", "effect", kTargetName, kNominalConfig}}, {}, false, 48000});
std::vector<ThermalConfig::State> states{{kTripPoint, kConfigTripPoint}};
ProcessConfig process_config =
ProcessConfigBuilder()
.SetDefaultVolumeCurve(
VolumeCurve::DefaultForMinGain(VolumeCurve::kDefaultGainForMinVolume))
.AddDeviceProfile({std::nullopt, DeviceConfig::OutputDeviceProfile(
false, {}, false, std::move(pipeline_config))})
.AddThermalPolicyEntry(ThermalConfig::Entry(kTargetName, states))
.Build();
fuchsia::thermal::ControllerPtr thermal_controller;
FakeThermalController fake_thermal_controller;
fake_thermal_controller.Bind(thermal_controller.NewRequest());
ThermalAgent under_test(std::move(thermal_controller), process_config.thermal_config(),
process_config.device_config(), ConfigCallback());
RunLoopUntilIdle();
EXPECT_NE(ZX_OK, fake_thermal_controller.binding_error());
ExpectNoOtherConfigAction();
fake_thermal_controller.ExpectSubscribeCalled(fuchsia::thermal::ActorType::AUDIO, {kTripPoint});
// Thermal config in effect.
fake_thermal_controller.SetThermalState(1);
RunLoopUntilIdle();
EXPECT_TRUE(fake_thermal_controller.set_thermal_state_returned());
ExpectConfigAction(kTargetName, kConfigTripPoint);
ExpectNoOtherConfigAction();
fake_thermal_controller.set_thermal_state_returned() = false;
// Nominal config restored.
fake_thermal_controller.SetThermalState(0);
RunLoopUntilIdle();
EXPECT_TRUE(fake_thermal_controller.set_thermal_state_returned());
ExpectConfigAction(kTargetName, kNominalConfig);
ExpectNoOtherConfigAction();
}
const char kTargetName0[] = "target_name_0";
const char kTargetName1[] = "target_name_1";
const char kTargetName2[] = "target_name_2";
const char kNominalConfig0[] = "nominal_config_0";
const char kNominalConfig1[] = "nominal_config_1";
const char kNominalConfig2[] = "nominal_config_2";
constexpr uint32_t kTripPoint0_0 = 60;
constexpr uint32_t kTripPoint0_1 = 80;
constexpr uint32_t kTripPoint1_0 = 70;
constexpr uint32_t kTripPoint1_1 = 80;
constexpr uint32_t kTripPoint2_0 = 70;
constexpr uint32_t kTripPoint2_1 = 90;
const char kConfigTripPoint0_0[] = "config0_0";
const char kConfigTripPoint0_1[] = "config0_1";
const char kConfigTripPoint1_0[] = "config1_0";
const char kConfigTripPoint1_1[] = "config1_1";
const char kConfigTripPoint2_0[] = "config2_0";
const char kConfigTripPoint2_1[] = "config2_1";
// Verifies that the thermal agent works properly with multiple thermal config entries, each with
// multiple trip points.
TEST_F(ThermalAgentTest, MultipleConfigEntries) {
PipelineConfig pipeline_config({"mixgroup",
{},
{{"lib", "effect", kTargetName0, kNominalConfig0},
{"lib", "effect", kTargetName1, kNominalConfig1},
{"lib", "effect", kTargetName2, kNominalConfig2}},
{},
false,
48000});
std::vector<ThermalConfig::State> states0{{kTripPoint0_0, kConfigTripPoint0_0},
{kTripPoint0_1, kConfigTripPoint0_1}};
std::vector<ThermalConfig::State> states1{{kTripPoint1_0, kConfigTripPoint1_0},
{kTripPoint1_1, kConfigTripPoint1_1}};
std::vector<ThermalConfig::State> states2{{kTripPoint2_0, kConfigTripPoint2_0},
{kTripPoint2_1, kConfigTripPoint2_1}};
ProcessConfig process_config =
ProcessConfigBuilder()
.SetDefaultVolumeCurve(
VolumeCurve::DefaultForMinGain(VolumeCurve::kDefaultGainForMinVolume))
.AddDeviceProfile({std::nullopt, DeviceConfig::OutputDeviceProfile(
false, {}, false, std::move(pipeline_config))})
.AddThermalPolicyEntry(ThermalConfig::Entry(kTargetName0, states0))
.AddThermalPolicyEntry(ThermalConfig::Entry(kTargetName1, states1))
.AddThermalPolicyEntry(ThermalConfig::Entry(kTargetName2, states2))
.Build();
fuchsia::thermal::ControllerPtr thermal_controller;
FakeThermalController fake_thermal_controller;
fake_thermal_controller.Bind(thermal_controller.NewRequest());
ThermalAgent under_test(std::move(thermal_controller), process_config.thermal_config(),
process_config.device_config(), ConfigCallback());
RunLoopUntilIdle();
EXPECT_NE(ZX_OK, fake_thermal_controller.binding_error());
ExpectNoOtherConfigAction();
// We expect four trip points (5 states).
fake_thermal_controller.ExpectSubscribeCalled(
fuchsia::thermal::ActorType::AUDIO,
{kTripPoint0_0, kTripPoint1_0, kTripPoint1_1, kTripPoint2_1});
// Thermal config for state 1 in effect.
fake_thermal_controller.SetThermalState(1);
RunLoopUntilIdle();
EXPECT_TRUE(fake_thermal_controller.set_thermal_state_returned());
ExpectConfigAction(kTargetName0, kConfigTripPoint0_0);
ExpectNoOtherConfigAction();
fake_thermal_controller.set_thermal_state_returned() = false;
// Thermal config for state 2 in effect.
fake_thermal_controller.SetThermalState(2);
RunLoopUntilIdle();
EXPECT_TRUE(fake_thermal_controller.set_thermal_state_returned());
ExpectConfigAction(kTargetName1, kConfigTripPoint1_0);
ExpectConfigAction(kTargetName2, kConfigTripPoint2_0);
ExpectNoOtherConfigAction();
fake_thermal_controller.set_thermal_state_returned() = false;
// Thermal config for state 3 in effect.
fake_thermal_controller.SetThermalState(3);
RunLoopUntilIdle();
EXPECT_TRUE(fake_thermal_controller.set_thermal_state_returned());
ExpectConfigAction(kTargetName0, kConfigTripPoint0_1);
ExpectConfigAction(kTargetName1, kConfigTripPoint1_1);
ExpectNoOtherConfigAction();
fake_thermal_controller.set_thermal_state_returned() = false;
// Thermal config for state 4 in effect.
fake_thermal_controller.SetThermalState(4);
RunLoopUntilIdle();
EXPECT_TRUE(fake_thermal_controller.set_thermal_state_returned());
ExpectConfigAction(kTargetName2, kConfigTripPoint2_1);
ExpectNoOtherConfigAction();
fake_thermal_controller.set_thermal_state_returned() = false;
// Thermal config for state 2 in effect.
fake_thermal_controller.SetThermalState(2);
RunLoopUntilIdle();
EXPECT_TRUE(fake_thermal_controller.set_thermal_state_returned());
ExpectConfigAction(kTargetName0, kConfigTripPoint0_0);
ExpectConfigAction(kTargetName1, kConfigTripPoint1_0);
ExpectConfigAction(kTargetName2, kConfigTripPoint2_0);
ExpectNoOtherConfigAction();
fake_thermal_controller.set_thermal_state_returned() = false;
// Nominal config restored.
fake_thermal_controller.SetThermalState(0);
RunLoopUntilIdle();
EXPECT_TRUE(fake_thermal_controller.set_thermal_state_returned());
ExpectConfigAction(kTargetName0, kNominalConfig0);
ExpectConfigAction(kTargetName1, kNominalConfig1);
ExpectConfigAction(kTargetName2, kNominalConfig2);
ExpectNoOtherConfigAction();
}
} // namespace
} // namespace media::audio