| // Copyright (c) 2012 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/power_supply.h" |
| |
| #include <algorithm> |
| #include <cmath> |
| #include <map> |
| #include <string> |
| |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/memory/scoped_ptr.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/stringprintf.h> |
| #include <gtest/gtest.h> |
| |
| #include "power_manager/common/clock.h" |
| #include "power_manager/common/fake_prefs.h" |
| #include "power_manager/common/power_constants.h" |
| #include "power_manager/common/test_main_loop_runner.h" |
| #include "power_manager/powerd/system/udev_stub.h" |
| #include "power_manager/proto_bindings/power_supply_properties.pb.h" |
| |
| namespace power_manager { |
| namespace system { |
| |
| namespace { |
| |
| const char kAcType[] = "Mains"; |
| const char kBatteryType[] = "Battery"; |
| const char kUsbType[] = "USB"; |
| const char kUnknownType[] = "Unknown"; |
| |
| const char kCharging[] = "Charging"; |
| const char kDischarging[] = "Discharging"; |
| const char kNotCharging[] = "Not charging"; |
| |
| // Default voltage reported by sysfs. |
| const double kVoltage = 2.5; |
| |
| // Default value for kPowerSupplyFullFactorPref. |
| const double kFullFactor = 0.98; |
| |
| // Starting value used by |power_supply_| as "now". |
| const base::TimeTicks kStartTime = base::TimeTicks::FromInternalValue(1000); |
| |
| class TestObserver : public PowerSupplyObserver { |
| public: |
| TestObserver() {} |
| virtual ~TestObserver() {} |
| |
| // Runs the event loop until OnPowerStatusUpdate() is invoked or a timeout is |
| // hit. Returns true if the method was invoked and false if it wasn't. |
| bool WaitForNotification() { |
| return runner_.StartLoop(base::TimeDelta::FromSeconds(10)); |
| } |
| |
| // PowerSupplyObserver overrides: |
| void OnPowerStatusUpdate() override { |
| runner_.StopLoop(); |
| } |
| |
| private: |
| TestMainLoopRunner runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestObserver); |
| }; |
| |
| } // namespace |
| |
| class PowerSupplyTest : public ::testing::Test { |
| public: |
| PowerSupplyTest() {} |
| |
| virtual void SetUp() { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| ASSERT_TRUE(temp_dir_.IsValid()); |
| |
| prefs_.SetInt64(kLowBatteryShutdownTimePref, 180); |
| prefs_.SetDouble(kPowerSupplyFullFactorPref, kFullFactor); |
| prefs_.SetInt64(kMaxCurrentSamplesPref, 5); |
| prefs_.SetInt64(kMaxChargeSamplesPref, 5); |
| |
| power_supply_.reset(new PowerSupply); |
| test_api_.reset(new PowerSupply::TestApi(power_supply_.get())); |
| test_api_->SetCurrentTime(kStartTime); |
| |
| ac_dir_ = temp_dir_.path().Append("ac"); |
| battery_dir_ = temp_dir_.path().Append("battery"); |
| } |
| |
| protected: |
| // Initializes |power_supply_|. |
| void Init() { |
| power_supply_->Init(temp_dir_.path(), &prefs_, &udev_, |
| false /* log_shutdown_thresholds */); |
| } |
| |
| // Sets the time so that |power_supply_| will believe that the current |
| // has stabilized. |
| void SetStabilizedTime() { |
| const base::TimeTicks now = test_api_->GetCurrentTime(); |
| if (power_supply_->battery_stabilized_timestamp() > now) |
| test_api_->SetCurrentTime(power_supply_->battery_stabilized_timestamp()); |
| } |
| |
| // Writes |value| to |filename| within |dir_|. |
| void WriteValue(const base::FilePath& dir, |
| const std::string& filename, |
| const std::string& value) { |
| CHECK(base::WriteFile(dir.Append(filename), value.c_str(), value.length())); |
| } |
| |
| // Converts |value| to the format used by sysfs and passes it to WriteValue(). |
| void WriteDoubleValue(const base::FilePath& dir, |
| const std::string& filename, |
| double value) { |
| // sysfs stores doubles by multiplying them by 1000000. |
| const int int_value = round(value * 1000000); |
| WriteValue(dir, filename, base::IntToString(int_value)); |
| } |
| |
| // Writes reasonable default values to |temp_dir_|. |
| // The battery's max charge is initialized to 1.0 to make things simple. |
| void WriteDefaultValues(PowerSource source) { |
| ASSERT_TRUE(base::CreateDirectory(ac_dir_)); |
| ASSERT_TRUE(base::CreateDirectory(battery_dir_)); |
| |
| UpdatePowerSourceAndBatteryStatus(source, kAcType, |
| source == POWER_AC ? kCharging : kDischarging); |
| WriteValue(battery_dir_, "type", kBatteryType); |
| WriteValue(battery_dir_, "present", "1"); |
| |
| UpdateChargeAndCurrent(1.0, 0.0); |
| WriteDoubleValue(battery_dir_, "charge_full", 1.0); |
| WriteDoubleValue(battery_dir_, "charge_full_design", 1.0); |
| WriteDoubleValue(battery_dir_, "voltage_now", kVoltage); |
| WriteDoubleValue(battery_dir_, "voltage_min_design", kVoltage); |
| WriteValue(battery_dir_, "cycle_count", base::IntToString(10000)); |
| } |
| |
| // Updates the files describing the power source and battery status. |
| void UpdatePowerSourceAndBatteryStatus(PowerSource power_source, |
| const std::string& ac_type, |
| const std::string& battery_status) { |
| WriteValue(ac_dir_, "online", power_source == POWER_AC ? "1" : "0"); |
| WriteValue(ac_dir_, "type", ac_type); |
| WriteValue(battery_dir_, "status", battery_status); |
| } |
| |
| // Updates the files describing the battery's charge and current. |
| void UpdateChargeAndCurrent(double charge, double current) { |
| WriteDoubleValue(battery_dir_, "charge_now", charge); |
| WriteDoubleValue(battery_dir_, "current_now", current); |
| } |
| |
| // Returns a string describing battery estimates. If |time_to_empty_sec| is |
| // nonzero, the appropriate time-to-shutdown estimate will be calculated |
| // based on kLowBatteryShutdownTimePref. |
| std::string MakeEstimateString(bool calculating, |
| int time_to_empty_sec, |
| int time_to_full_sec) { |
| int time_to_shutdown_sec = time_to_empty_sec; |
| int64 shutdown_sec = 0; |
| if (time_to_empty_sec > 0 && |
| prefs_.GetInt64(kLowBatteryShutdownTimePref, &shutdown_sec)) { |
| time_to_shutdown_sec = |
| std::max(time_to_empty_sec - static_cast<int>(shutdown_sec), 0); |
| } |
| return base::StringPrintf( |
| "calculating=%d empty=%d shutdown=%d full=%d", calculating, |
| time_to_empty_sec, time_to_shutdown_sec, time_to_full_sec); |
| } |
| |
| // Call UpdateStatus() and return a string describing the returned battery |
| // estimates, suitable for comparison with a string built via |
| // MakeEstimateString(). |
| std::string UpdateAndGetEstimateString() { |
| PowerStatus status; |
| if (!UpdateStatus(&status)) |
| return std::string(); |
| return base::StringPrintf( |
| "calculating=%d empty=%d shutdown=%d full=%d", |
| status.is_calculating_battery_time, |
| static_cast<int>(status.battery_time_to_empty.InSeconds()), |
| static_cast<int>(status.battery_time_to_shutdown.InSeconds()), |
| static_cast<int>(status.battery_time_to_full.InSeconds())); |
| } |
| |
| // Refreshes and updates |status|. Returns false if the refresh failed (but |
| // still copies |power_supply_|'s current status to |status|). |
| bool UpdateStatus(PowerStatus* status) WARN_UNUSED_RESULT { |
| CHECK(status); |
| const bool success = power_supply_->RefreshImmediately(); |
| *status = power_supply_->GetPowerStatus(); |
| return success; |
| } |
| |
| FakePrefs prefs_; |
| base::ScopedTempDir temp_dir_; |
| base::FilePath ac_dir_; |
| base::FilePath battery_dir_; |
| UdevStub udev_; |
| scoped_ptr<PowerSupply> power_supply_; |
| scoped_ptr<PowerSupply::TestApi> test_api_; |
| }; |
| |
| // Test system without power supply sysfs (e.g. virtual machine). |
| TEST_F(PowerSupplyTest, NoPowerSupplySysfs) { |
| Init(); |
| PowerStatus power_status; |
| ASSERT_TRUE(UpdateStatus(&power_status)); |
| // In absence of power supply sysfs, default assumption is line power on, no |
| // battery present. |
| EXPECT_TRUE(power_status.line_power_on); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_AC, |
| power_status.external_power); |
| EXPECT_FALSE(power_status.battery_is_present); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_NOT_PRESENT, |
| power_status.battery_state); |
| } |
| |
| // Test line power without battery. |
| TEST_F(PowerSupplyTest, NoBattery) { |
| WriteDefaultValues(POWER_AC); |
| base::DeleteFile(battery_dir_, true); |
| Init(); |
| PowerStatus power_status; |
| ASSERT_TRUE(UpdateStatus(&power_status)); |
| EXPECT_TRUE(power_status.line_power_on); |
| EXPECT_EQ(kAcType, power_status.line_power_type); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_AC, |
| power_status.external_power); |
| EXPECT_FALSE(power_status.battery_is_present); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_NOT_PRESENT, |
| power_status.battery_state); |
| EXPECT_FALSE(power_status.supports_dual_role_devices); |
| } |
| |
| // Test battery charging and discharging status. |
| TEST_F(PowerSupplyTest, ChargingAndDischarging) { |
| const double kCharge = 0.5; |
| const double kCurrent = 1.0; |
| WriteDefaultValues(POWER_AC); |
| UpdateChargeAndCurrent(kCharge, kCurrent); |
| Init(); |
| PowerStatus power_status; |
| ASSERT_TRUE(UpdateStatus(&power_status)); |
| EXPECT_TRUE(power_status.line_power_on); |
| EXPECT_EQ(kAcType, power_status.line_power_type); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_AC, |
| power_status.external_power); |
| EXPECT_TRUE(power_status.battery_is_present); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_CHARGING, |
| power_status.battery_state); |
| EXPECT_DOUBLE_EQ(kCharge * kVoltage, power_status.battery_energy); |
| EXPECT_DOUBLE_EQ(kCurrent * kVoltage, power_status.battery_energy_rate); |
| EXPECT_DOUBLE_EQ(50.0, power_status.battery_percentage); |
| EXPECT_FALSE(power_status.supports_dual_role_devices); |
| |
| // Switch to battery. |
| UpdatePowerSourceAndBatteryStatus(POWER_BATTERY, kAcType, kDischarging); |
| ASSERT_TRUE(UpdateStatus(&power_status)); |
| EXPECT_FALSE(power_status.line_power_on); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_DISCONNECTED, |
| power_status.external_power); |
| EXPECT_TRUE(power_status.battery_is_present); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_DISCHARGING, |
| power_status.battery_state); |
| EXPECT_DOUBLE_EQ(kCharge * kVoltage, power_status.battery_energy); |
| EXPECT_DOUBLE_EQ(kCurrent * kVoltage, power_status.battery_energy_rate); |
| EXPECT_DOUBLE_EQ(50.0, power_status.battery_percentage); |
| |
| // Test with a negative current. |
| UpdateChargeAndCurrent(kCharge, -kCurrent); |
| ASSERT_TRUE(UpdateStatus(&power_status)); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_DISCHARGING, |
| power_status.battery_state); |
| EXPECT_DOUBLE_EQ(kCharge * kVoltage, power_status.battery_energy); |
| EXPECT_DOUBLE_EQ(kCurrent * kVoltage, power_status.battery_energy_rate); |
| } |
| |
| // Tests that the line power source doesn't need to be named "Mains". |
| TEST_F(PowerSupplyTest, NonMainsLinePower) { |
| const char kType[] = "ArbitraryName"; |
| WriteDefaultValues(POWER_AC); |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kType, kCharging); |
| Init(); |
| PowerStatus power_status; |
| ASSERT_TRUE(UpdateStatus(&power_status)); |
| EXPECT_TRUE(power_status.line_power_on); |
| EXPECT_EQ(kType, power_status.line_power_type); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_AC, |
| power_status.external_power); |
| EXPECT_TRUE(power_status.battery_is_present); |
| EXPECT_FALSE(power_status.supports_dual_role_devices); |
| } |
| |
| TEST_F(PowerSupplyTest, DualRolePowerSources) { |
| // Delete the AC power supply and report two line power sources, both |
| // initially offline. |
| WriteDefaultValues(POWER_BATTERY); |
| base::DeleteFile(ac_dir_, true); |
| |
| const char kLine1Id[] = "line1"; |
| const char kLine1Manufacturer[] = "04fe"; |
| const char kLine1ModelName[] = "0256"; |
| const base::FilePath line1_dir = temp_dir_.path().Append(kLine1Id); |
| ASSERT_TRUE(base::CreateDirectory(line1_dir)); |
| WriteValue(line1_dir, "type", kUnknownType); |
| WriteValue(line1_dir, "online", "0"); |
| WriteValue(line1_dir, "status", kNotCharging); |
| WriteDoubleValue(line1_dir, "current_max", 0.0); |
| WriteDoubleValue(line1_dir, "voltage_max_design", 0.0); |
| WriteValue(line1_dir, "manufacturer", kLine1Manufacturer); |
| WriteValue(line1_dir, "model_name", kLine1ModelName); |
| |
| const char kLine2Id[] = "line2"; |
| const char kLine2Manufacturer[] = "587b"; |
| const char kLine2ModelName[] = "3402"; |
| const base::FilePath line2_dir = temp_dir_.path().Append(kLine2Id); |
| ASSERT_TRUE(base::CreateDirectory(line2_dir)); |
| WriteValue(line2_dir, "type", kUnknownType); |
| WriteValue(line2_dir, "online", "0"); |
| WriteValue(line2_dir, "status", kNotCharging); |
| WriteDoubleValue(line2_dir, "current_max", 0.0); |
| WriteDoubleValue(line2_dir, "voltage_max_design", 0.0); |
| WriteValue(line2_dir, "manufacturer", kLine2Manufacturer); |
| WriteValue(line2_dir, "model_name", kLine2ModelName); |
| |
| // Set the minimum power for being classified as an AC charger. |
| const double kCurrentMax = 2.0; |
| const double kVoltageMax = 12.0; |
| prefs_.SetDouble(kUsbMinAcWattsPref, kCurrentMax * kVoltageMax); |
| |
| Init(); |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_FALSE(status.line_power_on); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_DISCONNECTED, |
| status.external_power); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_DISCHARGING, |
| status.battery_state); |
| EXPECT_EQ(0u, status.available_external_power_sources.size()); |
| EXPECT_EQ("", status.external_power_source_id); |
| EXPECT_TRUE(status.supports_dual_role_devices); |
| |
| // Start charging from the first power source at a high level. |
| WriteValue(line1_dir, "type", kUsbType); |
| WriteValue(line1_dir, "online", "1"); |
| WriteValue(line1_dir, "status", kCharging); |
| WriteDoubleValue(line1_dir, "current_max", kCurrentMax); |
| WriteDoubleValue(line1_dir, "voltage_max_design", kVoltageMax); |
| WriteValue(battery_dir_, "status", kCharging); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_TRUE(status.line_power_on); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_AC, status.external_power); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_FULL, status.battery_state); |
| ASSERT_EQ(1u, status.available_external_power_sources.size()); |
| EXPECT_EQ(kLine1Id, status.available_external_power_sources[0].id); |
| EXPECT_EQ(kLine1Manufacturer, |
| status.available_external_power_sources[0].manufacturer_id); |
| EXPECT_EQ(kLine1ModelName, |
| status.available_external_power_sources[0].model_id); |
| EXPECT_EQ(kCurrentMax * kVoltageMax, |
| status.available_external_power_sources[0].max_power); |
| EXPECT_FALSE(status.available_external_power_sources[0].active_by_default); |
| EXPECT_EQ(kLine1Id, status.external_power_source_id); |
| EXPECT_TRUE(status.supports_dual_role_devices); |
| |
| // Disconnect the first power source and start charging from the second one at |
| // a low power. |
| WriteValue(line1_dir, "type", kUnknownType); |
| WriteValue(line1_dir, "online", "0"); |
| WriteValue(line1_dir, "status", kNotCharging); |
| WriteValue(line2_dir, "type", kUsbType); |
| WriteValue(line2_dir, "online", "1"); |
| WriteValue(line2_dir, "status", kCharging); |
| const double kCurrentFactor = 0.5; |
| WriteDoubleValue(line2_dir, "current_max", kCurrentMax * kCurrentFactor); |
| WriteDoubleValue(line2_dir, "voltage_max_design", kVoltageMax); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_TRUE(status.line_power_on); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_USB, status.external_power); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_FULL, status.battery_state); |
| ASSERT_EQ(1u, status.available_external_power_sources.size()); |
| EXPECT_EQ(kLine2Id, status.available_external_power_sources[0].id); |
| EXPECT_EQ(kLine2Manufacturer, |
| status.available_external_power_sources[0].manufacturer_id); |
| EXPECT_EQ(kLine2ModelName, |
| status.available_external_power_sources[0].model_id); |
| EXPECT_EQ(kCurrentMax * kCurrentFactor * kVoltageMax, |
| status.available_external_power_sources[0].max_power); |
| EXPECT_FALSE(status.available_external_power_sources[0].active_by_default); |
| EXPECT_EQ(kLine2Id, status.external_power_source_id); |
| |
| // Now discharge from the first power source (while still charging from the |
| // second one) and check that it's ignored. |
| WriteValue(line1_dir, "type", kUsbType); |
| WriteValue(line1_dir, "online", "1"); |
| WriteValue(line1_dir, "status", kDischarging); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_TRUE(status.line_power_on); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_USB, status.external_power); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_FULL, status.battery_state); |
| ASSERT_EQ(2u, status.available_external_power_sources.size()); |
| EXPECT_EQ(kLine1Id, status.available_external_power_sources[0].id); |
| EXPECT_EQ(kLine1Manufacturer, |
| status.available_external_power_sources[0].manufacturer_id); |
| EXPECT_EQ(kLine1ModelName, |
| status.available_external_power_sources[0].model_id); |
| EXPECT_EQ(kCurrentMax * kVoltageMax, |
| status.available_external_power_sources[0].max_power); |
| EXPECT_FALSE(status.available_external_power_sources[0].active_by_default); |
| EXPECT_EQ(kLine2Id, status.available_external_power_sources[1].id); |
| EXPECT_EQ(kLine2Manufacturer, |
| status.available_external_power_sources[1].manufacturer_id); |
| EXPECT_EQ(kLine2ModelName, |
| status.available_external_power_sources[1].model_id); |
| EXPECT_EQ(kCurrentMax * kCurrentFactor * kVoltageMax, |
| status.available_external_power_sources[1].max_power); |
| EXPECT_FALSE(status.available_external_power_sources[1].active_by_default); |
| EXPECT_EQ(kLine2Id, status.external_power_source_id); |
| |
| // Request switching to the first power source. |
| EXPECT_TRUE(power_supply_->SetPowerSource(kLine1Id)); |
| std::string value; |
| EXPECT_TRUE(base::ReadFileToString( |
| line1_dir.Append(PowerSupply::kChargeControlLimitMaxFile), &value)); |
| EXPECT_EQ("0", value); |
| |
| // Now switch to the second one. |
| EXPECT_TRUE(power_supply_->SetPowerSource(kLine2Id)); |
| EXPECT_TRUE(base::ReadFileToString( |
| line2_dir.Append(PowerSupply::kChargeControlLimitMaxFile), &value)); |
| EXPECT_EQ("0", value); |
| |
| // Passing an empty ID should result in -1 getting written to the active power |
| // source's limit file (resulting in a switch to the battery). |
| EXPECT_TRUE(power_supply_->SetPowerSource("")); |
| EXPECT_TRUE(base::ReadFileToString( |
| line2_dir.Append(PowerSupply::kChargeControlLimitMaxFile), &value)); |
| EXPECT_EQ("-1", value); |
| |
| // Ignore invalid IDs. |
| EXPECT_FALSE(power_supply_->SetPowerSource("bogus")); |
| EXPECT_FALSE(power_supply_->SetPowerSource(".")); |
| EXPECT_FALSE(power_supply_->SetPowerSource("..")); |
| EXPECT_FALSE(power_supply_->SetPowerSource("../")); |
| EXPECT_FALSE(power_supply_->SetPowerSource(line1_dir.value())); |
| |
| // If the kernel reports a dedicated charger by using the "Mains" type rather |
| // than "USB", powerd should report it as being active by default. |
| WriteValue(line2_dir, "type", kAcType); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_USB, status.external_power); |
| ASSERT_EQ(2u, status.available_external_power_sources.size()); |
| EXPECT_TRUE(status.available_external_power_sources[1].active_by_default); |
| EXPECT_EQ(kLine2Id, status.external_power_source_id); |
| |
| // The maximum power should be checked even for dedicated chargers. |
| WriteDoubleValue(line2_dir, "current_max", kCurrentMax); |
| WriteDoubleValue(line2_dir, "voltage_max_design", kVoltageMax); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_AC, status.external_power); |
| ASSERT_EQ(2u, status.available_external_power_sources.size()); |
| EXPECT_TRUE(status.available_external_power_sources[1].active_by_default); |
| EXPECT_EQ(kLine2Id, status.external_power_source_id); |
| |
| // A maximum power of 0 watts should be disregarded. |
| WriteDoubleValue(line2_dir, "current_max", 0.0); |
| WriteDoubleValue(line2_dir, "voltage_max_design", 0.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_AC, status.external_power); |
| ASSERT_EQ(2u, status.available_external_power_sources.size()); |
| EXPECT_TRUE(status.available_external_power_sources[1].active_by_default); |
| EXPECT_EQ(kLine2Id, status.external_power_source_id); |
| } |
| |
| TEST_F(PowerSupplyTest, ChargingPortNames) { |
| // Write a pref describing two charging ports and say that we're charging from |
| // the first one. PowerSupply will sort the sources by name. |
| const char kSecondName[] = "port2"; |
| prefs_.SetString( |
| kChargingPortsPref, |
| base::StringPrintf("%s LEFT_FRONT\n%s RIGHT_BACK", |
| ac_dir_.BaseName().value().c_str(), kSecondName)); |
| WriteDefaultValues(POWER_AC); |
| |
| // Connect a second, idle power source. |
| const base::FilePath kSecondDir = temp_dir_.path().Append(kSecondName); |
| ASSERT_TRUE(base::CreateDirectory(kSecondDir)); |
| WriteValue(kSecondDir, "online", "1"); |
| WriteValue(kSecondDir, "type", kUsbType); |
| WriteValue(kSecondDir, "status", kNotCharging); |
| |
| // Add a third source that isn't described by the pref. |
| const char kThirdName[] = "port3"; |
| const base::FilePath kThirdDir = temp_dir_.path().Append(kThirdName); |
| ASSERT_TRUE(base::CreateDirectory(kThirdDir)); |
| WriteValue(kThirdDir, "online", "1"); |
| WriteValue(kThirdDir, "type", kUsbType); |
| WriteValue(kThirdDir, "status", kNotCharging); |
| |
| // Check that all three sources' ports are reported correctly. |
| Init(); |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| ASSERT_EQ(3u, status.available_external_power_sources.size()); |
| EXPECT_EQ(PowerSupplyProperties_PowerSource_Port_LEFT_FRONT, |
| status.available_external_power_sources[0].port); |
| EXPECT_EQ(PowerSupplyProperties_PowerSource_Port_RIGHT_BACK, |
| status.available_external_power_sources[1].port); |
| EXPECT_EQ(PowerSupplyProperties_PowerSource_Port_UNKNOWN, |
| status.available_external_power_sources[2].port); |
| } |
| |
| TEST_F(PowerSupplyTest, IgnorePeripherals) { |
| // Power supplies corresponding to external peripherals (i.e. with a "scope" |
| // of "Device") should be ignored. |
| WriteDefaultValues(POWER_AC); |
| WriteValue(ac_dir_, "scope", "Device"); |
| WriteValue(battery_dir_, "status", kDischarging); |
| |
| Init(); |
| PowerStatus power_status; |
| ASSERT_TRUE(UpdateStatus(&power_status)); |
| EXPECT_FALSE(power_status.line_power_on); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_DISCONNECTED, |
| power_status.external_power); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_DISCHARGING, |
| power_status.battery_state); |
| } |
| |
| // Test battery reporting energy instead of charge. |
| TEST_F(PowerSupplyTest, EnergyDischarging) { |
| WriteDefaultValues(POWER_BATTERY); |
| base::DeleteFile(battery_dir_.Append("charge_full"), false); |
| base::DeleteFile(battery_dir_.Append("charge_full_design"), false); |
| base::DeleteFile(battery_dir_.Append("charge_now"), false); |
| base::DeleteFile(battery_dir_.Append("current_now"), false); |
| base::DeleteFile(battery_dir_.Append("voltage_min_design"), false); |
| |
| const double kNominalVoltage = kVoltage + 1.0; |
| const double kChargeFull = 2.40; |
| const double kChargeNow = 1.80; |
| const double kCurrentNow = 0.20; |
| // Use nominal voltage when calculating remaining battery charge and the |
| // current voltage when calculating current. |
| const double kEnergyFull = kChargeFull * kNominalVoltage; |
| const double kEnergyNow = kChargeNow * kNominalVoltage; |
| const double kPowerNow = kCurrentNow * kVoltage; |
| const double kEnergyRate = kCurrentNow * kVoltage; |
| const double kPercentage = 100.0 * kChargeNow / kChargeFull; |
| WriteDoubleValue(battery_dir_, "energy_full", kEnergyFull); |
| WriteDoubleValue(battery_dir_, "energy_full_design", kEnergyFull); |
| WriteDoubleValue(battery_dir_, "energy_now", kEnergyNow); |
| WriteDoubleValue(battery_dir_, "power_now", kPowerNow); |
| WriteDoubleValue(battery_dir_, "voltage_min_design", kNominalVoltage); |
| |
| Init(); |
| PowerStatus power_status; |
| ASSERT_TRUE(UpdateStatus(&power_status)); |
| EXPECT_FALSE(power_status.line_power_on); |
| EXPECT_TRUE(power_status.battery_is_present); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_DISCHARGING, |
| power_status.battery_state); |
| EXPECT_DOUBLE_EQ(kEnergyNow, power_status.battery_energy); |
| EXPECT_DOUBLE_EQ(kEnergyRate, power_status.battery_energy_rate); |
| EXPECT_DOUBLE_EQ(kPercentage, power_status.battery_percentage); |
| |
| // Charge values should be computed. |
| EXPECT_DOUBLE_EQ(kChargeFull, power_status.battery_charge_full); |
| EXPECT_DOUBLE_EQ(kChargeFull, power_status.battery_charge_full_design); |
| EXPECT_DOUBLE_EQ(kChargeNow, power_status.battery_charge); |
| EXPECT_DOUBLE_EQ(kCurrentNow, power_status.battery_current); |
| |
| WriteDoubleValue(battery_dir_, "power_now", -kPowerNow); |
| ASSERT_TRUE(UpdateStatus(&power_status)); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_DISCHARGING, |
| power_status.battery_state); |
| EXPECT_DOUBLE_EQ(kEnergyNow, power_status.battery_energy); |
| EXPECT_DOUBLE_EQ(kEnergyRate, power_status.battery_energy_rate); |
| EXPECT_DOUBLE_EQ(kPercentage, power_status.battery_percentage); |
| } |
| |
| TEST_F(PowerSupplyTest, PollDelays) { |
| WriteDefaultValues(POWER_AC); |
| |
| const base::TimeDelta kPollDelay = base::TimeDelta::FromSeconds(30); |
| const base::TimeDelta kStartupDelay = base::TimeDelta::FromSeconds(6); |
| const base::TimeDelta kACDelay = base::TimeDelta::FromSeconds(7); |
| const base::TimeDelta kBatteryDelay = base::TimeDelta::FromSeconds(8); |
| const base::TimeDelta kResumeDelay = base::TimeDelta::FromSeconds(10); |
| const base::TimeDelta kSlack = base::TimeDelta::FromMilliseconds( |
| PowerSupply::kBatteryStabilizedSlackMs); |
| |
| prefs_.SetInt64(kBatteryPollIntervalPref, kPollDelay.InMilliseconds()); |
| prefs_.SetInt64(kBatteryStabilizedAfterStartupMsPref, |
| kStartupDelay.InMilliseconds()); |
| prefs_.SetInt64(kBatteryStabilizedAfterLinePowerConnectedMsPref, |
| kACDelay.InMilliseconds()); |
| prefs_.SetInt64(kBatteryStabilizedAfterLinePowerDisconnectedMsPref, |
| kBatteryDelay.InMilliseconds()); |
| prefs_.SetInt64(kBatteryStabilizedAfterResumeMsPref, |
| kResumeDelay.InMilliseconds()); |
| |
| base::TimeTicks current_time = kStartTime; |
| Init(); |
| |
| // The battery times should be reported as "calculating" just after |
| // initialization. |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_TRUE(status.line_power_on); |
| EXPECT_TRUE(status.is_calculating_battery_time); |
| EXPECT_EQ((kStartupDelay + kSlack).InMilliseconds(), |
| test_api_->current_poll_delay().InMilliseconds()); |
| |
| // After enough time has elapsed, the battery times should be reported. |
| current_time += kStartupDelay + kSlack; |
| test_api_->SetCurrentTime(current_time); |
| ASSERT_TRUE(test_api_->TriggerPollTimeout()); |
| status = power_supply_->GetPowerStatus(); |
| EXPECT_TRUE(status.line_power_on); |
| EXPECT_FALSE(status.is_calculating_battery_time); |
| EXPECT_EQ(kPollDelay.InMilliseconds(), |
| test_api_->current_poll_delay().InMilliseconds()); |
| |
| // Polling should stop when the system is about to suspend. |
| power_supply_->SetSuspended(true); |
| EXPECT_EQ(0, test_api_->current_poll_delay().InMilliseconds()); |
| |
| // After resuming, the status should be updated immediately and the |
| // battery times should be reported as "calculating" again. |
| current_time += base::TimeDelta::FromSeconds(120); |
| test_api_->SetCurrentTime(current_time); |
| UpdatePowerSourceAndBatteryStatus(POWER_BATTERY, kAcType, kDischarging); |
| power_supply_->SetSuspended(false); |
| status = power_supply_->GetPowerStatus(); |
| EXPECT_FALSE(status.line_power_on); |
| EXPECT_TRUE(status.is_calculating_battery_time); |
| EXPECT_EQ((kResumeDelay + kSlack).InMilliseconds(), |
| test_api_->current_poll_delay().InMilliseconds()); |
| |
| // Check that the updated times are returned after a delay. |
| current_time += kResumeDelay + kSlack; |
| test_api_->SetCurrentTime(current_time); |
| ASSERT_TRUE(test_api_->TriggerPollTimeout()); |
| status = power_supply_->GetPowerStatus(); |
| EXPECT_FALSE(status.line_power_on); |
| EXPECT_FALSE(status.is_calculating_battery_time); |
| |
| // Connect AC, report a udev event, and check that the status is updated. |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kAcType, kCharging); |
| power_supply_->OnUdevEvent( |
| PowerSupply::kUdevSubsystem, "AC", UDEV_ACTION_CHANGE); |
| status = power_supply_->GetPowerStatus(); |
| EXPECT_TRUE(status.line_power_on); |
| EXPECT_TRUE(status.is_calculating_battery_time); |
| EXPECT_EQ((kACDelay + kSlack).InMilliseconds(), |
| test_api_->current_poll_delay().InMilliseconds()); |
| |
| // After the delay, estimates should be made again. |
| current_time += kACDelay + kSlack; |
| test_api_->SetCurrentTime(current_time); |
| ASSERT_TRUE(test_api_->TriggerPollTimeout()); |
| status = power_supply_->GetPowerStatus(); |
| EXPECT_TRUE(status.line_power_on); |
| EXPECT_FALSE(status.is_calculating_battery_time); |
| |
| // Now test the delay when going back to battery power. |
| UpdatePowerSourceAndBatteryStatus(POWER_BATTERY, kAcType, kDischarging); |
| power_supply_->OnUdevEvent( |
| PowerSupply::kUdevSubsystem, "AC", UDEV_ACTION_CHANGE); |
| status = power_supply_->GetPowerStatus(); |
| EXPECT_FALSE(status.line_power_on); |
| EXPECT_TRUE(status.is_calculating_battery_time); |
| EXPECT_EQ((kBatteryDelay + kSlack).InMilliseconds(), |
| test_api_->current_poll_delay().InMilliseconds()); |
| |
| // After the delay, estimates should be made again. |
| current_time += kBatteryDelay + kSlack; |
| test_api_->SetCurrentTime(current_time); |
| ASSERT_TRUE(test_api_->TriggerPollTimeout()); |
| status = power_supply_->GetPowerStatus(); |
| EXPECT_FALSE(status.line_power_on); |
| EXPECT_FALSE(status.is_calculating_battery_time); |
| } |
| |
| TEST_F(PowerSupplyTest, UpdateBatteryTimeEstimates) { |
| // Start out with the battery 50% full and an unset current. |
| WriteDefaultValues(POWER_AC); |
| UpdateChargeAndCurrent(0.5, 0.0); |
| prefs_.SetDouble(kPowerSupplyFullFactorPref, 1.0); |
| // To simplify this test, average just the last two samples. |
| prefs_.SetInt64(kMaxCurrentSamplesPref, 2); |
| Init(); |
| |
| EXPECT_EQ(MakeEstimateString(true, 0, 0), UpdateAndGetEstimateString()); |
| |
| // Set the current such that it'll take an hour to charge fully and |
| // advance the clock so the current will be used. |
| UpdateChargeAndCurrent(0.5, 0.5); |
| SetStabilizedTime(); |
| EXPECT_EQ(MakeEstimateString(false, 0, 3600), UpdateAndGetEstimateString()); |
| |
| // Let half an hour pass and report that the battery is 75% full. |
| test_api_->AdvanceTime(base::TimeDelta::FromMinutes(30)); |
| UpdateChargeAndCurrent(0.75, 0.5); |
| EXPECT_EQ(MakeEstimateString(false, 0, 1800), UpdateAndGetEstimateString()); |
| |
| // After a current reading of 1.0, the averaged current should be (0.5 + 1.0) |
| // / 2 = 0.75. The remaining 0.25 of charge to get to 100% should take twenty |
| // minutes. |
| UpdateChargeAndCurrent(0.75, 1.0); |
| EXPECT_EQ(MakeEstimateString(false, 0, 1200), UpdateAndGetEstimateString()); |
| |
| // Fifteen minutes later, set the current to 0.25 (giving an average of (1.0 + |
| // 0.25) / 2 = 0.625) and report an increased charge. There should be 0.125 / |
| // 0.625 * 3600 = 720 seconds until the battery is full. |
| test_api_->AdvanceTime(base::TimeDelta::FromMinutes(15)); |
| UpdateChargeAndCurrent(0.875, 0.25); |
| EXPECT_EQ(MakeEstimateString(false, 0, 720), UpdateAndGetEstimateString()); |
| |
| // Disconnect the charger and report an immediate drop in charge and |
| // current. The current shouldn't be used yet. |
| UpdatePowerSourceAndBatteryStatus(POWER_BATTERY, kAcType, kDischarging); |
| UpdateChargeAndCurrent(0.5, -0.5); |
| EXPECT_EQ(MakeEstimateString(true, 0, 0), UpdateAndGetEstimateString()); |
| |
| // After the current has had time to stabilize, the average should be |
| // reset and the time-to-empty should be estimated. |
| SetStabilizedTime(); |
| EXPECT_EQ(MakeEstimateString(false, 3600, 0), UpdateAndGetEstimateString()); |
| |
| // Thirty minutes later, decrease the charge and report a significantly |
| // higher current. |
| test_api_->AdvanceTime(base::TimeDelta::FromMinutes(30)); |
| UpdateChargeAndCurrent(0.25, -1.5); |
| EXPECT_EQ(MakeEstimateString(false, 900, 0), UpdateAndGetEstimateString()); |
| |
| // A current report of 0 should be ignored. |
| UpdateChargeAndCurrent(0.25, 0.0); |
| EXPECT_EQ(MakeEstimateString(false, 900, 0), UpdateAndGetEstimateString()); |
| |
| // Suspend, change the current, and resume. The battery time should be |
| // reported as "calculating". |
| power_supply_->SetSuspended(true); |
| UpdateChargeAndCurrent(0.25, -2.5); |
| test_api_->AdvanceTime(base::TimeDelta::FromSeconds(8)); |
| power_supply_->SetSuspended(false); |
| EXPECT_EQ(MakeEstimateString(true, 0, 0), UpdateAndGetEstimateString()); |
| |
| // Wait for the current to stabilize. The last valid sample (-1.5) should be |
| // averaged with the latest one. |
| SetStabilizedTime(); |
| EXPECT_EQ(MakeEstimateString(false, 450, 0), UpdateAndGetEstimateString()); |
| |
| // Switch back to line power. Since the current delivered on line power can |
| // vary greatly, the previous sample should be discarded. |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kAcType, kCharging); |
| UpdateChargeAndCurrent(0.5, 0.25); |
| EXPECT_EQ(MakeEstimateString(true, 0, 0), UpdateAndGetEstimateString()); |
| SetStabilizedTime(); |
| EXPECT_EQ(MakeEstimateString(false, 0, 7200), UpdateAndGetEstimateString()); |
| |
| // Go back to battery and check that the previous on-battery current sample |
| // (-2.5) is included in the average. |
| UpdatePowerSourceAndBatteryStatus(POWER_BATTERY, kAcType, kDischarging); |
| UpdateChargeAndCurrent(0.5, -1.5); |
| EXPECT_EQ(MakeEstimateString(true, 0, 0), UpdateAndGetEstimateString()); |
| SetStabilizedTime(); |
| EXPECT_EQ(MakeEstimateString(false, 900, 0), UpdateAndGetEstimateString()); |
| } |
| |
| TEST_F(PowerSupplyTest, UsbBatteryTimeEstimates) { |
| WriteDefaultValues(POWER_AC); |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kUsbType, kCharging); |
| UpdateChargeAndCurrent(0.5, 1.0); |
| prefs_.SetDouble(kPowerSupplyFullFactorPref, 1.0); |
| prefs_.SetInt64(kMaxCurrentSamplesPref, 2); |
| Init(); |
| |
| // Start out charging on USB power. |
| SetStabilizedTime(); |
| EXPECT_EQ(MakeEstimateString(false, 0, 1800), UpdateAndGetEstimateString()); |
| |
| // Now discharge while still on USB. Since the averaged charge is still |
| // positive, we should avoid providing a time-to-empty estimate. |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kUsbType, kDischarging); |
| UpdateChargeAndCurrent(0.5, -0.5); |
| EXPECT_EQ(MakeEstimateString(false, -1, 0), UpdateAndGetEstimateString()); |
| |
| // After another sample brings the average current to -1.0, |
| // time-to-empty/shutdown should be calculated. |
| UpdateChargeAndCurrent(0.5, -1.5); |
| EXPECT_EQ(MakeEstimateString(false, 1800, 0), UpdateAndGetEstimateString()); |
| |
| // Now start charging. Since the average current is still negative, we should |
| // avoid computing time-to-full. |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kUsbType, kCharging); |
| UpdateChargeAndCurrent(0.5, 0.5); |
| EXPECT_EQ(MakeEstimateString(false, 0, -1), UpdateAndGetEstimateString()); |
| |
| // Switch to battery power. |
| UpdatePowerSourceAndBatteryStatus(POWER_BATTERY, kAcType, kDischarging); |
| UpdateChargeAndCurrent(0.5, -1.0); |
| EXPECT_EQ(MakeEstimateString(true, 0, 0), UpdateAndGetEstimateString()); |
| SetStabilizedTime(); |
| EXPECT_EQ(MakeEstimateString(false, 1800, 0), UpdateAndGetEstimateString()); |
| |
| // Go back to USB. |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kAcType, kCharging); |
| UpdateChargeAndCurrent(0.5, 1.0); |
| EXPECT_EQ(MakeEstimateString(true, 0, 0), UpdateAndGetEstimateString()); |
| |
| // Since different USB chargers can provide different current, the previous |
| // on-line-power average should be thrown out. |
| SetStabilizedTime(); |
| EXPECT_EQ(MakeEstimateString(false, 0, 1800), UpdateAndGetEstimateString()); |
| } |
| |
| TEST_F(PowerSupplyTest, BatteryTimeEstimatesWithZeroCurrent) { |
| WriteDefaultValues(POWER_AC); |
| UpdateChargeAndCurrent(0.5, 0.1 * kEpsilon); |
| Init(); |
| |
| // When the only available current readings are close to 0 (which would |
| // result in very large time estimates), -1 estimates should be provided |
| // instead. |
| SetStabilizedTime(); |
| EXPECT_EQ(MakeEstimateString(false, 0, -1), UpdateAndGetEstimateString()); |
| |
| UpdatePowerSourceAndBatteryStatus(POWER_BATTERY, kAcType, kDischarging); |
| EXPECT_EQ(MakeEstimateString(true, 0, 0), UpdateAndGetEstimateString()); |
| SetStabilizedTime(); |
| EXPECT_EQ(MakeEstimateString(false, -1, 0), UpdateAndGetEstimateString()); |
| } |
| |
| TEST_F(PowerSupplyTest, FullFactor) { |
| // When the battery has reached the full factor, it should be reported as |
| // fully charged regardless of the current. |
| WriteDefaultValues(POWER_AC); |
| UpdateChargeAndCurrent(kFullFactor, 1.0); |
| Init(); |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_FULL, status.battery_state); |
| EXPECT_DOUBLE_EQ(100.0, status.display_battery_percentage); |
| |
| // It should stay full when the current goes to zero. |
| UpdateChargeAndCurrent(kFullFactor, 0.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_FULL, status.battery_state); |
| EXPECT_DOUBLE_EQ(100.0, status.display_battery_percentage); |
| } |
| |
| TEST_F(PowerSupplyTest, DisplayBatteryPercent) { |
| static const double kShutdownPercent = 5.0; |
| prefs_.SetDouble(kLowBatteryShutdownPercentPref, kShutdownPercent); |
| |
| // Start out with a full battery on AC power. |
| WriteDefaultValues(POWER_AC); |
| UpdateChargeAndCurrent(1.0, 0.0); |
| Init(); |
| |
| // 100% should be reported both on AC and battery power. |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(100.0, status.display_battery_percentage); |
| UpdatePowerSourceAndBatteryStatus(POWER_BATTERY, kAcType, kDischarging); |
| UpdateChargeAndCurrent(1.0, -1.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(100.0, status.display_battery_percentage); |
| |
| // Decrease the battery charge, but keep it above the full-factor-derived |
| // "full" threshold. Batteries sometimes report a lower charge as soon |
| // as line power has been disconnected. |
| const double kFullCharge = kFullFactor; |
| UpdateChargeAndCurrent(kFullCharge, 0.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(100.0, status.display_battery_percentage); |
| |
| // Lower charges should be scaled. |
| const double kLowerCharge = 0.92; |
| UpdateChargeAndCurrent(kLowerCharge, 0.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(100.0 * (100.0 * kLowerCharge - kShutdownPercent) / |
| (100.0 * kFullFactor - kShutdownPercent), |
| status.display_battery_percentage); |
| |
| // Switch to AC and check that the scaling remains the same. |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kAcType, kCharging); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(100.0 * (100.0 * kLowerCharge - kShutdownPercent) / |
| (100.0 * kFullFactor - kShutdownPercent), |
| status.display_battery_percentage); |
| |
| UpdateChargeAndCurrent(0.85, 0.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(100 * (85.0 - kShutdownPercent) / |
| (100.0 * kFullFactor - kShutdownPercent), |
| status.display_battery_percentage); |
| |
| UpdateChargeAndCurrent(kShutdownPercent / 100.0, 0.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(0.0, status.display_battery_percentage); |
| |
| UpdateChargeAndCurrent(0.0, 0.0); |
| EXPECT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(0.0, status.display_battery_percentage); |
| |
| UpdateChargeAndCurrent(-0.1, 0.0); |
| EXPECT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(0.0, status.display_battery_percentage); |
| } |
| |
| TEST_F(PowerSupplyTest, IgnoreReadingsDuringFirmwareUpdate) { |
| // Check that reading broken battery data the first time through yields |
| // failure but still results in the partially-correct status being recorded. |
| // At startup, powerd needs to use what it can get. |
| WriteDefaultValues(POWER_AC); |
| UpdateChargeAndCurrent(0.0, 0.0); |
| WriteDoubleValue(battery_dir_, "voltage_min_design", 0.0); |
| Init(); |
| |
| PowerStatus status; |
| EXPECT_FALSE(UpdateStatus(&status)); |
| EXPECT_TRUE(status.line_power_on); |
| EXPECT_FALSE(status.battery_below_shutdown_threshold); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_AC, status.external_power); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_NOT_PRESENT, |
| status.battery_state); |
| |
| // Report a full battery. |
| UpdateChargeAndCurrent(1.0, 0.0); |
| WriteDoubleValue(battery_dir_, "voltage_min_design", kVoltage); |
| EXPECT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(100.0, status.display_battery_percentage); |
| |
| // Now check that another invalid reading is completely ignored. |
| UpdateChargeAndCurrent(0.0, 0.0); |
| WriteDoubleValue(battery_dir_, "voltage_min_design", 0.0); |
| EXPECT_FALSE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(100.0, status.display_battery_percentage); |
| } |
| |
| TEST_F(PowerSupplyTest, CheckForLowBattery) { |
| const double kShutdownPercent = 5.0; |
| const double kCurrent = -1.0; |
| prefs_.SetDouble(kLowBatteryShutdownPercentPref, kShutdownPercent); |
| |
| WriteDefaultValues(POWER_BATTERY); |
| UpdateChargeAndCurrent((kShutdownPercent + 1.0) / 100.0, kCurrent); |
| Init(); |
| |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_FALSE(status.battery_below_shutdown_threshold); |
| |
| UpdateChargeAndCurrent((kShutdownPercent - 1.0) / 100.0, kCurrent); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_TRUE(status.battery_below_shutdown_threshold); |
| |
| // If the charge is zero, assume that something is being misreported and |
| // avoid shutting down. |
| UpdateChargeAndCurrent(0.0, kCurrent); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_FALSE(status.battery_below_shutdown_threshold); |
| |
| // Don't shut down when on AC power when the battery's charge isn't observed |
| // to be decreasing. |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kAcType, kDischarging); |
| UpdateChargeAndCurrent((kShutdownPercent - 1.0) / 100.0, kCurrent); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_FALSE(status.battery_below_shutdown_threshold); |
| |
| // Don't shut down for other chargers in this situation, either. |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kUsbType, kDischarging); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_FALSE(status.battery_below_shutdown_threshold); |
| |
| // Test that the system shuts down while on AC power if the charge appears to |
| // be falling (i.e. the charger isn't able to deliver enough current). |
| SetStabilizedTime(); |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kAcType, kDischarging); |
| UpdateChargeAndCurrent((kShutdownPercent - 1.0) / 100.0, kCurrent); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_FALSE(status.battery_below_shutdown_threshold); |
| |
| // After just half of the observation period has elapsed, the system should |
| // still be up. |
| const base::TimeDelta kObservationTime = base::TimeDelta::FromMilliseconds( |
| PowerSupply::kObservedBatteryChargeRateMinMs); |
| UpdateChargeAndCurrent((kShutdownPercent - 1.5) / 100.0, kCurrent); |
| test_api_->AdvanceTime(kObservationTime / 2); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_FALSE(status.battery_below_shutdown_threshold); |
| |
| // If the charge is still trending downward after the full observation period |
| // has elapsed, the system should shut down. |
| UpdateChargeAndCurrent((kShutdownPercent - 2.0) / 100.0, kCurrent); |
| test_api_->AdvanceTime(kObservationTime / 2); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_TRUE(status.battery_below_shutdown_threshold); |
| } |
| |
| TEST_F(PowerSupplyTest, LowPowerCharger) { |
| // If a charger is connected but the current is zero and the battery |
| // isn't full, the battery should be reported as discharging. |
| WriteDefaultValues(POWER_AC); |
| UpdateChargeAndCurrent(0.5, 0.0); |
| Init(); |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_AC, |
| status.external_power); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_DISCHARGING, |
| status.battery_state); |
| |
| // If the current is nonzero but the kernel-reported status is |
| // "Discharging", the battery should be reported as discharging. |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kAcType, kDischarging); |
| UpdateChargeAndCurrent(0.5, 1.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_AC, |
| status.external_power); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_DISCHARGING, |
| status.battery_state); |
| } |
| |
| TEST_F(PowerSupplyTest, ConnectedToUsb) { |
| WriteDefaultValues(POWER_AC); |
| UpdateChargeAndCurrent(0.5, 1.0); |
| Init(); |
| |
| // Check that the "connected to USB" status is reported for all |
| // USB-related strings used by the kernel. |
| PowerStatus status; |
| const char* kUsbTypes[] = { "USB", "USB_DCP", "USB_CDP", "USB_ACA" }; |
| for (size_t i = 0; i < arraysize(kUsbTypes); ++i) { |
| const char* kType = kUsbTypes[i]; |
| SCOPED_TRACE(kType); |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kType, kCharging); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_CHARGING, |
| status.battery_state); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_USB, status.external_power); |
| } |
| |
| // The USB type should be reported even when the current is 0. |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kUsbType, kCharging); |
| UpdateChargeAndCurrent(0.5, 0.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(PowerSupplyProperties_BatteryState_DISCHARGING, |
| status.battery_state); |
| EXPECT_EQ(PowerSupplyProperties_ExternalPower_USB, |
| status.external_power); |
| } |
| |
| TEST_F(PowerSupplyTest, ShutdownPercentAffectsBatteryTime) { |
| const double kShutdownPercent = 10.0; |
| prefs_.SetDouble(kLowBatteryShutdownPercentPref, kShutdownPercent); |
| const double kShutdownSec = 3200; |
| prefs_.SetDouble(kLowBatteryShutdownTimePref, kShutdownSec); |
| const double kCurrent = -1.0; |
| |
| WriteDefaultValues(POWER_BATTERY); |
| UpdateChargeAndCurrent(0.5, kCurrent); |
| prefs_.SetDouble(kPowerSupplyFullFactorPref, 1.0); |
| Init(); |
| SetStabilizedTime(); |
| |
| // The reported time until shutdown should be based only on the charge that's |
| // available before shutdown. Note also that the time-based shutdown threshold |
| // is ignored since a percent-based threshold is set. |
| const double kShutdownCharge = kShutdownPercent / 100.0 * 1.0; |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(1800, status.battery_time_to_empty.InSeconds()); |
| EXPECT_EQ(roundl((0.5 - kShutdownCharge) * 3600), |
| status.battery_time_to_shutdown.InSeconds()); |
| EXPECT_FALSE(status.battery_below_shutdown_threshold); |
| |
| // The reported time should be zero once the threshold is reached. |
| UpdateChargeAndCurrent(kShutdownCharge, kCurrent); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(roundl(kShutdownCharge / 1.0 * 3600), |
| status.battery_time_to_empty.InSeconds()); |
| EXPECT_EQ(0, status.battery_time_to_shutdown.InSeconds()); |
| EXPECT_TRUE(status.battery_below_shutdown_threshold); |
| |
| // It should remain zero if the threshold is passed. |
| static const double kLowerCharge = kShutdownCharge / 2.0; |
| UpdateChargeAndCurrent(kLowerCharge, kCurrent); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(roundl(kLowerCharge / 1.0 * 3600), |
| status.battery_time_to_empty.InSeconds()); |
| EXPECT_EQ(0, status.battery_time_to_shutdown.InSeconds()); |
| EXPECT_TRUE(status.battery_below_shutdown_threshold); |
| } |
| |
| TEST_F(PowerSupplyTest, ObservedBatteryChargeRate) { |
| const int kMaxSamples = 5; |
| prefs_.SetInt64(kMaxCurrentSamplesPref, kMaxSamples); |
| prefs_.SetInt64(kMaxChargeSamplesPref, kMaxSamples); |
| |
| WriteDefaultValues(POWER_BATTERY); |
| WriteDoubleValue(battery_dir_, "charge_full", 10.0); |
| UpdateChargeAndCurrent(10.0, -1.0); |
| prefs_.SetDouble(kPowerSupplyFullFactorPref, 1.0); |
| Init(); |
| SetStabilizedTime(); |
| |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(0.0, status.observed_battery_charge_rate); |
| |
| // Advance the time, but not by enough to estimate the rate. |
| const base::TimeDelta kObservationTime = base::TimeDelta::FromMilliseconds( |
| PowerSupply::kObservedBatteryChargeRateMinMs); |
| test_api_->AdvanceTime(kObservationTime / 2); |
| UpdateChargeAndCurrent(9.0, -1.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(0.0, status.observed_battery_charge_rate); |
| |
| // Advance the time by enough so the next reading will be a full hour from the |
| // first one, indicating that the charge is dropping by 1 Ah per hour. |
| test_api_->AdvanceTime(base::TimeDelta::FromHours(1) - kObservationTime / 2); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(-1.0, status.observed_battery_charge_rate); |
| |
| // Decrease the charge by 3 Ah over the next hour. |
| test_api_->AdvanceTime(base::TimeDelta::FromHours(1)); |
| UpdateChargeAndCurrent(6.0, -1.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(-2.0, status.observed_battery_charge_rate); |
| |
| // Switch to AC power and report a different charge. The rate should be |
| // reported as 0 initially. |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kAcType, kCharging); |
| UpdateChargeAndCurrent(7.0, 1.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(0.0, status.observed_battery_charge_rate); |
| |
| // Let enough time pass for the battery readings to stabilize. |
| SetStabilizedTime(); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(0.0, status.observed_battery_charge_rate); |
| |
| // Advance the time just enough for the rate to be calculated and increase the |
| // charge by 1 Ah. |
| test_api_->AdvanceTime(kObservationTime); |
| UpdateChargeAndCurrent(8.0, 1.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(1.0 / (kObservationTime.InSecondsF() / 3600), |
| status.observed_battery_charge_rate); |
| |
| // Now advance the time to get a reading one hour from the first one and |
| // decrease the charge by 2 Ah from the first reading while on AC power. |
| test_api_->AdvanceTime(base::TimeDelta::FromHours(1) - kObservationTime); |
| UpdateChargeAndCurrent(5.0, 1.0); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(-2.0, status.observed_battery_charge_rate); |
| |
| // Send enough identical samples to fill the window and check that the rate is |
| // reported as 0. |
| for (int i = 0; i < kMaxSamples; ++i) { |
| test_api_->AdvanceTime(base::TimeDelta::FromHours(1)); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| } |
| EXPECT_DOUBLE_EQ(0.0, status.observed_battery_charge_rate); |
| } |
| |
| TEST_F(PowerSupplyTest, LowBatteryShutdownSafetyPercent) { |
| // Start out discharging on AC with a ludicrously-high current where all of |
| // the charge will be drained in a minute. |
| const double kCurrent = -60.0; |
| WriteDefaultValues(POWER_AC); |
| UpdatePowerSourceAndBatteryStatus(POWER_AC, kAcType, kDischarging); |
| UpdateChargeAndCurrent(0.5, kCurrent); |
| prefs_.SetInt64(kLowBatteryShutdownTimePref, 180); |
| prefs_.SetDouble(kPowerSupplyFullFactorPref, 1.0); |
| Init(); |
| |
| // The system shouldn't shut down initially since it's on AC power and a |
| // negative charge rate hasn't yet been observed. |
| SetStabilizedTime(); |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(30, status.battery_time_to_empty.InSeconds()); |
| EXPECT_EQ(0, status.battery_time_to_shutdown.InSeconds()); |
| EXPECT_DOUBLE_EQ(0.0, status.observed_battery_charge_rate); |
| EXPECT_FALSE(status.battery_below_shutdown_threshold); |
| |
| // Even after a negative charge rate is observed, the system still shouldn't |
| // shut down, since the battery percent is greater than the safety percent. |
| test_api_->AdvanceTime(base::TimeDelta::FromMilliseconds( |
| PowerSupply::kObservedBatteryChargeRateMinMs)); |
| UpdateChargeAndCurrent(0.25, kCurrent); |
| ASSERT_GT(25.0, PowerSupply::kLowBatteryShutdownSafetyPercent); |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_EQ(15, status.battery_time_to_empty.InSeconds()); |
| EXPECT_EQ(0, status.battery_time_to_shutdown.InSeconds()); |
| EXPECT_LT(status.observed_battery_charge_rate, 0.0); |
| EXPECT_FALSE(status.battery_below_shutdown_threshold); |
| } |
| |
| TEST_F(PowerSupplyTest, NotifyObserver) { |
| // Set a long polling delay to ensure that PowerSupply doesn't poll in the |
| // background during the test. |
| const base::TimeDelta kDelay = base::TimeDelta::FromSeconds(60); |
| prefs_.SetInt64(kBatteryPollIntervalPref, kDelay.InMilliseconds()); |
| prefs_.SetInt64(kBatteryStabilizedAfterStartupMsPref, |
| kDelay.InMilliseconds()); |
| |
| // Check that observers are notified about updates asynchronously. |
| TestObserver observer; |
| power_supply_->AddObserver(&observer); |
| WriteDefaultValues(POWER_AC); |
| Init(); |
| ASSERT_TRUE(power_supply_->RefreshImmediately()); |
| EXPECT_TRUE(observer.WaitForNotification()); |
| power_supply_->RemoveObserver(&observer); |
| } |
| |
| TEST_F(PowerSupplyTest, RegisterForUdevEvents) { |
| Init(); |
| EXPECT_TRUE(udev_.HasSubsystemObserver(PowerSupply::kUdevSubsystem, |
| power_supply_.get())); |
| |
| PowerSupply* dead_ptr = power_supply_.get(); |
| power_supply_.reset(); |
| EXPECT_FALSE(udev_.HasSubsystemObserver(PowerSupply::kUdevSubsystem, |
| dead_ptr)); |
| } |
| |
| TEST_F(PowerSupplyTest, CopyPowerStatusToProtocolBuffer) { |
| // Start out with a status indicating that the system is charging. |
| PowerStatus status; |
| status.line_power_on = true; |
| status.battery_energy_rate = 3.4; |
| status.is_calculating_battery_time = false; |
| status.battery_time_to_full = base::TimeDelta::FromSeconds(900); |
| status.display_battery_percentage = 75.8; |
| status.battery_is_present = true; |
| status.external_power = PowerSupplyProperties_ExternalPower_AC; |
| status.battery_state = PowerSupplyProperties_BatteryState_CHARGING; |
| status.supports_dual_role_devices = false; |
| |
| PowerSupplyProperties proto; |
| CopyPowerStatusToProtocolBuffer(status, &proto); |
| EXPECT_EQ(status.external_power, proto.external_power()); |
| EXPECT_EQ(status.battery_state, proto.battery_state()); |
| EXPECT_DOUBLE_EQ(status.display_battery_percentage, proto.battery_percent()); |
| EXPECT_EQ(0, proto.battery_time_to_empty_sec()); |
| EXPECT_EQ(status.battery_time_to_full.InSeconds(), |
| proto.battery_time_to_full_sec()); |
| EXPECT_FALSE(proto.is_calculating_battery_time()); |
| EXPECT_DOUBLE_EQ(-status.battery_energy_rate, proto.battery_discharge_rate()); |
| EXPECT_FALSE(proto.supports_dual_role_devices()); |
| |
| // Check that power source details are copied. |
| const char kChargerId[] = "PORT1"; |
| const PowerSupplyProperties::PowerSource::Port kChargerPort = |
| PowerSupplyProperties_PowerSource_Port_LEFT; |
| const char kChargerManufacturerId[] = "ab4e"; |
| const char kChargerModelId[] = "0f31"; |
| const double kChargerMaxPower = 60.0; |
| const char kPhoneId[] = "PORT2"; |
| const PowerSupplyProperties::PowerSource::Port kPhonePort = |
| PowerSupplyProperties_PowerSource_Port_RIGHT; |
| const char kPhoneManufacturerId[] = "468b"; |
| const char kPhoneModelId[] = "0429"; |
| const double kPhoneMaxPower = 7.5; |
| status.external_power_source_id = kChargerId; |
| status.available_external_power_sources.push_back( |
| PowerStatus::Source(kChargerId, kChargerPort, kChargerManufacturerId, |
| kChargerModelId, kChargerMaxPower, true)); |
| status.available_external_power_sources.push_back( |
| PowerStatus::Source(kPhoneId, kPhonePort, kPhoneManufacturerId, |
| kPhoneModelId, kPhoneMaxPower, false)); |
| status.supports_dual_role_devices = true; |
| |
| proto.Clear(); |
| CopyPowerStatusToProtocolBuffer(status, &proto); |
| EXPECT_EQ(kChargerId, proto.external_power_source_id()); |
| ASSERT_EQ(2u, proto.available_external_power_source_size()); |
| EXPECT_EQ(kChargerId, proto.available_external_power_source(0).id()); |
| EXPECT_EQ(kChargerPort, proto.available_external_power_source(0).port()); |
| EXPECT_EQ(kChargerManufacturerId, |
| proto.available_external_power_source(0).manufacturer_id()); |
| EXPECT_EQ(kChargerModelId, |
| proto.available_external_power_source(0).model_id()); |
| EXPECT_EQ(kChargerMaxPower, |
| proto.available_external_power_source(0).max_power()); |
| EXPECT_TRUE(proto.available_external_power_source(0).active_by_default()); |
| EXPECT_EQ(kPhoneId, proto.available_external_power_source(1).id()); |
| EXPECT_EQ(kPhonePort, proto.available_external_power_source(1).port()); |
| EXPECT_EQ(kPhoneManufacturerId, |
| proto.available_external_power_source(1).manufacturer_id()); |
| EXPECT_EQ(kPhoneModelId, proto.available_external_power_source(1).model_id()); |
| EXPECT_EQ(kPhoneMaxPower, |
| proto.available_external_power_source(1).max_power()); |
| EXPECT_FALSE(proto.available_external_power_source(1).active_by_default()); |
| EXPECT_TRUE(proto.supports_dual_role_devices()); |
| |
| // Now disconnect everything and start discharging. |
| status.external_power_source_id.clear(); |
| status.available_external_power_sources.clear(); |
| status.line_power_on = false; |
| status.battery_time_to_full = base::TimeDelta(); |
| status.battery_time_to_empty = base::TimeDelta::FromSeconds(1800); |
| status.battery_time_to_shutdown = base::TimeDelta::FromSeconds(1500); |
| status.external_power = PowerSupplyProperties_ExternalPower_DISCONNECTED; |
| status.battery_state = PowerSupplyProperties_BatteryState_DISCHARGING; |
| |
| proto.Clear(); |
| CopyPowerStatusToProtocolBuffer(status, &proto); |
| EXPECT_EQ(status.external_power, proto.external_power()); |
| EXPECT_EQ(status.battery_state, proto.battery_state()); |
| EXPECT_DOUBLE_EQ(status.display_battery_percentage, proto.battery_percent()); |
| EXPECT_EQ(status.battery_time_to_shutdown.InSeconds(), |
| proto.battery_time_to_empty_sec()); |
| EXPECT_EQ(0, proto.battery_time_to_full_sec()); |
| EXPECT_FALSE(proto.is_calculating_battery_time()); |
| EXPECT_DOUBLE_EQ(status.battery_energy_rate, proto.battery_discharge_rate()); |
| |
| // Check that the is-calculating value is copied. |
| status.is_calculating_battery_time = true; |
| proto.Clear(); |
| CopyPowerStatusToProtocolBuffer(status, &proto); |
| EXPECT_TRUE(proto.is_calculating_battery_time()); |
| } |
| |
| TEST_F(PowerSupplyTest, BatteryEnergyValue) { |
| const double kCharge = 1.0; |
| // Set energy_now attribute to charge times voltage + 1 to double check that |
| // it is used instead of a value calculated from voltage and charge. |
| const double kEnergy = kVoltage * kCharge + 1.0; |
| |
| WriteDefaultValues(POWER_BATTERY); |
| UpdateChargeAndCurrent(kCharge, 0.0); |
| WriteDoubleValue(battery_dir_, "energy_now", kEnergy); |
| Init(); |
| |
| // Check that the energy_now attribute is used for battery_energy when |
| // available. |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(kEnergy, status.battery_energy); |
| EXPECT_DOUBLE_EQ(kCharge, status.battery_charge); |
| } |
| |
| TEST_F(PowerSupplyTest, NoNominalVoltage) { |
| const double kCharge = 0.5; |
| const double kCurrent = 1.0; |
| WriteDefaultValues(POWER_BATTERY); |
| UpdateChargeAndCurrent(kCharge, kCurrent); |
| |
| // Remove the default min voltage attribute from the battery |
| base::DeleteFile(battery_dir_.Append("voltage_min_design"), false); |
| Init(); |
| |
| // The battery should use the current voltage if there is no |
| // voltage_min/max_design attribute. |
| PowerStatus status; |
| ASSERT_TRUE(UpdateStatus(&status)); |
| EXPECT_DOUBLE_EQ(kVoltage, status.nominal_voltage); |
| EXPECT_DOUBLE_EQ(kVoltage * kCharge, status.battery_energy); |
| EXPECT_DOUBLE_EQ(kVoltage * kCurrent, status.battery_energy_rate); |
| } |
| |
| } // namespace system |
| } // namespace power_manager |