// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/webui/diagnostics_ui/backend/system/system_data_provider.h"
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "ash/system/diagnostics/diagnostics_log_controller.h"
#include "ash/system/diagnostics/fake_diagnostics_browser_delegate.h"
#include "ash/system/diagnostics/telemetry_log.h"
#include "ash/test/ash_test_base.h"
#include "ash/webui/diagnostics_ui/backend/common/histogram_util.h"
#include "ash/webui/diagnostics_ui/backend/system/cpu_usage_data.h"
#include "ash/webui/diagnostics_ui/backend/system/power_manager_client_conversions.h"
#include "ash/webui/diagnostics_ui/mojom/system_data_provider.mojom-forward.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/timer/mock_timer.h"
#include "chromeos/ash/components/mojo_service_manager/fake_mojo_service_manager.h"
#include "chromeos/ash/components/test/ash_test_suite.h"
#include "chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h"
#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd.mojom.h"
#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom-forward.h"
#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom-shared.h"
#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/resource_bundle.h"
namespace ash::diagnostics {
namespace {
namespace healthd_mojom = cros_healthd::mojom;
constexpr char kSystemDataError[] = "ChromeOS.DiagnosticsUi.Error.System";
constexpr char kBatteryDataError[] = "ChromeOS.DiagnosticsUi.Error.Battery";
constexpr char kProbeErrorBatteryInfo[] =
"ChromeOS.DiagnosticsUi.Error.CrosHealthdProbeError.BatteryInfo";
constexpr char kProbeErrorCpuInfo[] =
"ChromeOS.DiagnosticsUi.Error.CrosHealthdProbeError.CpuInfo";
constexpr char kProbeErrorMemoryInfo[] =
"ChromeOS.DiagnosticsUi.Error.CrosHealthdProbeError.MemoryInfo";
constexpr char kProbeErrorSystemInfo[] =
"ChromeOS.DiagnosticsUi.Error.CrosHealthdProbeError.SystemInfo";
void SetProbeTelemetryInfoResponse(healthd_mojom::BatteryInfoPtr battery_info,
healthd_mojom::CpuInfoPtr cpu_info,
healthd_mojom::MemoryInfoPtr memory_info,
healthd_mojom::SystemInfoPtr system_info) {
auto info = healthd_mojom::TelemetryInfo::New();
if (system_info) {
info->system_result =
healthd_mojom::SystemResult::NewSystemInfo(std::move(system_info));
}
if (battery_info) {
info->battery_result =
healthd_mojom::BatteryResult::NewBatteryInfo(std::move(battery_info));
}
if (memory_info) {
info->memory_result =
healthd_mojom::MemoryResult::NewMemoryInfo(std::move(memory_info));
}
if (cpu_info) {
info->cpu_result =
healthd_mojom::CpuResult::NewCpuInfo(std::move(cpu_info));
}
cros_healthd::FakeCrosHealthd::Get()->SetProbeTelemetryInfoResponseForTesting(
info);
}
void SetCrosHealthdSystemInfoResponse(const std::string& board_name,
const std::string& marketing_name,
const std::string& cpu_model,
uint32_t total_memory_kib,
uint16_t cpu_threads_count,
uint32_t cpu_max_clock_speed_khz,
bool has_battery,
const std::string& milestone_version,
const std::string& build_number,
const std::string& patch_number) {
// System info
auto os_info = healthd_mojom::OsInfo::New();
os_info->code_name = board_name;
os_info->marketing_name = marketing_name;
os_info->os_version = healthd_mojom::OsVersion::New(
milestone_version, build_number, patch_number, "unittest-channel");
auto system_info = healthd_mojom::SystemInfo::New();
system_info->os_info = std::move(os_info);
// Battery info
auto battery_info = has_battery ? healthd_mojom::BatteryInfo::New() : nullptr;
// Memory info
auto memory_info = healthd_mojom::MemoryInfo::New();
memory_info->total_memory_kib = total_memory_kib;
// CPU info
auto cpu_info = healthd_mojom::CpuInfo::New();
auto physical_cpu_info = healthd_mojom::PhysicalCpuInfo::New();
auto logical_cpu_info = healthd_mojom::LogicalCpuInfo::New();
logical_cpu_info->max_clock_speed_khz = cpu_max_clock_speed_khz;
physical_cpu_info->logical_cpus.push_back(std::move(logical_cpu_info));
physical_cpu_info->model_name = cpu_model;
cpu_info->num_total_threads = cpu_threads_count;
cpu_info->physical_cpus.emplace_back(std::move(physical_cpu_info));
SetProbeTelemetryInfoResponse(std::move(battery_info), std::move(cpu_info),
std::move(memory_info), std::move(system_info));
}
// Constructs a BatteryInfoPtr. If |temperature| = 0, it will be omitted from
// the response to simulate an empty temperature field.
healthd_mojom::BatteryInfoPtr CreateCrosHealthdBatteryInfoResponse(
int64_t cycle_count,
double voltage_now,
const std::string& vendor,
const std::string& serial_number,
double charge_full_design,
double charge_full,
double voltage_min_design,
const std::string& model_name,
double charge_now,
double current_now,
const std::string& technology,
const std::string& status,
const std::optional<std::string>& manufacture_date,
uint64_t temperature) {
healthd_mojom::NullableUint64Ptr temp_value_ptr(
healthd_mojom::NullableUint64::New());
if (temperature != 0) {
temp_value_ptr->value = temperature;
}
auto battery_info = healthd_mojom::BatteryInfo::New(
cycle_count, voltage_now, vendor, serial_number, charge_full_design,
charge_full, voltage_min_design, model_name, charge_now, current_now,
technology, status, manufacture_date, std::move(temp_value_ptr));
return battery_info;
}
healthd_mojom::BatteryInfoPtr CreateCrosHealthdBatteryInfoResponse(
const std::string& vendor,
double charge_full_design) {
return CreateCrosHealthdBatteryInfoResponse(
/*cycle_count=*/0,
/*voltage_now=*/0,
/*vendor=*/vendor,
/*serial_number=*/"",
/*charge_full_design=*/charge_full_design,
/*charge_full=*/0,
/*voltage_min_design=*/0,
/*model_name=*/"",
/*charge_now=*/0,
/*current_now=*/0,
/*technology=*/"",
/*status=*/"",
/*manufacture_date=*/std::nullopt,
/*temperature=*/0);
}
healthd_mojom::BatteryInfoPtr CreateCrosHealthdBatteryChargeStatusResponse(
double charge_now,
double current_now) {
return CreateCrosHealthdBatteryInfoResponse(
/*cycle_count=*/0,
/*voltage_now=*/0,
/*vendor=*/"",
/*serial_number=*/"",
/*charge_full_design=*/0,
/*charge_full=*/0,
/*voltage_min_design=*/0,
/*model_name=*/"",
/*charge_now=*/charge_now,
/*current_now=*/current_now,
/*technology=*/"",
/*status=*/"",
/*manufacture_date=*/std::nullopt,
/*temperature=*/0);
}
healthd_mojom::BatteryInfoPtr CreateCrosHealthdBatteryHealthResponse(
double charge_full_now,
double charge_full_design,
int32_t cycle_count) {
return CreateCrosHealthdBatteryInfoResponse(
/*cycle_count=*/cycle_count,
/*voltage_now=*/0,
/*vendor=*/"",
/*serial_number=*/"",
/*charge_full_design=*/charge_full_design,
/*charge_full=*/charge_full_now,
/*voltage_min_design=*/0,
/*model_name=*/"",
/*charge_now=*/0,
/*current_now=*/0,
/*technology=*/"",
/*status=*/"",
/*manufacture_date=*/std::nullopt,
/*temperature=*/0);
}
healthd_mojom::ProbeErrorPtr CreateProbeError(
healthd_mojom::ErrorType error_type) {
auto probe_error = healthd_mojom::ProbeError::New();
probe_error->type = error_type;
probe_error->msg = "probe error";
return probe_error;
}
void SetCrosHealthdBatteryInfoResponse(const std::string& vendor,
double charge_full_design) {
healthd_mojom::BatteryInfoPtr battery_info =
CreateCrosHealthdBatteryInfoResponse(vendor, charge_full_design);
SetProbeTelemetryInfoResponse(std::move(battery_info), /*cpu_info=*/nullptr,
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
}
void SetCrosHealthdBatteryChargeStatusResponse(double charge_now,
double current_now) {
healthd_mojom::BatteryInfoPtr battery_info =
CreateCrosHealthdBatteryChargeStatusResponse(charge_now, current_now);
SetProbeTelemetryInfoResponse(std::move(battery_info), /*cpu_info=*/nullptr,
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
}
void SetCrosHealthdBatteryHealthResponse(double charge_full_now,
double charge_full_design,
int32_t cycle_count) {
healthd_mojom::BatteryInfoPtr battery_info =
CreateCrosHealthdBatteryHealthResponse(charge_full_now,
charge_full_design, cycle_count);
SetProbeTelemetryInfoResponse(std::move(battery_info), /*cpu_info=*/nullptr,
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
}
void SetCrosHealthdMemoryUsageResponse(uint32_t total_memory_kib,
uint32_t free_memory_kib,
uint32_t available_memory_kib) {
healthd_mojom::MemoryInfoPtr memory_info = healthd_mojom::MemoryInfo::New(
total_memory_kib, free_memory_kib, available_memory_kib,
/*page_faults_since_last_boot=*/0);
SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr, /*cpu_info=*/nullptr,
/*memory_info=*/std::move(memory_info),
/*system_info=*/nullptr);
}
void SetCrosHealthdCpuResponse(
const std::vector<CpuUsageData>& usage_data,
const std::vector<int32_t>& cpu_temps,
const std::vector<uint32_t>& scaled_cpu_clock_speed) {
auto cpu_info_ptr = healthd_mojom::CpuInfo::New();
auto physical_cpu_info_ptr = healthd_mojom::PhysicalCpuInfo::New();
DCHECK_EQ(usage_data.size(), scaled_cpu_clock_speed.size());
for (size_t i = 0; i < usage_data.size(); ++i) {
const auto& data = usage_data[i];
auto logical_cpu_info_ptr = healthd_mojom::LogicalCpuInfo::New();
logical_cpu_info_ptr->user_time_user_hz = data.GetUserTime();
logical_cpu_info_ptr->system_time_user_hz = data.GetSystemTime();
logical_cpu_info_ptr->idle_time_user_hz = data.GetIdleTime();
logical_cpu_info_ptr->scaling_current_frequency_khz =
scaled_cpu_clock_speed[i];
physical_cpu_info_ptr->logical_cpus.emplace_back(
std::move(logical_cpu_info_ptr));
}
cpu_info_ptr->physical_cpus.push_back(std::move(physical_cpu_info_ptr));
for (const auto& cpu_temp : cpu_temps) {
auto cpu_temp_channel_ptr = healthd_mojom::CpuTemperatureChannel::New();
cpu_temp_channel_ptr->temperature_celsius = cpu_temp;
cpu_info_ptr->temperature_channels.emplace_back(
std::move(cpu_temp_channel_ptr));
}
SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr,
std::move(cpu_info_ptr),
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
}
// Sets the CpuUsage response on cros_healthd. |usage_data| should contain one
// entry for each logical cpu.
void SetCrosHealthdCpuUsageResponse(
const std::vector<CpuUsageData>& usage_data) {
// Use fake temp and scaled clock speed data since none was supplied.
const std::vector<uint32_t> scaled_clock_speeds(usage_data.size(), 10000);
SetCrosHealthdCpuResponse(usage_data, {50}, scaled_clock_speeds);
}
void SetCrosHealthdCpuTemperatureResponse(
const std::vector<int32_t>& cpu_temps) {
// Use fake usage_data and scaled clock speed data since none was supplied.
SetCrosHealthdCpuResponse({CpuUsageData(1000, 1000, 1000)}, cpu_temps,
{10000});
}
void SetCrosHealthdCpuScalingResponse(const std::vector<uint32_t>& cpu_speeds) {
// Use fake temp and usage_data data since none was supplied.
const std::vector<CpuUsageData> usage_data(cpu_speeds.size(),
CpuUsageData(1000, 1000, 1000));
SetCrosHealthdCpuResponse(usage_data, {50}, cpu_speeds);
}
bool AreValidPowerTimes(int64_t time_to_full, int64_t time_to_empty) {
// Exactly one of |time_to_full| or |time_to_empty| must be zero. The other
// can be a positive integer to represent the time to charge/discharge or -1
// to represent that the time is being calculated.
return (time_to_empty == 0 && (time_to_full > 0 || time_to_full == -1)) ||
(time_to_full == 0 && (time_to_empty > 0 || time_to_empty == -1));
}
power_manager::PowerSupplyProperties ConstructPowerSupplyProperties(
power_manager::PowerSupplyProperties::ExternalPower power_source,
power_manager::PowerSupplyProperties::BatteryState battery_state,
bool is_calculating_battery_time,
int64_t time_to_full,
int64_t time_to_empty) {
power_manager::PowerSupplyProperties props;
props.set_external_power(power_source);
props.set_battery_state(battery_state);
if (battery_state ==
power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT) {
// Leave |time_to_full| and |time_to_empty| unset.
return props;
}
DCHECK(AreValidPowerTimes(time_to_full, time_to_empty));
props.set_is_calculating_battery_time(is_calculating_battery_time);
props.set_battery_time_to_full_sec(time_to_full);
props.set_battery_time_to_empty_sec(time_to_empty);
return props;
}
// Sets the PowerSupplyProperties on FakePowerManagerClient. Calling this
// method immediately notifies PowerManagerClient observers. One of
// |time_to_full| or |time_to_empty| must be either -1 or a positive number.
// The other must be 0. If |battery_state| is NOT_PRESENT, both |time_to_full|
// and |time_to_empty| will be left unset.
void SetPowerManagerProperties(
power_manager::PowerSupplyProperties::ExternalPower power_source,
power_manager::PowerSupplyProperties::BatteryState battery_state,
bool is_calculating_battery_time,
int64_t time_to_full,
int64_t time_to_empty) {
power_manager::PowerSupplyProperties props = ConstructPowerSupplyProperties(
power_source, battery_state, is_calculating_battery_time, time_to_full,
time_to_empty);
chromeos::FakePowerManagerClient::Get()->UpdatePowerProperties(props);
}
healthd_mojom::SystemInfoPtr GetDefaultSystemInfoPtr() {
auto os_info = healthd_mojom::OsInfo::New();
os_info->code_name = "board_name";
os_info->marketing_name = "marketing_name";
os_info->os_version =
healthd_mojom::OsVersion::New("M99", "1234", "5.6", "unittest-channel");
auto system_info = healthd_mojom::SystemInfo::New();
system_info->os_info = std::move(os_info);
return system_info;
}
void VerifyChargeStatusResult(
const mojom::BatteryChargeStatusPtr& update,
double charge_now,
double current_now,
power_manager::PowerSupplyProperties::ExternalPower power_source,
power_manager::PowerSupplyProperties::BatteryState battery_state,
bool is_calculating_battery_time,
int64_t time_to_full,
int64_t time_to_empty) {
const uint32_t expected_charge_now_milliamp_hours = charge_now * 1000;
const int32_t expected_current_now_milliamps = current_now * 1000;
mojom::ExternalPowerSource expected_power_source =
ConvertPowerSourceFromProto(power_source);
mojom::BatteryState expected_battery_state =
ConvertBatteryStateFromProto(battery_state);
EXPECT_EQ(expected_charge_now_milliamp_hours,
update->charge_now_milliamp_hours);
EXPECT_EQ(expected_current_now_milliamps, update->current_now_milliamps);
EXPECT_EQ(expected_power_source, update->power_adapter_status);
EXPECT_EQ(expected_battery_state, update->battery_state);
if (expected_battery_state == mojom::BatteryState::kFull) {
EXPECT_EQ(std::u16string(), update->power_time);
return;
}
DCHECK(AreValidPowerTimes(time_to_full, time_to_empty));
const power_manager::PowerSupplyProperties props =
ConstructPowerSupplyProperties(power_source, battery_state,
is_calculating_battery_time, time_to_full,
time_to_empty);
std::u16string expected_power_time =
ConstructPowerTime(expected_battery_state, props);
EXPECT_EQ(expected_power_time, update->power_time);
}
void VerifyHealthResult(const mojom::BatteryHealthPtr& update,
double charge_full_now,
double charge_full_design,
int32_t expected_cycle_count) {
const int32_t expected_charge_full_now_milliamp_hours =
charge_full_now * 1000;
const int32_t expected_charge_full_design_milliamp_hours =
charge_full_design * 1000;
const int8_t expected_battery_wear_percentage =
100 * expected_charge_full_now_milliamp_hours /
expected_charge_full_design_milliamp_hours;
EXPECT_EQ(expected_charge_full_now_milliamp_hours,
update->charge_full_now_milliamp_hours);
EXPECT_EQ(expected_charge_full_design_milliamp_hours,
update->charge_full_design_milliamp_hours);
EXPECT_EQ(expected_cycle_count, update->cycle_count);
EXPECT_EQ(expected_battery_wear_percentage, update->battery_wear_percentage);
}
void VerifyMemoryUsageResult(const mojom::MemoryUsagePtr& update,
uint32_t expected_total_memory_kib,
uint32_t expected_free_memory_kib,
uint32_t expected_available_memory_kib) {
EXPECT_EQ(expected_total_memory_kib, update->total_memory_kib);
EXPECT_EQ(expected_free_memory_kib, update->free_memory_kib);
EXPECT_EQ(expected_available_memory_kib, update->available_memory_kib);
}
void VerifyCpuUsageResult(const mojom::CpuUsagePtr& update,
uint8_t expected_percent_user,
uint8_t expected_percent_system,
uint8_t expected_percent_free) {
EXPECT_EQ(expected_percent_user, update->percent_usage_user);
EXPECT_EQ(expected_percent_system, update->percent_usage_system);
EXPECT_EQ(expected_percent_free, update->percent_usage_free);
}
void VerifyCpuTempResult(const mojom::CpuUsagePtr& update,
uint32_t expected_average_temp) {
EXPECT_EQ(expected_average_temp, update->average_cpu_temp_celsius);
}
void VerifyCpuScalingResult(const mojom::CpuUsagePtr& update,
uint32_t expected_scaled_speed) {
EXPECT_EQ(expected_scaled_speed, update->scaling_current_frequency_khz);
}
void VerifySystemDataErrorBucketCounts(
const base::HistogramTester& tester,
size_t expected_no_data_error,
size_t expected_not_a_number_error,
size_t expected_expectation_not_met_error) {
tester.ExpectBucketCount(kSystemDataError, metrics::DataError::kNoData,
expected_no_data_error);
tester.ExpectBucketCount(kSystemDataError, metrics::DataError::kNotANumber,
expected_not_a_number_error);
tester.ExpectBucketCount(kSystemDataError,
metrics::DataError::kExpectationNotMet,
expected_expectation_not_met_error);
}
void VerifyBatteryDataErrorBucketCounts(
const base::HistogramTester& tester,
size_t expected_no_data_error,
size_t expected_not_a_number_error,
size_t expected_expectation_not_met_error) {
tester.ExpectBucketCount(kBatteryDataError, metrics::DataError::kNoData,
expected_no_data_error);
tester.ExpectBucketCount(kBatteryDataError, metrics::DataError::kNotANumber,
expected_not_a_number_error);
tester.ExpectBucketCount(kBatteryDataError,
metrics::DataError::kExpectationNotMet,
expected_expectation_not_met_error);
}
void VerifyProbeErrorBucketCounts(const base::HistogramTester& tester,
const std::string& metric_name,
size_t expected_unknown_error,
size_t expected_parse_error,
size_t expected_service_unavailable,
size_t expected_system_utility_error,
size_t expected_file_read_error) {
tester.ExpectBucketCount(metric_name, healthd_mojom::ErrorType::kUnknown,
expected_unknown_error);
tester.ExpectBucketCount(metric_name, healthd_mojom::ErrorType::kParseError,
expected_parse_error);
tester.ExpectBucketCount(metric_name,
healthd_mojom::ErrorType::kServiceUnavailable,
expected_service_unavailable);
tester.ExpectBucketCount(metric_name,
healthd_mojom::ErrorType::kSystemUtilityError,
expected_system_utility_error);
tester.ExpectBucketCount(metric_name,
healthd_mojom::ErrorType::kFileReadError,
expected_file_read_error);
}
} // namespace
struct FakeBatteryChargeStatusObserver
: public mojom::BatteryChargeStatusObserver {
// mojom::BatteryChargeStatusObserver
void OnBatteryChargeStatusUpdated(
mojom::BatteryChargeStatusPtr status_ptr) override {
updates.push_back(std::move(status_ptr));
}
// Tracks calls to OnBatteryChargeStatusUpdated. Each call adds an element to
// the vector.
std::vector<mojom::BatteryChargeStatusPtr> updates;
mojo::Receiver<mojom::BatteryChargeStatusObserver> receiver{this};
};
struct FakeBatteryHealthObserver : public mojom::BatteryHealthObserver {
// mojom::BatteryHealthObserver
void OnBatteryHealthUpdated(mojom::BatteryHealthPtr status_ptr) override {
updates.push_back(std::move(status_ptr));
}
// Tracks calls to OnBatteryHealthUpdated. Each call adds an element to
// the vector.
std::vector<mojom::BatteryHealthPtr> updates;
mojo::Receiver<mojom::BatteryHealthObserver> receiver{this};
};
struct FakeMemoryUsageObserver : public mojom::MemoryUsageObserver {
// mojom::MemoryUsageObserver
void OnMemoryUsageUpdated(mojom::MemoryUsagePtr status_ptr) override {
updates.push_back(std::move(status_ptr));
}
// Tracks calls to OnMemoryUsageUpdated. Each call adds an element to
// the vector.
std::vector<mojom::MemoryUsagePtr> updates;
mojo::Receiver<mojom::MemoryUsageObserver> receiver{this};
};
struct FakeCpuUsageObserver : public mojom::CpuUsageObserver {
// mojom::CpuUsageObserver
void OnCpuUsageUpdated(mojom::CpuUsagePtr status_ptr) override {
updates.push_back(std::move(status_ptr));
}
// Tracks calls to OnCpuUsageUpdated. Each call adds an element to
// the vector.
std::vector<mojom::CpuUsagePtr> updates;
mojo::Receiver<mojom::CpuUsageObserver> receiver{this};
};
class SystemDataProviderTest : public AshTestBase {
public:
SystemDataProviderTest() = default;
SystemDataProviderTest(const SystemDataProviderTest&) = delete;
SystemDataProviderTest& operator=(const SystemDataProviderTest&) = delete;
~SystemDataProviderTest() override = default;
void SetUp() override {
ui::ResourceBundle::CleanupSharedInstance();
AshTestSuite::LoadTestResources();
AshTestBase::SetUp();
base::RunLoop().RunUntilIdle();
cros_healthd::FakeCrosHealthd::Initialize();
system_data_provider_ = std::make_unique<SystemDataProvider>();
DiagnosticsLogController::Initialize(
std::make_unique<FakeDiagnosticsBrowserDelegate>());
}
void TearDown() override {
system_data_provider_.reset();
cros_healthd::FakeCrosHealthd::Shutdown();
base::RunLoop().RunUntilIdle();
AshTestBase::TearDown();
}
protected:
std::unique_ptr<SystemDataProvider> system_data_provider_;
private:
::ash::mojo_service_manager::FakeMojoServiceManager fake_service_manager_;
};
TEST_F(SystemDataProviderTest, GetSystemInfo) {
const std::string expected_board_name = "board_name";
const std::string expected_marketing_name = "marketing_name";
const std::string expected_cpu_model = "cpu_model";
const uint32_t expected_total_memory_kib = 1234;
const uint16_t expected_cpu_threads_count = 5678;
const uint32_t expected_cpu_max_clock_speed_khz = 91011;
const bool expected_has_battery = true;
const std::string expected_milestone_version = "M99";
const std::string expected_build_number = "1234";
const std::string expected_patch_number = "5.6";
SetCrosHealthdSystemInfoResponse(
expected_board_name, expected_marketing_name, expected_cpu_model,
expected_total_memory_kib, expected_cpu_threads_count,
expected_cpu_max_clock_speed_khz, expected_has_battery,
expected_milestone_version, expected_build_number, expected_patch_number);
base::RunLoop run_loop;
system_data_provider_->GetSystemInfo(
base::BindLambdaForTesting([&](mojom::SystemInfoPtr ptr) {
ASSERT_TRUE(ptr);
EXPECT_EQ(expected_board_name, ptr->board_name);
EXPECT_EQ(expected_marketing_name, ptr->marketing_name);
EXPECT_EQ(expected_cpu_model, ptr->cpu_model_name);
EXPECT_EQ(expected_total_memory_kib, ptr->total_memory_kib);
EXPECT_EQ(expected_cpu_threads_count, ptr->cpu_threads_count);
EXPECT_EQ(expected_cpu_max_clock_speed_khz,
ptr->cpu_max_clock_speed_khz);
EXPECT_EQ(expected_milestone_version,
ptr->version_info->milestone_version);
EXPECT_EQ(expected_has_battery, ptr->device_capabilities->has_battery);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(SystemDataProviderTest, NoBattery) {
const std::string expected_board_name = "board_name";
const std::string expected_marketing_name = "marketing_name";
const std::string expected_cpu_model = "cpu_model";
const uint32_t expected_total_memory_kib = 1234;
const uint16_t expected_cpu_threads_count = 5678;
const uint32_t expected_cpu_max_clock_speed_khz = 91011;
const bool expected_has_battery = false;
const std::string expected_milestone_version = "M99";
const std::string expected_build_number = "1234";
const std::string expected_patch_number = "5.6";
SetCrosHealthdSystemInfoResponse(
expected_board_name, expected_marketing_name, expected_cpu_model,
expected_total_memory_kib, expected_cpu_threads_count,
expected_cpu_max_clock_speed_khz, expected_has_battery,
expected_milestone_version, expected_build_number, expected_patch_number);
base::RunLoop run_loop;
system_data_provider_->GetSystemInfo(
base::BindLambdaForTesting([&](mojom::SystemInfoPtr ptr) {
ASSERT_TRUE(ptr);
EXPECT_EQ(expected_board_name, ptr->board_name);
EXPECT_EQ(expected_marketing_name, ptr->marketing_name);
EXPECT_EQ(expected_cpu_model, ptr->cpu_model_name);
EXPECT_EQ(expected_total_memory_kib, ptr->total_memory_kib);
EXPECT_EQ(expected_cpu_threads_count, ptr->cpu_threads_count);
EXPECT_EQ(expected_cpu_max_clock_speed_khz,
ptr->cpu_max_clock_speed_khz);
EXPECT_EQ(expected_milestone_version,
ptr->version_info->milestone_version);
EXPECT_EQ(expected_has_battery, ptr->device_capabilities->has_battery);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(SystemDataProviderTest, BatteryInfo) {
const std::string expected_manufacturer = "manufacturer";
const double charge_full_amp_hours = 25;
SetCrosHealthdBatteryInfoResponse(expected_manufacturer,
charge_full_amp_hours);
const uint32_t expected_charge_full_design_milliamp_hours =
charge_full_amp_hours * 1000;
base::RunLoop run_loop;
system_data_provider_->GetBatteryInfo(
base::BindLambdaForTesting([&](mojom::BatteryInfoPtr ptr) {
ASSERT_TRUE(ptr);
EXPECT_EQ(expected_manufacturer, ptr->manufacturer);
EXPECT_EQ(expected_charge_full_design_milliamp_hours,
ptr->charge_full_design_milliamp_hours);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(SystemDataProviderTest, BatteryChargeStatusObserver) {
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetBatteryChargeStatusTimerForTesting(
std::move(timer));
// Setup initial data
const double charge_now_amp_hours = 20;
const double current_now_amps = 2;
const auto power_source =
power_manager::PowerSupplyProperties_ExternalPower_AC;
const auto battery_state =
power_manager::PowerSupplyProperties_BatteryState_CHARGING;
const bool is_calculating_battery_time = false;
const int64_t time_to_full_secs = 1000;
const int64_t time_to_empty_secs = 0;
SetCrosHealthdBatteryChargeStatusResponse(charge_now_amp_hours,
current_now_amps);
SetPowerManagerProperties(power_source, battery_state,
is_calculating_battery_time, time_to_full_secs,
time_to_empty_secs);
// Registering as an observer should trigger one update.
FakeBatteryChargeStatusObserver charge_status_observer;
system_data_provider_->ObserveBatteryChargeStatus(
charge_status_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, charge_status_observer.updates.size());
VerifyChargeStatusResult(charge_status_observer.updates[0],
charge_now_amp_hours, current_now_amps, power_source,
battery_state, is_calculating_battery_time,
time_to_full_secs, time_to_empty_secs);
// Firing the timer should trigger another.
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, charge_status_observer.updates.size());
VerifyChargeStatusResult(charge_status_observer.updates[0],
charge_now_amp_hours, current_now_amps, power_source,
battery_state, is_calculating_battery_time,
time_to_full_secs, time_to_empty_secs);
// Updating the PowerManagerClient Properties should trigger yet another.
const int64_t new_time_to_full_secs = time_to_full_secs - 10;
SetPowerManagerProperties(
power_manager::PowerSupplyProperties_ExternalPower_AC,
power_manager::PowerSupplyProperties_BatteryState_CHARGING,
is_calculating_battery_time, new_time_to_full_secs, time_to_empty_secs);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3u, charge_status_observer.updates.size());
VerifyChargeStatusResult(charge_status_observer.updates[0],
charge_now_amp_hours, current_now_amps, power_source,
battery_state, is_calculating_battery_time,
new_time_to_full_secs, time_to_empty_secs);
}
TEST_F(SystemDataProviderTest, BatteryHealthObserver) {
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetBatteryHealthTimerForTesting(std::move(timer));
// Setup initial data
const double charge_full_now = 20;
const double charge_full_design = 26;
const int32_t cycle_count = 500;
SetCrosHealthdBatteryHealthResponse(charge_full_now, charge_full_design,
cycle_count);
// Registering as an observer should trigger one update.
FakeBatteryHealthObserver health_observer;
system_data_provider_->ObserveBatteryHealth(
health_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, health_observer.updates.size());
VerifyHealthResult(health_observer.updates[0], charge_full_now,
charge_full_design, cycle_count);
// Firing the timer should trigger another.
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, health_observer.updates.size());
VerifyHealthResult(health_observer.updates[1], charge_full_now,
charge_full_design, cycle_count);
// Updating the information in Croshealthd does not trigger an update until
// the timer fires
const int32_t new_cycle_count = cycle_count + 1;
SetCrosHealthdBatteryHealthResponse(charge_full_now, charge_full_design,
new_cycle_count);
EXPECT_EQ(2u, health_observer.updates.size());
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3u, health_observer.updates.size());
VerifyHealthResult(health_observer.updates[2], charge_full_now,
charge_full_design, new_cycle_count);
}
TEST_F(SystemDataProviderTest, MemoryUsageObserver) {
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetMemoryUsageTimerForTesting(std::move(timer));
// Setup initial data
const uint32_t total_memory_kib = 10000;
const uint32_t free_memory_kib = 2000;
const uint32_t available_memory_kib = 4000;
SetCrosHealthdMemoryUsageResponse(total_memory_kib, free_memory_kib,
available_memory_kib);
// Registering as an observer should trigger one update.
FakeMemoryUsageObserver memory_usage_observer;
system_data_provider_->ObserveMemoryUsage(
memory_usage_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, memory_usage_observer.updates.size());
VerifyMemoryUsageResult(memory_usage_observer.updates[0], total_memory_kib,
free_memory_kib, available_memory_kib);
// Firing the timer should trigger another.
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, memory_usage_observer.updates.size());
VerifyMemoryUsageResult(memory_usage_observer.updates[1], total_memory_kib,
free_memory_kib, available_memory_kib);
// Updating the information in Croshealthd does not trigger an update until
// the timer fires
const uint32_t new_available_memory_kib = available_memory_kib + 1000;
SetCrosHealthdMemoryUsageResponse(total_memory_kib, free_memory_kib,
new_available_memory_kib);
EXPECT_EQ(2u, memory_usage_observer.updates.size());
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3u, memory_usage_observer.updates.size());
VerifyMemoryUsageResult(memory_usage_observer.updates[2], total_memory_kib,
free_memory_kib, new_available_memory_kib);
}
TEST_F(SystemDataProviderTest, CpuUsageObserverOneProcessor) {
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetCpuUsageTimerForTesting(std::move(timer));
// Setup initial data
CpuUsageData core_1(1000, 1000, 1000);
SetCrosHealthdCpuUsageResponse({core_1});
// Registering as an observer should trigger one update.
FakeCpuUsageObserver cpu_usage_observer;
system_data_provider_->ObserveCpuUsage(
cpu_usage_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
// There should be one update with no percentages since we could not calculate
// a delta yet.
EXPECT_EQ(1u, cpu_usage_observer.updates.size());
VerifyCpuUsageResult(cpu_usage_observer.updates[0],
/*expected_percent_user=*/0,
/*expected_percent_system=*/0,
/*expected_percent_free=*/0);
CpuUsageData delta(3000, 2500, 4500);
SetCrosHealthdCpuUsageResponse({core_1 + delta});
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, cpu_usage_observer.updates.size());
VerifyCpuUsageResult(cpu_usage_observer.updates[1],
delta.GetUserTime() * 100 / delta.GetTotalTime(),
delta.GetSystemTime() * 100 / delta.GetTotalTime(),
delta.GetIdleTime() * 100 / delta.GetTotalTime());
}
TEST_F(SystemDataProviderTest, CpuUsageObserverTwoProcessor) {
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetCpuUsageTimerForTesting(std::move(timer));
// Setup initial data
CpuUsageData core_1(1000, 1000, 1000);
CpuUsageData core_2(2000, 2000, 2000);
SetCrosHealthdCpuUsageResponse({core_1, core_2});
// Registering as an observer should trigger one update.
FakeCpuUsageObserver cpu_usage_observer;
system_data_provider_->ObserveCpuUsage(
cpu_usage_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
// There should be one update with no percentages since we could not calculate
// a delta yet.
EXPECT_EQ(1u, cpu_usage_observer.updates.size());
VerifyCpuUsageResult(cpu_usage_observer.updates[0],
/*expected_percent_user=*/0,
/*expected_percent_system=*/0,
/*expected_percent_free=*/0);
CpuUsageData core_1_delta(3000, 2500, 4500);
CpuUsageData core_2_delta(1000, 5500, 3500);
SetCrosHealthdCpuUsageResponse(
{core_1 + core_1_delta, core_2 + core_2_delta});
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
// The result should be the averages of the times from the two cores.
const int64_t expected_percent_user = 20;
const int64_t expected_percent_system = 40;
const int64_t expected_percent_free = 40;
EXPECT_EQ(2u, cpu_usage_observer.updates.size());
VerifyCpuUsageResult(cpu_usage_observer.updates[1], expected_percent_user,
expected_percent_system, expected_percent_free);
}
TEST_F(SystemDataProviderTest, CpuUsageObserverTemp) {
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetCpuUsageTimerForTesting(std::move(timer));
// Setup initial data
int temp_1 = 40;
int temp_2 = 50;
int temp_3 = 15;
SetCrosHealthdCpuTemperatureResponse({temp_1, temp_2, temp_3});
// Registering as an observer should trigger one update.
FakeCpuUsageObserver cpu_usage_observer;
system_data_provider_->ObserveCpuUsage(
cpu_usage_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, cpu_usage_observer.updates.size());
VerifyCpuTempResult(cpu_usage_observer.updates[0],
/*expected_average_temp=*/35);
temp_1 = 20;
temp_2 = 25;
temp_3 = 45;
SetCrosHealthdCpuTemperatureResponse({temp_1, temp_2, temp_3});
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, cpu_usage_observer.updates.size());
VerifyCpuTempResult(cpu_usage_observer.updates[1],
/*expected_average_temp=*/30);
temp_1 = 20;
temp_2 = 26;
temp_3 = 46;
SetCrosHealthdCpuTemperatureResponse({temp_1, temp_2, temp_3});
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3u, cpu_usage_observer.updates.size());
// Integer division so `expected_average_temp` should still be 30.
VerifyCpuTempResult(cpu_usage_observer.updates[2],
/*expected_average_temp=*/30);
}
TEST_F(SystemDataProviderTest, CpuUsageScaledClock) {
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetCpuUsageTimerForTesting(std::move(timer));
// Setup initial data
uint32_t core_1_speed = 4000;
uint32_t core_2_speed = 5000;
SetCrosHealthdCpuScalingResponse({core_1_speed, core_2_speed});
// Registering as an observer should trigger one update.
FakeCpuUsageObserver cpu_usage_observer;
system_data_provider_->ObserveCpuUsage(
cpu_usage_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, cpu_usage_observer.updates.size());
VerifyCpuScalingResult(cpu_usage_observer.updates[0],
/*expected_scaled_speed=*/4500);
core_1_speed = 2000;
core_2_speed = 2000;
SetCrosHealthdCpuScalingResponse({core_1_speed, core_2_speed});
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, cpu_usage_observer.updates.size());
VerifyCpuScalingResult(cpu_usage_observer.updates[1],
/*expected_scaled_speed=*/2000);
core_1_speed = 2000;
core_2_speed = 2001;
SetCrosHealthdCpuScalingResponse({core_1_speed, core_2_speed});
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3u, cpu_usage_observer.updates.size());
// Integer division so `expected_scaled_speed` should still be 2000.
VerifyCpuScalingResult(cpu_usage_observer.updates[2],
/*expected_scaled_speed=*/2000);
}
TEST_F(SystemDataProviderTest, GetSystemInfoLogs) {
DiagnosticsLogController::Get()->SetTelemetryLogForTesting(
std::make_unique<TelemetryLog>());
const std::string expected_board_name = "board_name";
const std::string expected_marketing_name = "marketing_name";
const std::string expected_cpu_model = "cpu_model";
const uint32_t expected_total_memory_kib = 1234;
const uint16_t expected_cpu_threads_count = 5678;
const uint32_t expected_cpu_max_clock_speed_khz = 91011;
const bool expected_has_battery = true;
const std::string expected_milestone_version = "M99";
const std::string expected_build_number = "1234";
const std::string expected_patch_number = "5.6";
const std::string expected_full_version = expected_milestone_version + '.' +
expected_build_number + '.' +
expected_patch_number;
SetCrosHealthdSystemInfoResponse(
expected_board_name, expected_marketing_name, expected_cpu_model,
expected_total_memory_kib, expected_cpu_threads_count,
expected_cpu_max_clock_speed_khz, expected_has_battery,
expected_milestone_version, expected_build_number, expected_patch_number);
base::RunLoop run_loop;
system_data_provider_->GetSystemInfo(
base::BindLambdaForTesting([&](mojom::SystemInfoPtr ptr) {
ASSERT_TRUE(ptr);
EXPECT_EQ(expected_board_name, ptr->board_name);
EXPECT_EQ(expected_marketing_name, ptr->marketing_name);
EXPECT_EQ(expected_cpu_model, ptr->cpu_model_name);
EXPECT_EQ(expected_total_memory_kib, ptr->total_memory_kib);
EXPECT_EQ(expected_cpu_threads_count, ptr->cpu_threads_count);
EXPECT_EQ(expected_cpu_max_clock_speed_khz,
ptr->cpu_max_clock_speed_khz);
EXPECT_EQ(expected_milestone_version,
ptr->version_info->milestone_version);
EXPECT_EQ(expected_has_battery, ptr->device_capabilities->has_battery);
run_loop.Quit();
}));
run_loop.Run();
// Check the contents of the telemetry log
const std::vector<std::string> log_contents = base::SplitString(
DiagnosticsLogController::Get()->GetTelemetryLog().GetContents(), "\n",
base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
// Expect one title line and 9 content lines.
EXPECT_EQ(10u, log_contents.size());
const std::string expected_snapshot_time_prefix = "Snapshot Time: ";
EXPECT_GT(log_contents[1].size(), expected_snapshot_time_prefix.size());
EXPECT_TRUE(base::StartsWith(log_contents[1], expected_snapshot_time_prefix));
EXPECT_EQ("Board Name: " + expected_board_name, log_contents[2]);
EXPECT_EQ("Marketing Name: " + expected_marketing_name, log_contents[3]);
EXPECT_EQ("CpuModel Name: " + expected_cpu_model, log_contents[4]);
EXPECT_EQ(
"Total Memory (kib): " + base::NumberToString(expected_total_memory_kib),
log_contents[5]);
EXPECT_EQ(
"Thread Count: " + base::NumberToString(expected_cpu_threads_count),
log_contents[6]);
EXPECT_EQ("Cpu Max Clock Speed (kHz): " +
base::NumberToString(expected_cpu_max_clock_speed_khz),
log_contents[7]);
EXPECT_EQ("Version: " + expected_full_version, log_contents[8]);
EXPECT_EQ("Has Battery: true", log_contents[9]);
}
TEST_F(SystemDataProviderTest, ResetReceiverOnBindInterface) {
// This test simulates a user refreshing the WebUI page. The receiver should
// be reset before binding the new receiver. Otherwise we would get a DCHECK
// error from mojo::Receiver
mojo::Remote<mojom::SystemDataProvider> remote;
system_data_provider_->BindInterface(remote.BindNewPipeAndPassReceiver());
base::RunLoop().RunUntilIdle();
remote.reset();
system_data_provider_->BindInterface(remote.BindNewPipeAndPassReceiver());
base::RunLoop().RunUntilIdle();
}
TEST_F(SystemDataProviderTest, BatteryInfoPtrDataValidation) {
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetBatteryHealthTimerForTesting(std::move(timer));
const std::string vendor = "fake_vendor";
healthd_mojom::BatteryInfoPtr battery_info_all_zero =
CreateCrosHealthdBatteryInfoResponse(vendor, /*charge_full_design*/ 0);
SetProbeTelemetryInfoResponse(std::move(battery_info_all_zero),
/*cpu_info=*/nullptr,
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
// Registering as an observer should trigger one update.
FakeBatteryHealthObserver health_observer;
system_data_provider_->ObserveBatteryHealth(
health_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1ul, health_observer.updates.size());
auto* battery_health_one = health_observer.updates[0].get();
EXPECT_EQ(0, battery_health_one->battery_wear_percentage);
EXPECT_FALSE(isnan(battery_health_one->battery_wear_percentage));
EXPECT_FALSE(isnan(battery_health_one->charge_full_now_milliamp_hours));
EXPECT_FALSE(isnan(battery_health_one->charge_full_design_milliamp_hours));
healthd_mojom::BatteryInfoPtr battery_info_not_a_number =
CreateCrosHealthdBatteryInfoResponse(vendor, /*charge_full_design*/ NAN);
SetProbeTelemetryInfoResponse(std::move(battery_info_not_a_number),
/*cpu_info=*/nullptr,
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
// Trigger timer to update data.
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2ul, health_observer.updates.size());
auto* battery_health_two = health_observer.updates[0].get();
EXPECT_EQ(0, battery_health_two->battery_wear_percentage);
EXPECT_FALSE(isnan(battery_health_two->battery_wear_percentage));
EXPECT_FALSE(isnan(battery_health_two->charge_full_now_milliamp_hours));
EXPECT_FALSE(isnan(battery_health_two->charge_full_design_milliamp_hours));
healthd_mojom::BatteryInfoPtr battery_info_charge_full_nan =
CreateCrosHealthdBatteryInfoResponse(vendor, /*charge_full_design*/ 1);
battery_info_charge_full_nan->charge_full = NAN;
SetProbeTelemetryInfoResponse(std::move(battery_info_charge_full_nan),
/*cpu_info=*/nullptr,
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
// Trigger timer to update data.
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3ul, health_observer.updates.size());
auto* battery_health_three = health_observer.updates[2].get();
EXPECT_EQ(0, battery_health_three->battery_wear_percentage);
EXPECT_FALSE(isnan(battery_health_three->battery_wear_percentage));
EXPECT_FALSE(isnan(battery_health_three->charge_full_now_milliamp_hours));
EXPECT_FALSE(isnan(battery_health_three->charge_full_design_milliamp_hours));
}
TEST_F(SystemDataProviderTest, CpuUsagePtrDataValidation) {
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetCpuUsageTimerForTesting(std::move(timer));
FakeCpuUsageObserver cpu_usage_observer;
system_data_provider_->ObserveCpuUsage(
cpu_usage_observer.receiver.BindNewPipeAndPassRemote());
// Simulate receiving a nullptr for CpuInfo.
SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr, nullptr,
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, cpu_usage_observer.updates.size());
EXPECT_EQ(0u, cpu_usage_observer.updates[0]->average_cpu_temp_celsius);
EXPECT_EQ(0u, cpu_usage_observer.updates[0]->scaling_current_frequency_khz);
// Simulate receiving a CpuInfo with no data set.
healthd_mojom::CpuInfoPtr cpu_info_no_data = healthd_mojom::CpuInfo::New();
SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr,
std::move(cpu_info_no_data),
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
// Trigger timer to update data.
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, cpu_usage_observer.updates.size());
EXPECT_EQ(0u, cpu_usage_observer.updates[1]->average_cpu_temp_celsius);
EXPECT_EQ(0u, cpu_usage_observer.updates[1]->scaling_current_frequency_khz);
// Simulate receiving a CpuInfo with and empty temperature channel and empty
// logical cpu.
std::vector<healthd_mojom::PhysicalCpuInfoPtr> physical_cpus;
physical_cpus.emplace_back(healthd_mojom::PhysicalCpuInfo::New());
std::vector<healthd_mojom::CpuTemperatureChannelPtr> temperature_channels;
healthd_mojom::CpuInfoPtr cpu_info_no_temperatures =
healthd_mojom::CpuInfo::New(/*num_total_threads=*/0u,
healthd_mojom::CpuArchitectureEnum::kUnknown,
std::move(physical_cpus),
std::move(temperature_channels), nullptr);
SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr,
std::move(cpu_info_no_temperatures),
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
// Trigger timer to update data.
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3u, cpu_usage_observer.updates.size());
EXPECT_EQ(0u, cpu_usage_observer.updates[2]->average_cpu_temp_celsius);
EXPECT_EQ(0u, cpu_usage_observer.updates[2]->scaling_current_frequency_khz);
}
// Validate expected metric NoData error triggered when request for SystemInfo
// returns no system info data.
TEST_F(SystemDataProviderTest, RecordSystemDataError_NoSystemInfo) {
base::HistogramTester histogram_tester;
VerifySystemDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr,
/*cpu_info=*/nullptr,
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
base::RunLoop run_loop;
system_data_provider_->GetSystemInfo(
base::BindLambdaForTesting([&](mojom::SystemInfoPtr ptr) {
EXPECT_TRUE(ptr);
VerifySystemDataErrorBucketCounts(
histogram_tester,
/*expected_no_data_error=*/1,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
run_loop.Quit();
}));
run_loop.Run();
}
// Validate expected metric NoData error triggered when request for SystemInfo
// returns no cpu info data.
TEST_F(SystemDataProviderTest, RecordSystemDataError_NoCpuInfo) {
base::HistogramTester histogram_tester;
VerifySystemDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
// System info.
auto system_info = GetDefaultSystemInfoPtr();
// Battery info.
auto battery_info = healthd_mojom::BatteryInfo::New();
// Memory info.
auto memory_info = healthd_mojom::MemoryInfo::New();
memory_info->total_memory_kib = 1234;
SetProbeTelemetryInfoResponse(std::move(battery_info),
/*cpu_info=*/nullptr, std::move(memory_info),
std::move(system_info));
base::RunLoop run_loop;
system_data_provider_->GetSystemInfo(
base::BindLambdaForTesting([&](mojom::SystemInfoPtr ptr) {
EXPECT_TRUE(ptr);
VerifySystemDataErrorBucketCounts(
histogram_tester,
/*expected_no_data_error=*/1,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(SystemDataProviderTest, RecordSystemDataError_NoPhysicalCpuInfo) {
base::HistogramTester histogram_tester;
VerifySystemDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
// System info.
auto system_info = GetDefaultSystemInfoPtr();
// Battery info.
auto battery_info = healthd_mojom::BatteryInfo::New();
// Memory info.
auto memory_info = healthd_mojom::MemoryInfo::New();
memory_info->total_memory_kib = 1234;
// CPU info.
auto cpu_info = healthd_mojom::CpuInfo::New();
auto logical_cpu_info = healthd_mojom::LogicalCpuInfo::New();
logical_cpu_info->max_clock_speed_khz = 91011;
cpu_info->num_total_threads = 5678;
SetProbeTelemetryInfoResponse(std::move(battery_info), std::move(cpu_info),
std::move(memory_info), std::move(system_info));
base::RunLoop run_loop;
system_data_provider_->GetSystemInfo(
base::BindLambdaForTesting([&](mojom::SystemInfoPtr ptr) {
EXPECT_TRUE(ptr);
VerifySystemDataErrorBucketCounts(
histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/1);
run_loop.Quit();
}));
run_loop.Run();
}
// Validate expected metric ExpectationNotMet error triggered when logical cpu
// info is empty.
TEST_F(SystemDataProviderTest, RecordSystemDataError_NoLogicalCpuInfo) {
base::HistogramTester histogram_tester;
VerifySystemDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
// System info.
auto system_info = GetDefaultSystemInfoPtr();
// Battery info.
auto battery_info = healthd_mojom::BatteryInfo::New();
// Memory info.
auto memory_info = healthd_mojom::MemoryInfo::New();
memory_info->total_memory_kib = 1234;
// CPU info.
auto cpu_info = healthd_mojom::CpuInfo::New();
auto physical_cpu_info = healthd_mojom::PhysicalCpuInfo::New();
physical_cpu_info->model_name = "cpu_model";
cpu_info->num_total_threads = 5678;
cpu_info->physical_cpus.emplace_back(std::move(physical_cpu_info));
SetProbeTelemetryInfoResponse(std::move(battery_info), std::move(cpu_info),
std::move(memory_info), std::move(system_info));
base::RunLoop run_loop;
system_data_provider_->GetSystemInfo(
base::BindLambdaForTesting([&](mojom::SystemInfoPtr ptr) {
EXPECT_TRUE(ptr);
VerifySystemDataErrorBucketCounts(
histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/1);
run_loop.Quit();
}));
run_loop.Run();
}
// Validate expected metric ExpectationNotMet error triggered when cpu usage
// delta is zero.
TEST_F(SystemDataProviderTest, RecordSystemDataError_DeltaZero) {
base::HistogramTester histogram_tester;
VerifySystemDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetCpuUsageTimerForTesting(std::move(timer));
// Setup initial data
CpuUsageData core_1(100, 1000, 1000);
SetCrosHealthdCpuUsageResponse({core_1});
// Registering as an observer should trigger one update.
FakeCpuUsageObserver cpu_usage_observer;
system_data_provider_->ObserveCpuUsage(
cpu_usage_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
VerifySystemDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/1);
}
// Validate expected metric triggered when request for BatteryInfo returns a
// ProbeError.
TEST_F(SystemDataProviderTest, RecordProbeError_BatteryInfo) {
base::HistogramTester histogram_tester;
VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorBatteryInfo,
/*expected_unknown_error=*/0,
/*expected_parse_error=*/0,
/*expected_service_unavailable=*/0,
/*expected_system_utility_error=*/0,
/*expected_file_read_error=*/0);
auto info = healthd_mojom::TelemetryInfo::New();
auto battery_result = healthd_mojom::BatteryResult::NewError(
CreateProbeError(healthd_mojom::ErrorType::kParseError));
info->battery_result = std::move(battery_result);
cros_healthd::FakeCrosHealthd::Get()->SetProbeTelemetryInfoResponseForTesting(
info);
base::RunLoop run_loop;
system_data_provider_->GetBatteryInfo(
base::BindLambdaForTesting([&](mojom::BatteryInfoPtr ptr) {
EXPECT_TRUE(ptr);
VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorBatteryInfo,
/*expected_unknown_error=*/0,
/*expected_parse_error=*/1,
/*expected_service_unavailable=*/0,
/*expected_system_utility_error=*/0,
/*expected_file_read_error=*/0);
run_loop.Quit();
}));
run_loop.Run();
}
// Validate expected metric NoData error triggered when request for SystemInfo
// returns no battery info data.
TEST_F(SystemDataProviderTest, RecordBatteryDataError_BatteryInfoNoDataError) {
base::HistogramTester histogram_tester;
VerifyBatteryDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
// Intentionally do not set any battery info.
base::RunLoop run_loop;
system_data_provider_->GetBatteryInfo(
base::BindLambdaForTesting([&](mojom::BatteryInfoPtr ptr) {
ASSERT_TRUE(ptr);
VerifyBatteryDataErrorBucketCounts(
histogram_tester,
/*expected_no_data_error=*/1,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
run_loop.Quit();
}));
run_loop.Run();
}
// Validate expected metric triggered when request for CpuInfo returns a
// ProbeError.
TEST_F(SystemDataProviderTest, RecordProbeError_CpuInfo) {
base::HistogramTester histogram_tester;
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetCpuUsageTimerForTesting(std::move(timer));
FakeCpuUsageObserver cpu_usage_observer;
system_data_provider_->ObserveCpuUsage(
cpu_usage_observer.receiver.BindNewPipeAndPassRemote());
VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorCpuInfo,
/*expected_unknown_error=*/0,
/*expected_parse_error=*/0,
/*expected_service_unavailable=*/0,
/*expected_system_utility_error=*/0,
/*expected_file_read_error=*/0);
auto info = healthd_mojom::TelemetryInfo::New();
cros_healthd::FakeCrosHealthd::Get()->SetProbeTelemetryInfoResponseForTesting(
info);
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
VerifyProbeErrorBucketCounts(
histogram_tester, kProbeErrorCpuInfo, /*expected_unknown_error=*/0,
/*expected_parse_error=*/0, /*expected_service_unavailable=*/0,
/*expected_system_utility_error=*/0, /*expected_file_read_error=*/0);
auto cpu_result = healthd_mojom::CpuResult::NewError(
CreateProbeError(healthd_mojom::ErrorType::kFileReadError));
info->cpu_result = std::move(cpu_result);
cros_healthd::FakeCrosHealthd::Get()->SetProbeTelemetryInfoResponseForTesting(
info);
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorCpuInfo,
/*expected_unknown_error=*/0,
/*expected_parse_error=*/0,
/*expected_service_unavailable=*/0,
/*expected_system_utility_error=*/0,
/*expected_file_read_error=*/1);
}
// Validate expected metric triggered when request for MemoryInfo returns a
// ProbeError.
TEST_F(SystemDataProviderTest, RecordProbeError_MemoryInfo) {
base::HistogramTester histogram_tester;
VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorMemoryInfo,
/*expected_unknown_error=*/0,
/*expected_parse_error=*/0,
/*expected_service_unavailable=*/0,
/*expected_system_utility_error=*/0,
/*expected_file_read_error=*/0);
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetMemoryUsageTimerForTesting(std::move(timer));
FakeMemoryUsageObserver observer;
system_data_provider_->ObserveMemoryUsage(
observer.receiver.BindNewPipeAndPassRemote());
// Clear any pending observerations from initializing.
base::RunLoop().RunUntilIdle();
VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorMemoryInfo,
/*expected_unknown_error=*/0,
/*expected_parse_error=*/0,
/*expected_service_unavailable=*/0,
/*expected_system_utility_error=*/0,
/*expected_file_read_error=*/0);
auto info = healthd_mojom::TelemetryInfo::New();
auto memory_result = healthd_mojom::MemoryResult::NewError(
CreateProbeError(healthd_mojom::ErrorType::kSystemUtilityError));
info->memory_result = std::move(memory_result);
cros_healthd::FakeCrosHealthd::Get()->SetProbeTelemetryInfoResponseForTesting(
info);
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorMemoryInfo,
/*expected_unknown_error=*/0,
/*expected_parse_error=*/0,
/*expected_service_unavailable=*/0,
/*expected_system_utility_error=*/1,
/*expected_file_read_error=*/0);
}
// Validate expected metric triggered when request for SystemInfo returns a
// ProbeError.
TEST_F(SystemDataProviderTest, RecordProbeError_SystemInfo) {
base::HistogramTester histogram_tester;
VerifyProbeErrorBucketCounts(
histogram_tester, kProbeErrorSystemInfo, /*expected_unknown_error=*/0,
/*expected_parse_error=*/0, /*expected_service_unavailable=*/0,
/*expected_system_utility_error=*/0, /*expected_file_read_error=*/0);
auto system_result = healthd_mojom::SystemResult::NewError(
CreateProbeError(healthd_mojom::ErrorType::kServiceUnavailable));
auto info = healthd_mojom::TelemetryInfo::New();
info->system_result = std::move(system_result);
cros_healthd::FakeCrosHealthd::Get()->SetProbeTelemetryInfoResponseForTesting(
info);
base::RunLoop run_loop;
system_data_provider_->GetSystemInfo(
base::BindLambdaForTesting([&](mojom::SystemInfoPtr ptr) {
ASSERT_TRUE(ptr);
VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorSystemInfo,
/*expected_unknown_error=*/0,
/*expected_parse_error=*/0,
/*expected_service_unavailable=*/1,
/*expected_system_utility_error=*/0,
/*expected_file_read_error=*/0);
run_loop.Quit();
}));
run_loop.Run();
}
// Validate expected metric ExpectationNotMet error triggered when
// charge_full_design value from battery_info is zero.
TEST_F(SystemDataProviderTest, RecordBatteryDataError_ChargeFullDesignZero) {
base::HistogramTester histogram_tester;
VerifyBatteryDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
const std::string vendor = "fake_vendor";
healthd_mojom::BatteryInfoPtr battery_info_all_zero =
CreateCrosHealthdBatteryInfoResponse(vendor, /*charge_full_design*/ 0);
SetProbeTelemetryInfoResponse(std::move(battery_info_all_zero),
/*cpu_info=*/nullptr,
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
base::RunLoop run_loop;
system_data_provider_->GetBatteryInfo(
base::BindLambdaForTesting([&](mojom::BatteryInfoPtr ptr) {
ASSERT_TRUE(ptr);
VerifyBatteryDataErrorBucketCounts(
histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/1);
run_loop.Quit();
}));
run_loop.Run();
}
// Validate expected metric ExpectationNotMet error triggered when
// charge_full value from battery_info is zero.
TEST_F(SystemDataProviderTest, RecordBatteryDataError_ChargeFullZero) {
base::HistogramTester histogram_tester;
VerifyBatteryDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
const std::string vendor = "fake_vendor";
healthd_mojom::BatteryInfoPtr battery_info_charge_full_zero =
CreateCrosHealthdBatteryInfoResponse(vendor, /*charge_full_design*/ 1);
SetProbeTelemetryInfoResponse(std::move(battery_info_charge_full_zero),
/*cpu_info=*/nullptr,
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
// Registering as an observer should trigger one update.
FakeBatteryHealthObserver health_observer;
system_data_provider_->ObserveBatteryHealth(
health_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
VerifyBatteryDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/1);
}
// Validate expected metric ExpectationNotMet error triggered when
// has battery info mismatch from two sources.
TEST_F(SystemDataProviderTest, RecordBatteryDataError_HasBatteryInfoMismatch) {
base::HistogramTester histogram_tester;
VerifyBatteryDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
// Registering as an observer should trigger one update.
FakeBatteryChargeStatusObserver charge_status_observer;
system_data_provider_->ObserveBatteryChargeStatus(
charge_status_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
VerifyBatteryDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/1);
}
// Validate expected metric NoData error triggered when battery charge status
// returns null.
TEST_F(SystemDataProviderTest, RecordBatteryDataError_ChargeStatusNull) {
base::HistogramTester histogram_tester;
VerifyBatteryDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/0,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
std::nullopt_t props = std::nullopt;
chromeos::FakePowerManagerClient::Get()->UpdatePowerProperties(props);
// Registering as an observer should trigger one update.
FakeBatteryChargeStatusObserver charge_status_observer;
system_data_provider_->ObserveBatteryChargeStatus(
charge_status_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
VerifyBatteryDataErrorBucketCounts(histogram_tester,
/*expected_no_data_error=*/1,
/*expected_not_a_number_error=*/0,
/*expected_expectation_not_met_error=*/0);
}
} // namespace ash::diagnostics