blob: 4af776764cef42c9ca0fd54d84dd6fd1c1a61508 [file] [log] [blame]
// 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