blob: 4e6ef97b608ddfbe9c4fc1d4c5c53eee03b3ac59 [file] [log] [blame]
// Copyright 2019 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 <memory>
#include <string>
#include <base/files/file_path.h>
#include <base/files/scoped_temp_dir.h>
#include <base/macros.h>
#include <base/strings/string_split.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "diagnostics/routines/battery_sysfs/battery_sysfs.h"
#include "diagnostics/wilco_dtc_supportd/file_test_utils.h"
#include "wilco_dtc_supportd.pb.h" // NOLINT(build/include)
namespace diagnostics {
namespace {
using ::testing::UnorderedElementsAreArray;
constexpr int kMaximumCycleCount = 5;
constexpr int kPercentBatteryWearAllowed = 10;
constexpr int kHighCycleCount = 6;
constexpr int kLowCycleCount = 4;
constexpr int kHighChargeFull = 91;
constexpr int kLowChargeFull = 89;
constexpr int kFakeBatteryChargeFullDesign = 100;
constexpr char kFakeManufacturer[] = "Fake Manufacturer";
constexpr int kFakeCurrentNow = 90871023;
constexpr int kFakePresent = 1;
constexpr char kFakeStatus[] = "Full";
constexpr int kFakeVoltageNow = 90872;
constexpr int kFakeChargeNow = 98123;
constexpr char kFullCycleCountPath[] =
"sys/class/power_supply/BAT0/cycle_count";
std::string ConstructOutput() {
std::string output;
int wear_percentage =
100 - (kHighChargeFull * 100 / kFakeBatteryChargeFullDesign);
output += "Wear Percentage: " + std::to_string(wear_percentage) + "\n";
output += "Cycle Count: " + std::to_string(kLowCycleCount) + "\n";
output += "Manufacturer: " + std::string(kFakeManufacturer) + "\n";
output += "Current Now: " + std::to_string(kFakeCurrentNow) + "\n";
output += "Present: " + std::to_string(kFakePresent) + "\n";
output += "Status: " + std::string(kFakeStatus) + "\n";
output += "Voltage Now: " + std::to_string(kFakeVoltageNow) + "\n";
output += "Charge Full: " + std::to_string(kHighChargeFull) + "\n";
output +=
"Charge Full Design: " + std::to_string(kFakeBatteryChargeFullDesign) +
"\n";
output += "Charge Now: " + std::to_string(kFakeChargeNow) + "\n";
return output;
}
} // namespace
class BatterySysfsRoutineTest : public testing::Test {
protected:
BatterySysfsRoutineTest() {
params_.set_maximum_cycle_count(kMaximumCycleCount);
params_.set_percent_battery_wear_allowed(kPercentBatteryWearAllowed);
routine_ = std::make_unique<BatterySysfsRoutine>(params_);
}
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
routine_->set_root_dir_for_testing(temp_dir_.GetPath());
}
BatterySysfsRoutine* routine() { return routine_.get(); }
grpc_api::GetRoutineUpdateResponse* response() { return &response_; }
void RunRoutineAndWaitForExit() {
routine_->Start();
// Since the BatterySysfsRoutine has finished by the time Start() returns,
// there is no need to wait.
routine_->PopulateStatusUpdate(&response_, true);
}
void WriteFilesReadByLog() {
WriteFileContents(kBatterySysfsManufacturerPath, kFakeManufacturer);
WriteFileContents(kBatterySysfsCurrentNowPath,
std::to_string(kFakeCurrentNow));
WriteFileContents(kBatterySysfsPresentPath, std::to_string(kFakePresent));
WriteFileContents(kBatterySysfsStatusPath, kFakeStatus);
WriteFileContents(kBatterySysfsVoltageNowPath,
std::to_string(kFakeVoltageNow));
WriteFileContents(kBatterySysfsChargeNowPath,
std::to_string(kFakeChargeNow));
}
void WriteFileContents(const std::string& relative_file_path,
const std::string& file_contents) {
EXPECT_TRUE(
WriteFileAndCreateParentDirs(temp_dir_path()
.AppendASCII(kBatterySysfsPath)
.AppendASCII(relative_file_path),
file_contents));
}
const base::FilePath& temp_dir_path() const { return temp_dir_.GetPath(); }
private:
base::ScopedTempDir temp_dir_;
std::unique_ptr<BatterySysfsRoutine> routine_;
grpc_api::GetRoutineUpdateResponse response_;
grpc_api::BatterySysfsRoutineParameters params_;
DISALLOW_COPY_AND_ASSIGN(BatterySysfsRoutineTest);
};
// Test that the battery_sysfs routine fails if the cycle count is too high.
TEST_F(BatterySysfsRoutineTest, HighCycleCount) {
WriteFileContents(kBatterySysfsChargeFullPath,
std::to_string(kHighChargeFull));
WriteFileContents(kBatterySysfsChargeFullDesignPath,
std::to_string(kFakeBatteryChargeFullDesign));
WriteFileContents(kBatterySysfsCycleCountPath,
std::to_string(kHighCycleCount));
RunRoutineAndWaitForExit();
EXPECT_EQ(response()->status_message(),
kBatterySysfsExcessiveCycleCountMessage);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_FAILED);
}
// Test that the battery_sysfs routine fails if cycle_count is not present.
TEST_F(BatterySysfsRoutineTest, NoCycleCount) {
WriteFileContents(kBatterySysfsChargeFullPath,
std::to_string(kHighChargeFull));
WriteFileContents(kBatterySysfsChargeFullDesignPath,
std::to_string(kFakeBatteryChargeFullDesign));
RunRoutineAndWaitForExit();
EXPECT_EQ(response()->status_message(),
kBatterySysfsFailedReadingCycleCountMessage);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_ERROR);
}
// Test that the battery_sysfs routine fails if the wear percentage is too
// high.
TEST_F(BatterySysfsRoutineTest, HighWearPercentage) {
WriteFileContents(kBatterySysfsChargeFullPath,
std::to_string(kLowChargeFull));
WriteFileContents(kBatterySysfsChargeFullDesignPath,
std::to_string(kFakeBatteryChargeFullDesign));
WriteFileContents(kBatterySysfsCycleCountPath,
std::to_string(kLowCycleCount));
RunRoutineAndWaitForExit();
EXPECT_EQ(response()->status_message(), kBatterySysfsExcessiveWearMessage);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_FAILED);
}
// Test that the battery_sysfs routine fails if neither charge_full nor
// energy_full are present.
TEST_F(BatterySysfsRoutineTest, NoWearPercentage) {
WriteFileContents(kBatterySysfsCycleCountPath,
std::to_string(kLowCycleCount));
RunRoutineAndWaitForExit();
EXPECT_EQ(response()->status_message(),
kBatterySysfsFailedCalculatingWearPercentageMessage);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_ERROR);
}
// Test that the battery_sysfs routine passes if the cycle count and wear
// percentage are within acceptable limits.
TEST_F(BatterySysfsRoutineTest, GoodParameters) {
WriteFileContents(kBatterySysfsChargeFullPath,
std::to_string(kHighChargeFull));
WriteFileContents(kBatterySysfsChargeFullDesignPath,
std::to_string(kFakeBatteryChargeFullDesign));
WriteFileContents(kBatterySysfsCycleCountPath,
std::to_string(kLowCycleCount));
WriteFilesReadByLog();
RunRoutineAndWaitForExit();
EXPECT_EQ(response()->status_message(), kBatterySysfsRoutinePassedMessage);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_PASSED);
base::StringPairs expected_output_pairs;
base::StringPairs actual_output_pairs;
ASSERT_TRUE(base::SplitStringIntoKeyValuePairs(ConstructOutput(), ':', '\n',
&expected_output_pairs));
ASSERT_TRUE(base::SplitStringIntoKeyValuePairs(response()->output(), ':',
'\n', &actual_output_pairs));
EXPECT_THAT(actual_output_pairs,
UnorderedElementsAreArray(expected_output_pairs));
}
// Test that the battery_sysfs routine will find energy-reporting batteries.
TEST_F(BatterySysfsRoutineTest, EnergyReportingBattery) {
WriteFileContents(kBatterySysfsEnergyFullPath,
std::to_string(kHighChargeFull));
WriteFileContents(kBatterySysfsEnergyFullDesignPath,
std::to_string(kFakeBatteryChargeFullDesign));
WriteFileContents(kBatterySysfsCycleCountPath,
std::to_string(kLowCycleCount));
RunRoutineAndWaitForExit();
EXPECT_EQ(response()->status_message(), kBatterySysfsRoutinePassedMessage);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_PASSED);
}
// Test that the battery_sysfs routine uses the expected full path to
// cycle_count, relative to the temporary test directory.
TEST_F(BatterySysfsRoutineTest, FullCycleCountPath) {
WriteFileContents(kBatterySysfsChargeFullPath,
std::to_string(kHighChargeFull));
WriteFileContents(kBatterySysfsChargeFullDesignPath,
std::to_string(kFakeBatteryChargeFullDesign));
EXPECT_TRUE(WriteFileAndCreateParentDirs(
temp_dir_path().AppendASCII(kFullCycleCountPath),
std::to_string(kLowCycleCount)));
RunRoutineAndWaitForExit();
EXPECT_EQ(response()->status_message(), kBatterySysfsRoutinePassedMessage);
EXPECT_EQ(response()->status(), grpc_api::ROUTINE_STATUS_PASSED);
}
} // namespace diagnostics