text: // 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 <memory>
  #include <string>
  
  #include <base/files/file_util.h>
  #include <base/files/scoped_temp_dir.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 {
  
  using Connection = PowerStatus::Port::Connection;
  
  const char kAcType[] = "Mains";
  const char kBatteryType[] = "Battery";
  const char kUsbType[] = "USB";
  const char kUsbPdDrpType[] = "USB_PD_DRP";
  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() : num_updates_(0) {}
    virtual ~TestObserver() {}
  
    int num_updates() const { return num_updates_; }
    void reset_num_updates() { num_updates_ = 0; }
  
    // 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 {
      num_updates_++;
      if (runner_.LoopIsRunning())
        runner_.StopLoop();
    }
  
   private:
    // Number of times that OnPowerStatusUpdate() has been called.
    int num_updates_;
  
    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 == PowerSource::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 == PowerSource::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_t 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);
    }
  
    std::string GetEstimateStringFromStatus(const PowerStatus& status) {
      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()));
    }
  
    // 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 GetEstimateStringFromStatus(status);
    }
  
    // 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;
    }
  
    // Sends a udev event to |power_supply_|.
    void SendUdevEvent() {
      power_supply_->OnUdevEvent(
          PowerSupply::kUdevSubsystem, "AC", UdevAction::CHANGE);
    }
  
    FakePrefs prefs_;
    base::ScopedTempDir temp_dir_;
    base::FilePath ac_dir_;
    base::FilePath battery_dir_;
    UdevStub udev_;
    std::unique_ptr<PowerSupply> power_supply_;
    std::unique_ptr<PowerSupply::TestApi> test_api_;
  };
  
  TEST(PowerSupplyStaticTest, ConnectedSourcesAreEqual) {
    // Equality should be reported when no ports are found.
    PowerStatus a, b;
    EXPECT_TRUE(PowerSupply::ConnectedSourcesAreEqual(a, b));
  
    // A disconnected port should be disregarded.
    constexpr char kId1[] = "ID1";
    a.ports.push_back(PowerStatus::Port());
    a.ports[0].id = kId1;
    EXPECT_TRUE(PowerSupply::ConnectedSourcesAreEqual(a, b));
    EXPECT_TRUE(PowerSupply::ConnectedSourcesAreEqual(b, a));
  
    // After the port is connected, |a| and |b|'s connected sources no longer
    // match.
    a.ports[0].connection = Connection::DEDICATED_SOURCE;
    EXPECT_FALSE(PowerSupply::ConnectedSourcesAreEqual(a, b));
    EXPECT_FALSE(PowerSupply::ConnectedSourcesAreEqual(b, a));
  
    // A disconnected port that's added to |b| should be ignored.
    b.ports.push_back(PowerStatus::Port());
    b.ports[0].id = kId1;
    EXPECT_FALSE(PowerSupply::ConnectedSourcesAreEqual(a, b));
    EXPECT_FALSE(PowerSupply::ConnectedSourcesAreEqual(b, a));
  
    // Once |b|'s port is connected, the statuses should match again.
    b.ports[0].connection = Connection::DEDICATED_SOURCE;
    EXPECT_TRUE(PowerSupply::ConnectedSourcesAreEqual(a, b));
    EXPECT_TRUE(PowerSupply::ConnectedSourcesAreEqual(b, a));
  
    // Insert a new disconnected port at the beginning of |a|'s list and check
    // that the statuses are still reported as being equal.
    constexpr char kId0[] = "ID0";
    a.ports.insert(a.ports.begin(), PowerStatus::Port());
    a.ports[0].id = kId0;
    EXPECT_TRUE(PowerSupply::ConnectedSourcesAreEqual(a, b));
    EXPECT_TRUE(PowerSupply::ConnectedSourcesAreEqual(b, a));
  
    // If the new port is connected, the statuses should be unequal again.
    a.ports[0].connection = Connection::DEDICATED_SOURCE;
    EXPECT_FALSE(PowerSupply::ConnectedSourcesAreEqual(a, b));
    EXPECT_FALSE(PowerSupply::ConnectedSourcesAreEqual(b, a));
  
    // Give |b| a port with a different ID and check that the statuses are still
    // unequal.
    constexpr char kId0B[] = "ID0B";
    b.ports.insert(b.ports.begin(), PowerStatus::Port());
    b.ports[0].id = kId0B;
    b.ports[0].connection = Connection::DEDICATED_SOURCE;
  
    // Now update the ID and check that they're equal again.
    b.ports[0].id = kId0;
    EXPECT_TRUE(PowerSupply::ConnectedSourcesAreEqual(a, b));
    EXPECT_TRUE(PowerSupply::ConnectedSourcesAreEqual(b, a));
  
    // The ports' connection types also need to match.
    a.ports[0].connection = Connection::DUAL_ROLE;
    EXPECT_FALSE(PowerSupply::ConnectedSourcesAreEqual(a, b));
    EXPECT_FALSE(PowerSupply::ConnectedSourcesAreEqual(b, a));
    b.ports[0].connection = Connection::DUAL_ROLE;
    EXPECT_TRUE(PowerSupply::ConnectedSourcesAreEqual(a, b));
    EXPECT_TRUE(PowerSupply::ConnectedSourcesAreEqual(b, a));
  }
  
  // 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(PowerSource::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(PowerSource::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(
        PowerSource::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(PowerSource::AC);
    UpdatePowerSourceAndBatteryStatus(PowerSource::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(PowerSource::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", kUsbType);
    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);
    ASSERT_EQ(2u, status.ports.size());
    EXPECT_EQ(kLine1Id, status.ports[0].id);
    EXPECT_EQ(Connection::NONE, status.ports[0].connection);
    EXPECT_EQ(kLine2Id, status.ports[1].id);
    EXPECT_EQ(Connection::NONE, status.ports[1].connection);
    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", kUsbPdDrpType);
    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(2u, status.ports.size());
    EXPECT_EQ(kLine1Id, status.ports[0].id);
    EXPECT_EQ(Connection::DUAL_ROLE, status.ports[0].connection);
    EXPECT_EQ(kLine1Manufacturer, status.ports[0].manufacturer_id);
    EXPECT_EQ(kLine1ModelName, status.ports[0].model_id);
    EXPECT_EQ(kCurrentMax * kVoltageMax, status.ports[0].max_power);
    EXPECT_FALSE(status.ports[0].active_by_default);
    EXPECT_EQ(kLine2Id, status.ports[1].id);
    EXPECT_EQ(Connection::NONE, status.ports[1].connection);
    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", kUsbType);
    WriteValue(line1_dir, "online", "0");
    WriteValue(line1_dir, "status", kNotCharging);
    WriteValue(line2_dir, "type", kUsbPdDrpType);
    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(2u, status.ports.size());
    EXPECT_EQ(kLine1Id, status.ports[0].id);
    EXPECT_EQ(Connection::NONE, status.ports[0].connection);
    EXPECT_EQ(kLine2Id, status.ports[1].id);
    EXPECT_EQ(Connection::DUAL_ROLE, status.ports[1].connection);
    EXPECT_EQ(kLine2Manufacturer, status.ports[1].manufacturer_id);
    EXPECT_EQ(kLine2ModelName, status.ports[1].model_id);
    EXPECT_EQ(kCurrentMax * kCurrentFactor * kVoltageMax,
              status.ports[1].max_power);
    EXPECT_FALSE(status.ports[1].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 still listed as a connected source but not
    // reported as active.
    WriteValue(line1_dir, "type", kUsbPdDrpType);
    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.ports.size());
    EXPECT_EQ(kLine1Id, status.ports[0].id);
    EXPECT_EQ(Connection::DUAL_ROLE, status.ports[0].connection);
    EXPECT_EQ(kLine1Manufacturer, status.ports[0].manufacturer_id);
    EXPECT_EQ(kLine1ModelName, status.ports[0].model_id);
    EXPECT_EQ(kCurrentMax * kVoltageMax, status.ports[0].max_power);
    EXPECT_FALSE(status.ports[0].active_by_default);
    EXPECT_EQ(kLine2Id, status.ports[1].id);
    EXPECT_EQ(Connection::DUAL_ROLE, status.ports[1].connection);
    EXPECT_EQ(kLine2Manufacturer, status.ports[1].manufacturer_id);
    EXPECT_EQ(kLine2ModelName, status.ports[1].model_id);
    EXPECT_EQ(kCurrentMax * kCurrentFactor * kVoltageMax,
              status.ports[1].max_power);
    EXPECT_FALSE(status.ports[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_PD_DRP", 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.ports.size());
    EXPECT_EQ(kLine1Id, status.ports[0].id);
    EXPECT_EQ(Connection::DUAL_ROLE, status.ports[0].connection);
    EXPECT_EQ(kLine2Id, status.ports[1].id);
    EXPECT_EQ(Connection::DEDICATED_SOURCE, status.ports[1].connection);
    EXPECT_TRUE(status.ports[1].active_by_default);
    EXPECT_EQ(kLine2Id, status.external_power_source_id);
  
    // If the kernel reports a USB charger of any type that is not "USB_PD_DRP"
    // powerd should report it as being active by default.
    const char* const kUsbTypes[] = {
        "USB", "USB_DCP", "USB_CDP", "USB_ACA", "USB_C", "USB_PD",
    };
    for (size_t i = 0; i < arraysize(kUsbTypes); ++i) {
      const char* kType = kUsbTypes[i];
      SCOPED_TRACE(kType);
      WriteValue(line2_dir, "type", kType);
      ASSERT_TRUE(UpdateStatus(&status));
      EXPECT_EQ(PowerSupplyProperties_ExternalPower_USB, status.external_power);
      ASSERT_EQ(2u, status.ports.size());
      EXPECT_EQ(kLine2Id, status.ports[1].id);
      EXPECT_EQ(Connection::DEDICATED_SOURCE, status.ports[1].connection);
      EXPECT_TRUE(status.ports[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.ports.size());
    EXPECT_EQ(Connection::DEDICATED_SOURCE, status.ports[1].connection);
    EXPECT_TRUE(status.ports[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.ports.size());
    EXPECT_EQ(Connection::DEDICATED_SOURCE, status.ports[1].connection);
    EXPECT_TRUE(status.ports[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 ports 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(PowerSource::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 port 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 port's locations are reported correctly.
    Init();
    PowerStatus status;
    ASSERT_TRUE(UpdateStatus(&status));
    ASSERT_EQ(3u, status.ports.size());
    EXPECT_EQ(PowerSupplyProperties_PowerSource_Port_LEFT_FRONT,
              status.ports[0].location);
    EXPECT_EQ(PowerSupplyProperties_PowerSource_Port_RIGHT_BACK,
              status.ports[1].location);
    EXPECT_EQ(PowerSupplyProperties_PowerSource_Port_UNKNOWN,
              status.ports[2].location);
  }
  
  TEST_F(PowerSupplyTest, IgnorePeripherals) {
    // Power supplies corresponding to external peripherals (i.e. with a "scope"
    // of "Device") should be ignored.
    WriteDefaultValues(PowerSource::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(PowerSource::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(PowerSource::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(
        PowerSource::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(PowerSource::AC, kAcType, kCharging);
    SendUdevEvent();
    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(
        PowerSource::BATTERY, kAcType, kDischarging);
    SendUdevEvent();
    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(PowerSource::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(
        PowerSource::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(PowerSource::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(
        PowerSource::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(PowerSource::AC);
    UpdatePowerSourceAndBatteryStatus(PowerSource::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(PowerSource::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(PowerSource::AC, kUsbType, kCharging);
    UpdateChargeAndCurrent(0.5, 0.5);
    EXPECT_EQ(MakeEstimateString(false, 0, -1), UpdateAndGetEstimateString());
  
    // Switch to battery power.
    UpdatePowerSourceAndBatteryStatus(
        PowerSource::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(PowerSource::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(PowerSource::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(
        PowerSource::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(PowerSource::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(PowerSource::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(
        PowerSource::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(PowerSource::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(PowerSource::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(PowerSource::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(PowerSource::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(PowerSource::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(PowerSource::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(PowerSource::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(PowerSource::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(PowerSource::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* const 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(PowerSource::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(PowerSource::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(PowerSource::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(PowerSource::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(PowerSource::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(PowerSource::AC);
    UpdatePowerSourceAndBatteryStatus(PowerSource::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(PowerSource::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, IgnoreSpuriousUdevEvents) {
    TestObserver observer;
    power_supply_->AddObserver(&observer);
    WriteDefaultValues(PowerSource::AC);
    prefs_.SetInt64(kMaxCurrentSamplesPref, 3);
    prefs_.SetInt64(kLowBatteryShutdownTimePref, 0);
    prefs_.SetInt64(kBatteryStabilizedAfterStartupMsPref, 0);
    prefs_.SetInt64(kBatteryStabilizedAfterLinePowerConnectedMsPref, 0);
    prefs_.SetInt64(kBatteryStabilizedAfterLinePowerDisconnectedMsPref, 0);
    prefs_.SetDouble(kPowerSupplyFullFactorPref, 1.0);
    Init();
  
    const double kCharge = 0.5;
    const double kLowCurrent = 1.0;
    const double kHighCurrent = 2.0;
  
    // The amount of time that a battery at kCharge will take to reach full or
    // empty at kLowCurrent.
    const int kLowCurrentSec = 1800;
  
    // The notification from RefreshImmediately() should be asynchronous.
    UpdateChargeAndCurrent(kCharge, kLowCurrent);
    ASSERT_TRUE(power_supply_->RefreshImmediately());
    EXPECT_EQ(0, observer.num_updates());
    EXPECT_EQ(MakeEstimateString(false, 0, kLowCurrentSec),
              GetEstimateStringFromStatus(power_supply_->GetPowerStatus()));
  
    // Switch to battery power and check that a udev event triggers a synchronous
    // notification.
    UpdatePowerSourceAndBatteryStatus(
        PowerSource::BATTERY, kAcType, kDischarging);
    SendUdevEvent();
    EXPECT_EQ(1, observer.num_updates());
    EXPECT_EQ(MakeEstimateString(false, kLowCurrentSec, 0),
              GetEstimateStringFromStatus(power_supply_->GetPowerStatus()));
  
    // A second udev event should be disregarded if nothing has changed. Even
    // though the current has changed, the battery estimates shouldn't be updated.
    UpdateChargeAndCurrent(kCharge, kHighCurrent);
    SendUdevEvent();
    EXPECT_EQ(1, observer.num_updates());
    EXPECT_EQ(MakeEstimateString(false, kLowCurrentSec, 0),
              GetEstimateStringFromStatus(power_supply_->GetPowerStatus()));
  
    // Return to the low current and check that the high current sample wasn't
    // incorporated into the average.
    UpdateChargeAndCurrent(kCharge, kLowCurrent);
    EXPECT_EQ(MakeEstimateString(false, kLowCurrentSec, 0),
              UpdateAndGetEstimateString());
  
    // Switch to AC and check that another event triggers another notification and
    // updated estimates.
    observer.reset_num_updates();
    UpdatePowerSourceAndBatteryStatus(PowerSource::AC, kAcType, kCharging);
    SendUdevEvent();
    EXPECT_EQ(1, observer.num_updates());
    EXPECT_EQ(MakeEstimateString(false, 0, kLowCurrentSec),
              GetEstimateStringFromStatus(power_supply_->GetPowerStatus()));
  
    // Send another spurious event.
    UpdateChargeAndCurrent(kCharge, kHighCurrent);
    SendUdevEvent();
    EXPECT_EQ(1, observer.num_updates());
    EXPECT_EQ(MakeEstimateString(false, 0, kLowCurrentSec),
              GetEstimateStringFromStatus(power_supply_->GetPowerStatus()));
  
    // Check that the high current sample wasn't recorded.
    UpdateChargeAndCurrent(kCharge, kLowCurrent);
    EXPECT_EQ(MakeEstimateString(false, 0, kLowCurrentSec),
              UpdateAndGetEstimateString());
  
    // Add a power supply with an unknown type and check that it doesn't trigger a
    // notification.
    observer.reset_num_updates();
    const base::FilePath dir = temp_dir_.path().Append("foo");
    ASSERT_TRUE(base::CreateDirectory(dir));
    WriteValue(dir, "type", kUnknownType);
    WriteValue(dir, "online", "1");
    WriteValue(dir, "status", kNotCharging);
    UpdateChargeAndCurrent(kCharge, kHighCurrent);
    SendUdevEvent();
    EXPECT_EQ(0, observer.num_updates());
    EXPECT_EQ(MakeEstimateString(false, 0, kLowCurrentSec),
              GetEstimateStringFromStatus(power_supply_->GetPowerStatus()));
  
    // Switch the power supply's type so it's recognized and check that a
    // notification is sent.
    WriteValue(dir, "type", kUsbType);
    UpdateChargeAndCurrent(kCharge, kLowCurrent);
    SendUdevEvent();
    EXPECT_EQ(1, observer.num_updates());
    EXPECT_EQ(MakeEstimateString(false, 0, kLowCurrentSec),
              GetEstimateStringFromStatus(power_supply_->GetPowerStatus()));
  
    // An updated max current and voltage shouldn't generate a notification.
    WriteDoubleValue(dir, "current_max", 3.0);
    WriteDoubleValue(dir, "voltage_max_design", 20.0);
    SendUdevEvent();
    EXPECT_EQ(1, observer.num_updates());
    EXPECT_EQ(MakeEstimateString(false, 0, kLowCurrentSec),
              GetEstimateStringFromStatus(power_supply_->GetPowerStatus()));
  
    power_supply_->RemoveObserver(&observer);
  }
  
  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, but that ports that don't have
    // anything connected are ignored.
    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;
    status.ports.emplace_back(kChargerId,
                              kChargerPort,
                              Connection::DEDICATED_SOURCE,
                              kChargerManufacturerId,
                              kChargerModelId,
                              kChargerMaxPower,
                              true /* active_by_default */);
    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.ports.emplace_back(kPhoneId,
                              kPhonePort,
                              Connection::DUAL_ROLE,
                              kPhoneManufacturerId,
                              kPhoneModelId,
                              kPhoneMaxPower,
                              false /* active_by_default */);
    status.ports.emplace_back("PORT3",
                              PowerSupplyProperties_PowerSource_Port_FRONT,
                              Connection::NONE,
                              "",
                              "",
                              0.0,
                              false /* active_by_default */);
    status.external_power_source_id = kChargerId;
    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.ports.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());
    EXPECT_EQ(0, proto.available_external_power_source_size());
  
    // 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(PowerSource::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(PowerSource::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);
  
    // If the current voltage is also zero, report failure rather than returning
    // bad data: http://crbug.com/671374
    WriteDoubleValue(battery_dir_, "voltage_now", 0.0);
    EXPECT_FALSE(UpdateStatus(&status));
  }
  
  }  // namespace system
  }  // namespace power_manager
  