| // Copyright 2015 The Chromium OS 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 <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/values.h" |
| #include "gtest/gtest.h" |
| |
| #include "thermald/configuration.h" |
| #include "thermald/config_parser.h" |
| #include "thermald/thermal_zone.h" |
| |
| using std::make_pair; |
| using std::map; |
| using std::string; |
| using std::stringstream; |
| using std::unique_ptr; |
| using std::vector; |
| |
| namespace thermald { |
| |
| static const char *kSensor0Name = "sensor 0"; |
| static const int kSensor0ZoneId = 2; |
| static const int kSensor0SamplingPeriod = 500; |
| |
| static const char *kSensor1Name = "sensor 1"; |
| static const int kSensor1ChipId = 3; |
| static const int kSensor1SensorId = 7; |
| static const int kSensor1SamplingPeriod = 700; |
| |
| static const char *kFakeDataFile = "/fake/data/file"; |
| |
| static const char *kZone0Name = "zone0"; |
| static const char *kZone1Name = "zone1"; |
| |
| class ConfigParserTest : public testing::Test { |
| protected: |
| ConfigParserTest() { |
| sensors_.reset(new base::ListValue); |
| thermal_zones_.reset(new base::ListValue); |
| tmp_file_path_.clear(); |
| } |
| |
| virtual ~ConfigParserTest() { |
| if (!tmp_file_path_.empty()) { |
| DeleteTmpFile(); |
| } |
| } |
| |
| void GenerateTmpFile(const string &content) { |
| if (!tmp_file_path_.empty()) { |
| DeleteTmpFile(); |
| } |
| |
| ASSERT_TRUE(base::CreateTemporaryFile(&tmp_file_path_)); |
| |
| ASSERT_TRUE(WriteFile(tmp_file_path_, content.c_str(), content.length())); |
| } |
| |
| void DeleteTmpFile() { |
| base::DeleteFile(tmp_file_path_); |
| tmp_file_path_.clear(); |
| } |
| |
| bool GenerateAndParseConfigFile() { |
| base::DictionaryValue json_cfg; |
| json_cfg.SetWithoutPathExpansion("sensors", std::move(sensors_)); |
| json_cfg.SetWithoutPathExpansion("thermal_zones", |
| std::move(thermal_zones_)); |
| |
| string str; |
| base::JSONWriter::Write(json_cfg, &str); |
| GenerateTmpFile(str); |
| |
| cfg_ = ConfigParser::ParseFile(tmp_file_path_); |
| |
| return cfg_ != nullptr; |
| } |
| |
| void AssertSensorExistsAndValuesMatch(const string &sensor_name, |
| SensorType sensor_type, |
| int sampling_period) { |
| auto it = cfg_->sensors.find(sensor_name); |
| ASSERT_NE(cfg_->sensors.end(), it); |
| |
| ASSERT_EQ(sensor_name, it->second->name); |
| ASSERT_EQ(sensor_type, it->second->type); |
| ASSERT_EQ(sampling_period, it->second->sampling_period); |
| } |
| |
| void AddHwmonSensor(const string &name, int hwmon_chip_id, |
| int hwmon_sensor_id, int sampling_period) { |
| auto params = std::make_unique<base::ListValue>(); |
| params->AppendInteger(hwmon_chip_id); |
| params->AppendInteger(hwmon_sensor_id); |
| |
| AddSensor(name, "hwmon", std::move(params), sampling_period); |
| } |
| |
| void AddThermalZoneSensor(const string &name, int zone_id, |
| int sampling_period) { |
| auto params = std::make_unique<base::ListValue>(); |
| params->AppendInteger(zone_id); |
| |
| AddSensor(name, "thermal_zone", std::move(params), sampling_period); |
| } |
| |
| void AddAth10kSensor(const string &name, const string &ath10k_interface, |
| int sampling_period) { |
| auto params = std::make_unique<base::ListValue>(); |
| params->AppendString(ath10k_interface); |
| |
| AddSensor(name, "ath10k", std::move(params), sampling_period); |
| } |
| |
| void AddFakeSensor(const string &name, const string &fake_data_file, |
| int sampling_period) { |
| auto params = std::make_unique<base::ListValue>(); |
| params->AppendString(fake_data_file); |
| |
| AddSensor(name, "fake", std::move(params), sampling_period); |
| } |
| |
| void AddSensor(const string &name, const string &type, |
| unique_ptr<base::ListValue> params, int sampling_period) { |
| auto sensor = std::make_unique<base::ListValue>(); |
| sensor->AppendString(name); |
| sensor->AppendString(type); |
| sensor->Append(std::move(params)); |
| sensor->AppendInteger(sampling_period); |
| |
| sensors_->Append(std::move(sensor)); |
| } |
| |
| base::Value *CreateThermalState( |
| int id, |
| const vector<ThermalPointThresholds> &thresholds, |
| const map<string, int> &outputs) { |
| base::ListValue *state = new base::ListValue; |
| |
| state->AppendInteger(id); |
| |
| auto threshold_list = std::make_unique<base::ListValue>(); |
| for (auto &t : thresholds) { |
| auto threshold_pair = std::make_unique<base::ListValue>(); |
| threshold_pair->AppendInteger(t.activation); |
| threshold_pair->AppendInteger(t.bottom); |
| threshold_list->Append(std::move(threshold_pair)); |
| } |
| state->Append(std::move(threshold_list)); |
| |
| auto outputs_dict = std::make_unique<base::DictionaryValue>(); |
| for (auto &it : outputs) { |
| outputs_dict->SetWithoutPathExpansion( |
| it.first, std::make_unique<base::Value>(it.second)); |
| } |
| state->Append(std::move(outputs_dict)); |
| |
| return state; |
| } |
| |
| void AddThermalZone(const string &name, |
| const vector<string> &sensors, |
| const vector<base::Value *> &states) { |
| auto zone = std::make_unique<base::ListValue>(); |
| zone->AppendString(name); |
| |
| auto sensor_list = std::make_unique<base::ListValue>(); |
| for (auto &s : sensors) { |
| sensor_list->AppendString(s); |
| } |
| zone->Append(std::move(sensor_list)); |
| |
| auto state_list = std::make_unique<base::ListValue>(); |
| for (auto s : states) { |
| state_list->Append(base::WrapUnique(s)); |
| } |
| zone->Append(std::move(state_list)); |
| |
| thermal_zones_->Append(std::move(zone)); |
| } |
| |
| base::FilePath tmp_file_path_; |
| Configuration *cfg_; |
| |
| unique_ptr<base::ListValue> sensors_; |
| unique_ptr<base::ListValue> thermal_zones_; |
| }; |
| |
| class ConfigParserThresholdTest : public ConfigParserTest { |
| protected: |
| void AddExpectedSensorThresholds(const string &sensor_name, |
| int activation_threshold, |
| int bottom_threshold) { |
| ThermalPointThresholds thresholds; |
| thresholds.activation = activation_threshold; |
| thresholds.bottom = bottom_threshold; |
| expected_thresholds_.insert(make_pair(sensor_name, thresholds)); |
| } |
| |
| void AssertThresholdsMatch( |
| const map<string, ThermalPointThresholds> &actual_thresholds) { |
| ASSERT_EQ(expected_thresholds_.size(), actual_thresholds.size()); |
| |
| auto it0 = expected_thresholds_.begin(); |
| auto it1 = actual_thresholds.begin(); |
| |
| for (; it0 != expected_thresholds_.end(); it0++, it1++) { |
| ASSERT_EQ(it0->first, it1->first); |
| ASSERT_EQ(it1->second.activation, it0->second.activation); |
| ASSERT_EQ(it1->second.bottom, it0->second.bottom); |
| } |
| } |
| |
| map<string, ThermalPointThresholds> expected_thresholds_; |
| }; |
| |
| TEST_F(ConfigParserTest, ParseThermalZoneSensor) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| AssertSensorExistsAndValuesMatch(kSensor0Name, kThermalZoneSensor, |
| kSensor0SamplingPeriod); |
| } |
| |
| TEST_F(ConfigParserTest, ParseHwmonSensor) { |
| AddHwmonSensor(kSensor1Name, kSensor1ChipId, kSensor1SensorId, |
| kSensor1SamplingPeriod); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| AssertSensorExistsAndValuesMatch(kSensor1Name, kHwmonSensor, |
| kSensor1SamplingPeriod); |
| } |
| |
| TEST_F(ConfigParserTest, ParseAth10kSensor) { |
| AddAth10kSensor(kSensor1Name, "wifi0", kSensor1SamplingPeriod); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| AssertSensorExistsAndValuesMatch(kSensor1Name, kAth10kSensor, |
| kSensor1SamplingPeriod); |
| auto it = cfg_->sensors.find(kSensor1Name); |
| ASSERT_EQ(string("wifi0"), |
| it->second->ath10k_sensor.wlan_interface); |
| } |
| |
| TEST_F(ConfigParserTest, ParseFakeSensor) { |
| AddFakeSensor(kSensor1Name, kFakeDataFile, kSensor1SamplingPeriod); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| AssertSensorExistsAndValuesMatch(kSensor1Name, kFakeSensor, |
| kSensor1SamplingPeriod); |
| auto it = cfg_->sensors.find(kSensor1Name); |
| ASSERT_EQ(string(kFakeDataFile), |
| it->second->fake_sensor.fake_data_file); |
| } |
| |
| TEST_F(ConfigParserTest, ParseMultipleSensors) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddHwmonSensor(kSensor1Name, kSensor1ChipId, kSensor1SensorId, |
| kSensor1SamplingPeriod); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| AssertSensorExistsAndValuesMatch(kSensor0Name, kThermalZoneSensor, |
| kSensor0SamplingPeriod); |
| AssertSensorExistsAndValuesMatch(kSensor1Name, kHwmonSensor, |
| kSensor1SamplingPeriod); |
| } |
| |
| TEST_F(ConfigParserTest, ParseSingleThermalZone) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {})}); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| ASSERT_NE(cfg_->zones.end(), cfg_->zones.find(kZone0Name)); |
| } |
| |
| TEST_F(ConfigParserTest, ParseMultipleThermalZones) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {})}); |
| AddThermalZone(kZone1Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {})}); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| ASSERT_NE(cfg_->zones.end(), cfg_->zones.find(kZone0Name)); |
| ASSERT_NE(cfg_->zones.end(), cfg_->zones.find(kZone1Name)); |
| } |
| |
| TEST_F(ConfigParserTest, ThermalStatesAreAddedToZone) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {})}); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| const ThermalZoneCfg &zone_cfg(*cfg_->zones[kZone0Name]); |
| ASSERT_EQ(2, zone_cfg.states.size()); |
| ASSERT_EQ(1, zone_cfg.states[0]->id); |
| ASSERT_EQ(2, zone_cfg.states[1]->id); |
| } |
| |
| TEST_F(ConfigParserTest, OutputsAreAddedToThermalState) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, |
| {{"output0", 23}, {"output1", 42}})}); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| |
| const ThermalZoneCfg &zone_cfg(*cfg_->zones[kZone0Name]); |
| const ThermalStateCfg &state_cfg(*zone_cfg.states[1]); |
| ASSERT_EQ(2, state_cfg.outputs.size()); |
| ASSERT_EQ("output0", state_cfg.outputs[0].key); |
| ASSERT_EQ(23, state_cfg.outputs[0].value); |
| ASSERT_EQ("output1", state_cfg.outputs[1].key); |
| ASSERT_EQ(42, state_cfg.outputs[1].value); |
| } |
| |
| TEST_F(ConfigParserThresholdTest, |
| SetsWildcardThresholdsForLowestState_SingleSensor) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {})}); |
| |
| AddExpectedSensorThresholds(kSensor0Name, kTemp0K, kTemp0K); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| AssertThresholdsMatch(cfg_->zones[kZone0Name]->states[0]->thresholds); |
| } |
| |
| TEST_F(ConfigParserThresholdTest, |
| SetsWildcardThresholdsForLowestState_MultipleSensors) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddHwmonSensor(kSensor1Name, kSensor1ChipId, kSensor1SensorId, |
| kSensor1SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name, kSensor1Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}, {70, 65}}, {})}); |
| |
| AddExpectedSensorThresholds(kSensor0Name, kTemp0K, kTemp0K); |
| AddExpectedSensorThresholds(kSensor1Name, kTemp0K, kTemp0K); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| AssertThresholdsMatch(cfg_->zones[kZone0Name]->states[0]->thresholds); |
| } |
| |
| TEST_F(ConfigParserThresholdTest, UseThresholdsFromConfigFile_SingleSensor) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {})}); |
| |
| AddExpectedSensorThresholds(kSensor0Name, kTemp0K, kTemp0K); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| AssertThresholdsMatch(cfg_->zones[kZone0Name]->states[0]->thresholds); |
| } |
| |
| TEST_F(ConfigParserThresholdTest, |
| UsesThresholdsFromConfigFile_MultipleSensors) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddHwmonSensor(kSensor1Name, kSensor1ChipId, kSensor1SensorId, |
| kSensor1SamplingPeriod); |
| |
| AddThermalZone(kZone0Name, {kSensor0Name, kSensor1Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}, {70, 65}}, {})}); |
| |
| AddExpectedSensorThresholds(kSensor0Name, 60000, 55000); |
| AddExpectedSensorThresholds(kSensor1Name, 70000, 65000); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| AssertThresholdsMatch(cfg_->zones[kZone0Name]->states[1]->thresholds); |
| } |
| |
| TEST_F(ConfigParserThresholdTest, |
| SetsWildcardValuesForSensorsWithoutThresholds) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{-1, -1}}, {})}); |
| |
| AddExpectedSensorThresholds(kSensor0Name, kTempInfinite, kTemp0K); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| AssertThresholdsMatch(cfg_->zones[kZone0Name]->states[1]->thresholds); |
| } |
| |
| TEST_F(ConfigParserThresholdTest, BottomGreaterThanActivationThreshold) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{55, 55}}, {})}); |
| |
| ASSERT_FALSE(GenerateAndParseConfigFile()); |
| } |
| |
| TEST_F(ConfigParserThresholdTest, ActivationAndBottomThresholdsAreEqual) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {}), |
| CreateThermalState(3, {{65, 70}}, {})}); |
| |
| ASSERT_FALSE(GenerateAndParseConfigFile()); |
| } |
| |
| TEST_F(ConfigParserThresholdTest, IntermediateStateWithoutThresholds) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {}), |
| CreateThermalState(3, {{-1, -1}}, {}), |
| CreateThermalState(4, {{80, 75}}, {})}); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| } |
| |
| /* Verify that the parser rejects non increasing activation thresholds */ |
| TEST_F(ConfigParserThresholdTest, NonIncreasingActivationThresholds) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {}), |
| CreateThermalState(3, {{59, 56}}, {})}); |
| |
| ASSERT_FALSE(GenerateAndParseConfigFile()); |
| } |
| |
| /* Verify that the parser rejects non increasing bottom thresholds */ |
| TEST_F(ConfigParserThresholdTest, NonIncreasingBottomThresholds) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {}), |
| CreateThermalState(3, {{71, 54}}, {})}); |
| |
| ASSERT_FALSE(GenerateAndParseConfigFile()); |
| } |
| |
| /* Verify that the parser rejects non increasing activation thresholds in a |
| configuration with an intermediate state without thresholds */ |
| TEST_F(ConfigParserThresholdTest, |
| NonIncreasingActivationThreshold_IntermediateStateWithoutThresholds) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {}), |
| CreateThermalState(3, {{-1, -1}}, {}), |
| CreateThermalState(4, {{59, 56}}, {})}); |
| |
| ASSERT_FALSE(GenerateAndParseConfigFile()); |
| } |
| |
| /* Verify that the parser rejects non increasing bottom thresholds in a |
| configuration with an intermediate state without thresholds */ |
| TEST_F(ConfigParserThresholdTest, |
| NonIncreasingBottomThreshold_IntermediateStateWithoutThresholds) { |
| AddThermalZoneSensor(kSensor0Name, kSensor0ZoneId, kSensor0SamplingPeriod); |
| AddThermalZone(kZone0Name, {kSensor0Name}, |
| {CreateThermalState(1, {}, {}), |
| CreateThermalState(2, {{60, 55}}, {}), |
| CreateThermalState(3, {{-1, -1}}, {}), |
| CreateThermalState(4, {{80, 75}}, {})}); |
| |
| ASSERT_TRUE(GenerateAndParseConfigFile()); |
| } |
| |
| } // namespace thermald |