| // 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 |