blob: b85ccd7c7c3eaf12541dea7878ff66d31586bf33 [file] [log] [blame]
// Copyright 2020 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 "power_manager/powerd/system/thermal/cooling_device.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/compiler_specific.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_number_conversions.h>
#include <brillo/file_utils.h>
#include <gtest/gtest.h>
#include "power_manager/common/test_main_loop_runner.h"
#include "power_manager/powerd/system/thermal/device_thermal_state.h"
#include "power_manager/powerd/system/thermal/thermal_device_observer.h"
namespace power_manager {
namespace system {
namespace {
// Abort if expected thermal event hasn't been received after this many
// milliseconds.
const int kUpdateTimeoutMs = 5000;
// Frequency with which the cooling device is polled.
const int kPollIntervalMs = 100;
// Simple ThermalDeviceObserver implementation that runs the event loop until
// it receives a thermal state change.
class TestObserver : public ThermalDeviceObserver {
public:
TestObserver() {}
TestObserver(const TestObserver&) = delete;
TestObserver& operator=(const TestObserver&) = delete;
~TestObserver() override {}
// Runs |loop_| until OnThermalChanged() is called.
bool RunUntilThermalChanged() {
return loop_runner_.StartLoop(
base::TimeDelta::FromMilliseconds(kUpdateTimeoutMs));
}
void OnThermalChanged(ThermalDeviceInterface* sensor) override {
loop_runner_.StopLoop();
}
private:
TestMainLoopRunner loop_runner_;
};
} // namespace
class CoolingDeviceTest : public ::testing::Test {
public:
CoolingDeviceTest() {}
CoolingDeviceTest(const CoolingDeviceTest&) = delete;
CoolingDeviceTest& operator=(const CoolingDeviceTest&) = delete;
~CoolingDeviceTest() override {}
void SetUp() override {
CHECK(temp_dir_.CreateUniqueTempDir());
device_dir_ = temp_dir_.GetPath().Append("cooling_device1");
CHECK(base::CreateDirectory(device_dir_));
max_state_file_ = device_dir_.Append("max_state");
cur_state_file_ = device_dir_.Append("cur_state");
type_file_ = device_dir_.Append("type");
WriteMaxState(100);
WriteCurState(0);
WriteType("Processor");
cooling_device_.reset(new CoolingDevice(device_dir_));
cooling_device_->set_poll_interval_ms_for_testing(kPollIntervalMs);
cooling_device_->AddObserver(&observer_);
}
void TearDown() override { cooling_device_->RemoveObserver(&observer_); }
protected:
// Helpers to simulate cooling device sysfs files.
void WriteMaxState(int num) {
std::string num_string = base::NumberToString(num);
CHECK(brillo::WriteStringToFile(max_state_file_, num_string));
}
void WriteCurState(int num) {
std::string num_string = base::NumberToString(num);
CHECK(brillo::WriteStringToFile(cur_state_file_, num_string));
}
void WriteType(std::string type) {
CHECK(brillo::WriteStringToFile(type_file_, type));
}
// Temporary directory mimicking a /sys/class/thermal directory.
base::ScopedTempDir temp_dir_;
base::FilePath device_dir_;
// Files mocking actual cooling device sysfs files.
base::FilePath max_state_file_;
base::FilePath cur_state_file_;
base::FilePath type_file_;
TestObserver observer_;
std::unique_ptr<CoolingDevice> cooling_device_;
};
TEST_F(CoolingDeviceTest, ProcessorScaling) {
WriteType("Processor");
WriteMaxState(100);
cooling_device_->Init(false /* read_immedieatly */);
std::pair<int, DeviceThermalState> test_data[] = {
{0, DeviceThermalState::kNominal}, {10, DeviceThermalState::kFair},
{50, DeviceThermalState::kSerious}, {80, DeviceThermalState::kCritical},
{79, DeviceThermalState::kSerious}, {49, DeviceThermalState::kFair},
{9, DeviceThermalState::kNominal}};
for (const auto& p : test_data) {
WriteCurState(p.first);
ASSERT_TRUE(observer_.RunUntilThermalChanged());
EXPECT_EQ(p.second, cooling_device_->GetThermalState());
}
}
TEST_F(CoolingDeviceTest, FanScaling) {
WriteType("TFN1");
WriteMaxState(100);
cooling_device_->Init(false /* read_immedieatly */);
// No critical state for fan.
std::pair<int, DeviceThermalState> test_data[] = {
{0, DeviceThermalState::kNominal},
{50, DeviceThermalState::kFair},
{100, DeviceThermalState::kSerious},
{99, DeviceThermalState::kFair},
{49, DeviceThermalState::kNominal}};
for (const auto& p : test_data) {
WriteCurState(p.first);
ASSERT_TRUE(observer_.RunUntilThermalChanged());
EXPECT_EQ(p.second, cooling_device_->GetThermalState());
}
}
TEST_F(CoolingDeviceTest, ChargerScaling) {
WriteType("TCHG");
WriteMaxState(100);
cooling_device_->Init(false /* read_immedieatly */);
// No critical state for charger.
std::pair<int, DeviceThermalState> test_data[] = {
{0, DeviceThermalState::kNominal},
{70, DeviceThermalState::kFair},
{100, DeviceThermalState::kSerious},
{99, DeviceThermalState::kFair},
{69, DeviceThermalState::kNominal}};
for (const auto& p : test_data) {
WriteCurState(p.first);
ASSERT_TRUE(observer_.RunUntilThermalChanged());
EXPECT_EQ(p.second, cooling_device_->GetThermalState());
}
}
TEST_F(CoolingDeviceTest, OtherScaling) {
WriteType("thermal-dev-freq");
WriteMaxState(100);
cooling_device_->Init(false /* read_immedieatly */);
std::pair<int, DeviceThermalState> test_data[] = {
{0, DeviceThermalState::kNominal}, {50, DeviceThermalState::kFair},
{80, DeviceThermalState::kSerious}, {100, DeviceThermalState::kCritical},
{99, DeviceThermalState::kSerious}, {79, DeviceThermalState::kFair},
{49, DeviceThermalState::kNominal}};
for (const auto& p : test_data) {
WriteCurState(p.first);
ASSERT_TRUE(observer_.RunUntilThermalChanged());
EXPECT_EQ(p.second, cooling_device_->GetThermalState());
}
}
TEST_F(CoolingDeviceTest, Rounding) {
WriteType("Processor");
WriteMaxState(3);
cooling_device_->Init(false /* read_immedieatly */);
std::pair<int, DeviceThermalState> test_data[] = {
{0, DeviceThermalState::kNominal}, {1, DeviceThermalState::kFair},
{2, DeviceThermalState::kSerious}, {3, DeviceThermalState::kCritical},
{2, DeviceThermalState::kSerious}, {1, DeviceThermalState::kFair},
{0, DeviceThermalState::kNominal}};
for (const auto& p : test_data) {
WriteCurState(p.first);
ASSERT_TRUE(observer_.RunUntilThermalChanged());
EXPECT_EQ(p.second, cooling_device_->GetThermalState());
}
}
TEST_F(CoolingDeviceTest, ZeroMaxState) {
WriteType("Processor");
WriteMaxState(0);
WriteCurState(0);
cooling_device_->Init(true /* read_immedieatly */);
EXPECT_FALSE(observer_.RunUntilThermalChanged());
EXPECT_EQ(DeviceThermalState::kUnknown, cooling_device_->GetThermalState());
}
} // namespace system
} // namespace power_manager