Commit 472a020d authored by markuso's avatar markuso Committed by Commit bot

Implement device::BatteryStatus support for UPower daemon 0.99.x

The org.freedesktop.UPower API was changed when upgrading the UPower daemon
from version 0.9.23 to 0.99.x. The BatteryStatusManagerLinux used the
"Changed" signal which was replaced by the "PropertyChanged" signal.

Change the BatteryStatusManagerLinux to use the new UPower API
(and keep compatibility with the 0.9.23 version, which is still used in
Ubuntu 14.04 LTS).

1. use dbus::PropertySet: the dbus::PropertySet provides simple access to
the properties and connection to the property-changed notifications.

2. Use UPower method GetDisplayDevice:
The 'DisplayDevice' is a composite battery device. That was added in UPower
version 0.99.0. If we don't get that device or if it is no battery, then we
continue to enumerate all devices.

3. Listen to 'DeviceAdded' and 'DeviceRemoved' signals:
Re-enumerate battery devices if a device is added/removed.

4. Compatibility with UPower version < 0.99
Only old UPower versions need to connect to the 'Changed' signal.

5. Rewrite the existing unittests to use a BatteryStatusManagerLinux instance
with a dbus::MockBus and mock the dbus-methods/properties for the test.
Add more unittests:
- for changing device properties
- for the DisplayDevice
- for enumerating devices
- for the DeviceAdded and DeviceRemoved signals

Review-Url: https://codereview.chromium.org/2066503002
Cr-Commit-Position: refs/heads/master@{#413745}
parent b161f605
......@@ -14,7 +14,6 @@ if (is_mac) {
}
is_linux_without_udev = is_linux && !use_udev
is_linux_without_dbus = is_linux && !use_dbus
test("device_unittests") {
sources = [
......@@ -86,8 +85,9 @@ test("device_unittests") {
"//url",
]
if (!is_linux_without_dbus && !is_chromeos) {
if (is_linux && !is_chromeos && use_dbus) {
sources += [ "battery/battery_status_manager_linux_unittest.cc" ]
deps += [ "//dbus:test_support" ]
}
# HID and Serial:
......
......@@ -16,6 +16,7 @@ if (!is_android) {
"battery_monitor_impl.h",
"battery_status_manager_chromeos.cc",
"battery_status_manager_default.cc",
"battery_status_manager_linux-inl.h",
"battery_status_manager_linux.cc",
"battery_status_manager_linux.h",
"battery_status_manager_mac.cc",
......
......@@ -120,6 +120,7 @@
'battery_monitor_impl.h',
'battery_status_manager_chromeos.cc',
'battery_status_manager_default.cc',
'battery_status_manager_linux-inl.h',
'battery_status_manager_linux.cc',
'battery_status_manager_linux.h',
'battery_status_manager_mac.cc',
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_BATTERY_BATTERY_STATUS_MANAGER_LINUX_INL_H_
#define DEVICE_BATTERY_BATTERY_STATUS_MANAGER_LINUX_INL_H_
namespace device {
const char kUPowerInterfaceName[] = "org.freedesktop.UPower";
const char kUPowerServiceName[] = "org.freedesktop.UPower";
const char kUPowerMethodEnumerateDevices[] = "EnumerateDevices";
const char kUPowerMethodGetDisplayDevice[] = "GetDisplayDevice";
const char kUPowerPath[] = "/org/freedesktop/UPower";
const char kUPowerPropertyDaemonVersion[] = "DaemonVersion";
const char kUPowerSignalDeviceAdded[] = "DeviceAdded";
const char kUPowerSignalDeviceRemoved[] = "DeviceRemoved";
const char kUPowerDeviceInterfaceName[] = "org.freedesktop.UPower.Device";
const char kUPowerDevicePropertyIsPresent[] = "IsPresent";
const char kUPowerDevicePropertyPercentage[] = "Percentage";
const char kUPowerDevicePropertyState[] = "State";
const char kUPowerDevicePropertyTimeToEmpty[] = "TimeToEmpty";
const char kUPowerDevicePropertyTimeToFull[] = "TimeToFull";
const char kUPowerDevicePropertyType[] = "Type";
const char kUPowerDeviceSignalChanged[] = "Changed";
} // namespace device
#endif // DEVICE_BATTERY_BATTERY_STATUS_MANAGER_LINUX_INL_H_
......@@ -7,96 +7,122 @@
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/macros.h"
#include "base/metrics/histogram.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "base/version.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
#include "dbus/property.h"
#include "dbus/values_util.h"
#include "device/battery/battery_status_manager.h"
#include "device/battery/battery_status_manager_linux-inl.h"
namespace device {
namespace {
const char kUPowerServiceName[] = "org.freedesktop.UPower";
const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device";
const char kUPowerPath[] = "/org/freedesktop/UPower";
const char kUPowerDeviceSignalChanged[] = "Changed";
const char kUPowerEnumerateDevices[] = "EnumerateDevices";
const char kBatteryNotifierThreadName[] = "BatteryStatusNotifier";
// UPowerDeviceType reflects the possible UPower.Device.Type values,
// see upower.freedesktop.org/docs/Device.html#Device:Type.
enum UPowerDeviceType {
UPOWER_DEVICE_TYPE_UNKNOWN = 0,
UPOWER_DEVICE_TYPE_LINE_POWER = 1,
UPOWER_DEVICE_TYPE_BATTERY = 2,
UPOWER_DEVICE_TYPE_UPS = 3,
UPOWER_DEVICE_TYPE_MONITOR = 4,
UPOWER_DEVICE_TYPE_MOUSE = 5,
UPOWER_DEVICE_TYPE_KEYBOARD = 6,
UPOWER_DEVICE_TYPE_PDA = 7,
UPOWER_DEVICE_TYPE_PHONE = 8,
};
class UPowerProperties : public dbus::PropertySet {
public:
UPowerProperties(dbus::ObjectProxy* object_proxy,
const PropertyChangedCallback callback);
~UPowerProperties() override;
base::Version daemon_version();
private:
dbus::Property<std::string> daemon_version_;
typedef std::vector<dbus::ObjectPath> PathsVector;
DISALLOW_COPY_AND_ASSIGN(UPowerProperties);
};
double GetPropertyAsDouble(const base::DictionaryValue& dictionary,
const std::string& property_name,
double default_value) {
double value = default_value;
return dictionary.GetDouble(property_name, &value) ? value : default_value;
UPowerProperties::UPowerProperties(dbus::ObjectProxy* object_proxy,
const PropertyChangedCallback callback)
: dbus::PropertySet(object_proxy, kUPowerInterfaceName, callback) {
RegisterProperty(kUPowerPropertyDaemonVersion, &daemon_version_);
}
bool GetPropertyAsBoolean(const base::DictionaryValue& dictionary,
const std::string& property_name,
bool default_value) {
bool value = default_value;
return dictionary.GetBoolean(property_name, &value) ? value : default_value;
UPowerProperties::~UPowerProperties() {}
base::Version UPowerProperties::daemon_version() {
return (daemon_version_.is_valid() || daemon_version_.GetAndBlock())
? base::Version(daemon_version_.value())
: base::Version();
}
std::unique_ptr<base::DictionaryValue> GetPropertiesAsDictionary(
dbus::ObjectProxy* proxy) {
dbus::MethodCall method_call(dbus::kPropertiesInterface,
dbus::kPropertiesGetAll);
dbus::MessageWriter builder(&method_call);
builder.AppendString(kUPowerDeviceName);
class UPowerObject {
public:
typedef dbus::PropertySet::PropertyChangedCallback PropertyChangedCallback;
UPowerObject(dbus::Bus* dbus,
const PropertyChangedCallback property_changed_callback);
~UPowerObject();
std::vector<dbus::ObjectPath> EnumerateDevices();
dbus::ObjectPath GetDisplayDevice();
dbus::ObjectProxy* proxy() { return proxy_; }
UPowerProperties* properties() { return properties_.get(); }
private:
dbus::Bus* dbus_; // Owned by the BatteryStatusNotificationThread.
dbus::ObjectProxy* proxy_; // Owned by the dbus.
std::unique_ptr<UPowerProperties> properties_;
DISALLOW_COPY_AND_ASSIGN(UPowerObject);
};
std::unique_ptr<dbus::Response> response(proxy->CallMethodAndBlock(
UPowerObject::UPowerObject(
dbus::Bus* dbus,
const PropertyChangedCallback property_changed_callback)
: dbus_(dbus),
proxy_(dbus_->GetObjectProxy(kUPowerServiceName,
dbus::ObjectPath(kUPowerPath))),
properties_(new UPowerProperties(proxy_, property_changed_callback)) {}
UPowerObject::~UPowerObject() {
properties_.reset(); // before the proxy is deleted.
dbus_->RemoveObjectProxy(kUPowerServiceName, proxy_->object_path(),
base::Bind(&base::DoNothing));
}
std::vector<dbus::ObjectPath> UPowerObject::EnumerateDevices() {
std::vector<dbus::ObjectPath> paths;
dbus::MethodCall method_call(kUPowerServiceName,
kUPowerMethodEnumerateDevices);
std::unique_ptr<dbus::Response> response(proxy_->CallMethodAndBlock(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
if (response) {
dbus::MessageReader reader(response.get());
std::unique_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
base::DictionaryValue* dictionary_value = NULL;
if (value && value->GetAsDictionary(&dictionary_value)) {
ignore_result(value.release());
return std::unique_ptr<base::DictionaryValue>(dictionary_value);
reader.PopArrayOfObjectPaths(&paths);
}
}
return std::unique_ptr<base::DictionaryValue>();
return paths;
}
std::unique_ptr<PathsVector> GetPowerSourcesPaths(dbus::ObjectProxy* proxy) {
std::unique_ptr<PathsVector> paths(new PathsVector());
if (!proxy)
return paths;
dbus::ObjectPath UPowerObject::GetDisplayDevice() {
dbus::ObjectPath display_device_path;
if (!proxy_)
return display_device_path;
dbus::MethodCall method_call(kUPowerServiceName, kUPowerEnumerateDevices);
std::unique_ptr<dbus::Response> response(proxy->CallMethodAndBlock(
dbus::MethodCall method_call(kUPowerServiceName,
kUPowerMethodGetDisplayDevice);
std::unique_ptr<dbus::Response> response(proxy_->CallMethodAndBlock(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
if (response) {
dbus::MessageReader reader(response.get());
reader.PopArrayOfObjectPaths(paths.get());
reader.PopObjectPath(&display_device_path);
}
return paths;
return display_device_path;
}
void UpdateNumberBatteriesHistogram(int count) {
......@@ -104,15 +130,187 @@ void UpdateNumberBatteriesHistogram(int count) {
"BatteryStatus.NumberBatteriesLinux", count, 1, 5, 6);
}
class BatteryProperties : public dbus::PropertySet {
public:
BatteryProperties(dbus::ObjectProxy* object_proxy,
const PropertyChangedCallback callback);
~BatteryProperties() override;
void ConnectSignals() override;
void Invalidate();
bool is_present(bool default_value = false);
double percentage(double default_value = 100);
uint32_t state(uint32_t default_value = UPOWER_DEVICE_STATE_UNKNOWN);
int64_t time_to_empty(int64_t default_value = 0);
int64_t time_to_full(int64_t default_value = 0);
uint32_t type(uint32_t default_value = UPOWER_DEVICE_TYPE_UNKNOWN);
private:
bool connected_ = false;
dbus::Property<bool> is_present_;
dbus::Property<double> percentage_;
dbus::Property<uint32_t> state_;
dbus::Property<int64_t> time_to_empty_;
dbus::Property<int64_t> time_to_full_;
dbus::Property<uint32_t> type_;
DISALLOW_COPY_AND_ASSIGN(BatteryProperties);
};
BatteryProperties::BatteryProperties(dbus::ObjectProxy* object_proxy,
const PropertyChangedCallback callback)
: dbus::PropertySet(object_proxy, kUPowerDeviceInterfaceName, callback) {
RegisterProperty(kUPowerDevicePropertyIsPresent, &is_present_);
RegisterProperty(kUPowerDevicePropertyPercentage, &percentage_);
RegisterProperty(kUPowerDevicePropertyState, &state_);
RegisterProperty(kUPowerDevicePropertyTimeToEmpty, &time_to_empty_);
RegisterProperty(kUPowerDevicePropertyTimeToFull, &time_to_full_);
RegisterProperty(kUPowerDevicePropertyType, &type_);
}
BatteryProperties::~BatteryProperties() {}
void BatteryProperties::ConnectSignals() {
if (!connected_) {
connected_ = true;
dbus::PropertySet::ConnectSignals();
}
}
void BatteryProperties::Invalidate() {
is_present_.set_valid(false);
percentage_.set_valid(false);
state_.set_valid(false);
time_to_empty_.set_valid(false);
time_to_full_.set_valid(false);
type_.set_valid(false);
}
bool BatteryProperties::is_present(bool default_value) {
return (is_present_.is_valid() || is_present_.GetAndBlock())
? is_present_.value()
: default_value;
}
double BatteryProperties::percentage(double default_value) {
return (percentage_.is_valid() || percentage_.GetAndBlock())
? percentage_.value()
: default_value;
}
uint32_t BatteryProperties::state(uint32_t default_value) {
return (state_.is_valid() || state_.GetAndBlock()) ? state_.value()
: default_value;
}
int64_t BatteryProperties::time_to_empty(int64_t default_value) {
return (time_to_empty_.is_valid() || time_to_empty_.GetAndBlock())
? time_to_empty_.value()
: default_value;
}
int64_t BatteryProperties::time_to_full(int64_t default_value) {
return (time_to_full_.is_valid() || time_to_full_.GetAndBlock())
? time_to_full_.value()
: default_value;
}
uint32_t BatteryProperties::type(uint32_t default_value) {
return (type_.is_valid() || type_.GetAndBlock()) ? type_.value()
: default_value;
}
class BatteryObject {
public:
typedef dbus::PropertySet::PropertyChangedCallback PropertyChangedCallback;
BatteryObject(dbus::Bus* dbus,
const dbus::ObjectPath& device_path,
const PropertyChangedCallback& property_changed_callback);
~BatteryObject();
bool IsValid();
dbus::ObjectProxy* proxy() { return proxy_; }
BatteryProperties* properties() { return properties_.get(); }
private:
dbus::Bus* dbus_; // Owned by the BatteryStatusNotificationThread,
dbus::ObjectProxy* proxy_; // Owned by the dbus.
std::unique_ptr<BatteryProperties> properties_;
DISALLOW_COPY_AND_ASSIGN(BatteryObject);
};
BatteryObject::BatteryObject(
dbus::Bus* dbus,
const dbus::ObjectPath& device_path,
const PropertyChangedCallback& property_changed_callback)
: dbus_(dbus),
proxy_(dbus_->GetObjectProxy(kUPowerServiceName, device_path)),
properties_(new BatteryProperties(proxy_, property_changed_callback)) {}
BatteryObject::~BatteryObject() {
properties_.reset(); // before the proxy is deleted.
dbus_->RemoveObjectProxy(kUPowerServiceName, proxy_->object_path(),
base::Bind(&base::DoNothing));
}
bool BatteryObject::IsValid() {
return properties_->is_present() &&
properties_->type() == UPOWER_DEVICE_TYPE_BATTERY;
}
BatteryStatus ComputeWebBatteryStatus(BatteryProperties* properties) {
BatteryStatus status;
uint32_t state = properties->state();
status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING &&
state != UPOWER_DEVICE_STATE_EMPTY;
// Convert percentage to a value between 0 and 1 with 2 digits of precision.
// This is to bring it in line with other platforms like Mac and Android where
// we report level with 1% granularity. It also serves the purpose of reducing
// the possibility of fingerprinting and triggers less level change events on
// the blink side.
// TODO(timvolodine): consider moving this rounding to the blink side.
status.level = round(properties->percentage()) / 100.f;
switch (state) {
case UPOWER_DEVICE_STATE_CHARGING: {
int64_t time_to_full = properties->time_to_full();
status.charging_time = (time_to_full > 0)
? time_to_full
: std::numeric_limits<double>::infinity();
break;
}
case UPOWER_DEVICE_STATE_DISCHARGING: {
int64_t time_to_empty = properties->time_to_empty();
// Set dischargingTime if it's available. Otherwise leave the default
// value which is +infinity.
if (time_to_empty > 0)
status.discharging_time = time_to_empty;
status.charging_time = std::numeric_limits<double>::infinity();
break;
}
case UPOWER_DEVICE_STATE_FULL: {
break;
}
default: { status.charging_time = std::numeric_limits<double>::infinity(); }
}
return status;
}
} // namespace
// Class that represents a dedicated thread which communicates with DBus to
// obtain battery information and receives battery change notifications.
class BatteryStatusNotificationThread : public base::Thread {
class BatteryStatusManagerLinux::BatteryStatusNotificationThread
: public base::Thread {
public:
BatteryStatusNotificationThread(
const BatteryStatusService::BatteryUpdateCallback& callback)
: base::Thread(kBatteryNotifierThreadName),
callback_(callback),
battery_proxy_(NULL) {}
: base::Thread(kBatteryNotifierThreadName), callback_(callback) {}
~BatteryStatusNotificationThread() override {
// Make sure to shutdown the dbus connection if it is still open in the very
......@@ -129,83 +327,130 @@ class BatteryStatusNotificationThread : public base::Thread {
void StartListening() {
DCHECK(OnWatcherThread());
if (system_bus_.get())
if (upower_)
return;
if (!system_bus_)
InitDBus();
dbus::ObjectProxy* power_proxy =
system_bus_->GetObjectProxy(kUPowerServiceName,
dbus::ObjectPath(kUPowerPath));
std::unique_ptr<PathsVector> device_paths =
GetPowerSourcesPaths(power_proxy);
int num_batteries = 0;
for (size_t i = 0; i < device_paths->size(); ++i) {
const dbus::ObjectPath& device_path = device_paths->at(i);
dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy(
kUPowerServiceName, device_path);
std::unique_ptr<base::DictionaryValue> dictionary =
GetPropertiesAsDictionary(device_proxy);
upower_.reset(new UPowerObject(system_bus_.get(),
UPowerObject::PropertyChangedCallback()));
upower_->proxy()->ConnectToSignal(
kUPowerServiceName, kUPowerSignalDeviceAdded,
base::Bind(&BatteryStatusNotificationThread::DeviceAdded,
base::Unretained(this)),
base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
base::Unretained(this)));
upower_->proxy()->ConnectToSignal(
kUPowerServiceName, kUPowerSignalDeviceRemoved,
base::Bind(&BatteryStatusNotificationThread::DeviceRemoved,
base::Unretained(this)),
base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
base::Unretained(this)));
if (!dictionary)
continue;
FindBatteryDevice();
}
bool is_present = GetPropertyAsBoolean(*dictionary, "IsPresent", false);
uint32_t type = static_cast<uint32_t>(
GetPropertyAsDouble(*dictionary, "Type", UPOWER_DEVICE_TYPE_UNKNOWN));
void StopListening() {
DCHECK(OnWatcherThread());
ShutdownDBusConnection();
}
if (!is_present || type != UPOWER_DEVICE_TYPE_BATTERY) {
system_bus_->RemoveObjectProxy(kUPowerServiceName,
device_path,
base::Bind(&base::DoNothing));
continue;
void SetDBusForTesting(dbus::Bus* bus) { system_bus_ = bus; }
private:
bool OnWatcherThread() {
return task_runner()->BelongsToCurrentThread();
}
void InitDBus() {
DCHECK(OnWatcherThread());
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SYSTEM;
options.connection_type = dbus::Bus::PRIVATE;
system_bus_ = new dbus::Bus(options);
}
bool IsDaemonVersionBelow_0_99() {
base::Version daemon_version = upower_->properties()->daemon_version();
return daemon_version.IsValid() &&
daemon_version.CompareTo(base::Version("0.99")) < 0;
}
void FindBatteryDevice() {
// Move the currently watched battery_ device to a stack-local variable such
// that we can enumerate all devices (once more):
// first testing the display device, then testing all devices from
// EnumerateDevices. We will monitor the first battery device we find.
// - That may be the same device we did monitor on entering this method;
// then we'll use the same BatteryObject instance, that was moved to
// current - see UseCurrentOrCreateBattery().
// - Or it may be a new device; then the previously monitored BatteryObject
// instance (if any) is released on leaving this function.
// - Or we may not find a battery device; then on leaving this function
// battery_ will be nullptr and the previously monitored BatteryObject
// instance (if any) is no longer a battery and will be released.
std::unique_ptr<BatteryObject> current = std::move(battery_);
auto UseCurrentOrCreateBattery =
[&current, this](const dbus::ObjectPath& device_path) {
if (current && current->proxy()->object_path() == device_path)
return std::move(current);
else
return CreateBattery(device_path);
};
dbus::ObjectPath display_device_path = upower_->GetDisplayDevice();
if (display_device_path.IsValid()) {
std::unique_ptr<BatteryObject> battery =
UseCurrentOrCreateBattery(display_device_path);
if (battery->IsValid())
battery_ = std::move(battery);
}
if (battery_proxy_) {
if (!battery_) {
int num_batteries = 0;
for (const auto& device_path : upower_->EnumerateDevices()) {
std::unique_ptr<BatteryObject> battery =
UseCurrentOrCreateBattery(device_path);
if (!battery->IsValid())
continue;
if (battery_) {
// TODO(timvolodine): add support for multiple batteries. Currently we
// only collect information from the first battery we encounter
// (crbug.com/400780).
LOG(WARNING) << "multiple batteries found, "
<< "using status data of the first battery only.";
} else {
battery_proxy_ = device_proxy;
battery_ = std::move(battery);
}
num_batteries++;
}
UpdateNumberBatteriesHistogram(num_batteries);
}
if (!battery_proxy_) {
if (battery_) {
battery_->properties()->ConnectSignals();
NotifyBatteryStatus();
} else {
callback_.Run(BatteryStatus());
return;
}
battery_proxy_->ConnectToSignal(
kUPowerDeviceName,
kUPowerDeviceSignalChanged,
if (IsDaemonVersionBelow_0_99()) {
// UPower Version 0.99 replaced the Changed signal with the
// PropertyChanged signal. For older versions we need to listen
// to the Changed signal.
battery_->proxy()->ConnectToSignal(
kUPowerDeviceInterfaceName, kUPowerDeviceSignalChanged,
base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
base::Unretained(this)),
base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
base::Unretained(this)));
}
void StopListening() {
DCHECK(OnWatcherThread());
ShutdownDBusConnection();
}
private:
bool OnWatcherThread() {
return task_runner()->BelongsToCurrentThread();
}
void InitDBus() {
DCHECK(OnWatcherThread());
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SYSTEM;
options.connection_type = dbus::Bus::PRIVATE;
system_bus_ = new dbus::Bus(options);
}
void ShutdownDBusConnection() {
......@@ -214,93 +459,132 @@ class BatteryStatusNotificationThread : public base::Thread {
if (!system_bus_.get())
return;
battery_.reset(); // before the system_bus_ is shut down.
upower_.reset();
// Shutdown DBus connection later because there may be pending tasks on
// this thread.
message_loop()->PostTask(FROM_HERE,
base::Bind(&dbus::Bus::ShutdownAndBlock,
system_bus_));
system_bus_ = NULL;
battery_proxy_ = NULL;
}
void OnSignalConnected(const std::string& interface_name,
const std::string& signal_name,
bool success) {
DCHECK(OnWatcherThread());
bool success) {}
std::unique_ptr<BatteryObject> CreateBattery(
const dbus::ObjectPath& device_path) {
std::unique_ptr<BatteryObject> battery(new BatteryObject(
system_bus_.get(), device_path,
base::Bind(&BatteryStatusNotificationThread::BatteryPropertyChanged,
base::Unretained(this))));
return battery;
}
if (interface_name != kUPowerDeviceName ||
signal_name != kUPowerDeviceSignalChanged) {
return;
void DeviceAdded(dbus::Signal* signal /* unused */) {
// Re-iterate all devices to see if we need to monitor the added battery
// instead of the currently monitored battery.
FindBatteryDevice();
}
if (!system_bus_.get())
void DeviceRemoved(dbus::Signal* signal) {
if (!battery_)
return;
if (success) {
BatteryChanged(NULL);
} else {
// Failed to register for "Changed" signal, execute callback with the
// default values.
callback_.Run(BatteryStatus());
// UPower specifies that the DeviceRemoved signal has an object-path as
// argument, however IRL that signal was observed with a string argument,
// so cover both cases (argument as string, as object-path and neither of
// these) and call FindBatteryDevice() if either we couldn't get the
// argument or the removed device-path is the battery_.
dbus::MessageReader reader(signal);
dbus::ObjectPath removed_device_path;
switch (reader.GetDataType()) {
case dbus::Message::DataType::STRING: {
std::string removed_device_path_string;
if (reader.PopString(&removed_device_path_string))
removed_device_path = dbus::ObjectPath(removed_device_path_string);
break;
}
case dbus::Message::DataType::OBJECT_PATH:
reader.PopObjectPath(&removed_device_path);
break;
default:
break;
}
if (!removed_device_path.IsValid() ||
battery_->proxy()->object_path() == removed_device_path)
FindBatteryDevice();
}
void BatteryPropertyChanged(const std::string& property_name) {
NotifyBatteryStatus();
}
void BatteryChanged(dbus::Signal* signal /* unsused */) {
DCHECK(battery_);
battery_->properties()->Invalidate();
NotifyBatteryStatus();
}
void NotifyBatteryStatus() {
DCHECK(OnWatcherThread());
if (!system_bus_.get())
if (!system_bus_.get() || !battery_ || notifying_battery_status_)
return;
std::unique_ptr<base::DictionaryValue> dictionary =
GetPropertiesAsDictionary(battery_proxy_);
if (dictionary)
callback_.Run(ComputeWebBatteryStatus(*dictionary));
else
callback_.Run(BatteryStatus());
// If the system uses a UPower daemon older than version 0.99
// (see IsDaemonVersionBelow_0_99), then we are notified about changed
// battery_ properties through the 'Changed' signal of the battery_
// device (see BatteryChanged()). That is implemented to invalidate all
// battery_ properties (so they are re-fetched from the dbus). Getting
// the new property-value triggers a callback to BatteryPropertyChanged().
// notifying_battery_status_ is set to avoid recursion and computing the
// status too often.
notifying_battery_status_ = true;
callback_.Run(ComputeWebBatteryStatus(battery_->properties()));
notifying_battery_status_ = false;
}
BatteryStatusService::BatteryUpdateCallback callback_;
scoped_refptr<dbus::Bus> system_bus_;
dbus::ObjectProxy* battery_proxy_; // owned by the bus
std::unique_ptr<UPowerObject> upower_;
std::unique_ptr<BatteryObject> battery_;
bool notifying_battery_status_ = false;
DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread);
};
// Creates a notification thread and delegates Start/Stop calls to it.
class BatteryStatusManagerLinux : public BatteryStatusManager {
public:
explicit BatteryStatusManagerLinux(
BatteryStatusManagerLinux::BatteryStatusManagerLinux(
const BatteryStatusService::BatteryUpdateCallback& callback)
: callback_(callback) {}
~BatteryStatusManagerLinux() override {}
BatteryStatusManagerLinux::~BatteryStatusManagerLinux() {}
private:
// BatteryStatusManager:
bool StartListeningBatteryChange() override {
bool BatteryStatusManagerLinux::StartListeningBatteryChange() {
if (!StartNotifierThreadIfNecessary())
return false;
notifier_thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&BatteryStatusNotificationThread::StartListening,
FROM_HERE, base::Bind(&BatteryStatusNotificationThread::StartListening,
base::Unretained(notifier_thread_.get())));
return true;
}
}
void StopListeningBatteryChange() override {
void BatteryStatusManagerLinux::StopListeningBatteryChange() {
if (!notifier_thread_)
return;
notifier_thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&BatteryStatusNotificationThread::StopListening,
FROM_HERE, base::Bind(&BatteryStatusNotificationThread::StopListening,
base::Unretained(notifier_thread_.get())));
}
}
// Starts the notifier thread if not already started and returns true on
// success.
bool StartNotifierThreadIfNecessary() {
bool BatteryStatusManagerLinux::StartNotifierThreadIfNecessary() {
if (notifier_thread_)
return true;
......@@ -313,59 +597,24 @@ class BatteryStatusManagerLinux : public BatteryStatusManager {
return false;
}
return true;
}
BatteryStatusService::BatteryUpdateCallback callback_;
std::unique_ptr<BatteryStatusNotificationThread> notifier_thread_;
DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux);
};
} // namespace
BatteryStatus ComputeWebBatteryStatus(const base::DictionaryValue& dictionary) {
BatteryStatus status;
if (!dictionary.HasKey("State"))
return status;
}
uint32_t state = static_cast<uint32_t>(
GetPropertyAsDouble(dictionary, "State", UPOWER_DEVICE_STATE_UNKNOWN));
status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING &&
state != UPOWER_DEVICE_STATE_EMPTY;
double percentage = GetPropertyAsDouble(dictionary, "Percentage", 100);
// Convert percentage to a value between 0 and 1 with 2 digits of precision.
// This is to bring it in line with other platforms like Mac and Android where
// we report level with 1% granularity. It also serves the purpose of reducing
// the possibility of fingerprinting and triggers less level change events on
// the blink side.
// TODO(timvolodine): consider moving this rounding to the blink side.
status.level = round(percentage) / 100.f;
base::Thread* BatteryStatusManagerLinux::GetNotifierThreadForTesting() {
return notifier_thread_.get();
}
switch (state) {
case UPOWER_DEVICE_STATE_CHARGING : {
double time_to_full = GetPropertyAsDouble(dictionary, "TimeToFull", 0);
status.charging_time =
(time_to_full > 0) ? time_to_full
: std::numeric_limits<double>::infinity();
break;
}
case UPOWER_DEVICE_STATE_DISCHARGING : {
double time_to_empty = GetPropertyAsDouble(dictionary, "TimeToEmpty", 0);
// Set dischargingTime if it's available. Otherwise leave the default
// value which is +infinity.
if (time_to_empty > 0)
status.discharging_time = time_to_empty;
status.charging_time = std::numeric_limits<double>::infinity();
break;
}
case UPOWER_DEVICE_STATE_FULL : {
break;
}
default: {
status.charging_time = std::numeric_limits<double>::infinity();
}
}
return status;
// static
std::unique_ptr<BatteryStatusManagerLinux>
BatteryStatusManagerLinux::CreateForTesting(
const BatteryStatusService::BatteryUpdateCallback& callback,
dbus::Bus* bus) {
std::unique_ptr<BatteryStatusManagerLinux> manager(
new BatteryStatusManagerLinux(callback));
if (manager->StartNotifierThreadIfNecessary())
manager->notifier_thread_->SetDBusForTesting(bus);
else
manager.reset();
return manager;
}
// static
......
......@@ -6,13 +6,13 @@
#include "device/battery/battery_export.h"
#include "device/battery/battery_status.mojom.h"
#include "device/battery/battery_status_manager.h"
namespace base {
class DictionaryValue;
}
namespace dbus {
class Bus;
} // namespace dbus
namespace device {
// UPowerDeviceState reflects the possible UPower.Device.State values,
// see upower.freedesktop.org/docs/Device.html#Device:State.
enum UPowerDeviceState {
......@@ -25,9 +25,50 @@ enum UPowerDeviceState {
UPOWER_DEVICE_STATE_PENDING_DISCHARGE = 6,
};
// Returns the BatteryStatus computed using the provided dictionary.
DEVICE_BATTERY_EXPORT BatteryStatus ComputeWebBatteryStatus(
const base::DictionaryValue& dictionary);
// UPowerDeviceType reflects the possible UPower.Device.Type values,
// see upower.freedesktop.org/docs/Device.html#Device:Type.
enum UPowerDeviceType {
UPOWER_DEVICE_TYPE_UNKNOWN = 0,
UPOWER_DEVICE_TYPE_LINE_POWER = 1,
UPOWER_DEVICE_TYPE_BATTERY = 2,
UPOWER_DEVICE_TYPE_UPS = 3,
UPOWER_DEVICE_TYPE_MONITOR = 4,
UPOWER_DEVICE_TYPE_MOUSE = 5,
UPOWER_DEVICE_TYPE_KEYBOARD = 6,
UPOWER_DEVICE_TYPE_PDA = 7,
UPOWER_DEVICE_TYPE_PHONE = 8,
};
// Creates a notification thread and delegates Start/Stop calls to it.
class DEVICE_BATTERY_EXPORT BatteryStatusManagerLinux
: public BatteryStatusManager {
public:
explicit BatteryStatusManagerLinux(
const BatteryStatusService::BatteryUpdateCallback& callback);
~BatteryStatusManagerLinux() override;
private:
friend class BatteryStatusManagerLinuxTest;
class BatteryStatusNotificationThread;
// BatteryStatusManager:
bool StartListeningBatteryChange() override;
void StopListeningBatteryChange() override;
// Starts the notifier thread if not already started and returns true on
// success.
bool StartNotifierThreadIfNecessary();
base::Thread* GetNotifierThreadForTesting();
static std::unique_ptr<BatteryStatusManagerLinux> CreateForTesting(
const BatteryStatusService::BatteryUpdateCallback& callback,
dbus::Bus* bus);
BatteryStatusService::BatteryUpdateCallback callback_;
std::unique_ptr<BatteryStatusNotificationThread> notifier_thread_;
DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux);
};
} // namespace device
......
......@@ -4,17 +4,541 @@
#include "device/battery/battery_status_manager_linux.h"
#include "base/values.h"
#include <limits>
#include <list>
#include <string>
#include "base/bind.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "dbus/mock_bus.h"
#include "dbus/mock_object_proxy.h"
#include "dbus/object_path.h"
#include "dbus/property.h"
#include "device/battery/battery_status_manager_linux-inl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::Invoke;
using testing::NiceMock;
using testing::Return;
using testing::Unused;
namespace device {
namespace {
const char kUPowerDeviceACLinePath[] =
"/org/freedesktop/UPower/devices/line_power_AC";
const char kUPowerDeviceBattery0Path[] =
"/org/freedesktop/UPower/devices/battery_BAT0";
const char kUPowerDeviceBattery1Path[] =
"/org/freedesktop/UPower/devices/battery_BAT1";
const char kUPowerDisplayDevicePath[] =
"/org/freedesktop/UPower/devices/DisplayDevice";
class MockUPowerObject {
public:
MockUPowerObject() {}
void ConnectToSignal(
const std::string& interface_name,
const std::string& signal_name,
dbus::ObjectProxy::SignalCallback signal_callback,
dbus::ObjectProxy::OnConnectedCallback on_connected_callback);
dbus::Response* CreateCallMethodResponse(dbus::MethodCall* method_call,
Unused);
void SignalDeviceAdded(const std::string& added_device_path);
void SignalDeviceRemoved(const std::string& removed_device_path);
scoped_refptr<dbus::MockObjectProxy> proxy;
dbus::ObjectProxy::SignalCallback signal_callback_device_added;
dbus::ObjectProxy::SignalCallback signal_callback_device_changed;
dbus::ObjectProxy::SignalCallback signal_callback_device_removed;
std::string daemon_version;
std::list<std::string> devices;
std::string display_device;
private:
DISALLOW_COPY_AND_ASSIGN(MockUPowerObject);
};
void MockUPowerObject::ConnectToSignal(
const std::string& interface_name,
const std::string& signal_name,
dbus::ObjectProxy::SignalCallback signal_callback,
dbus::ObjectProxy::OnConnectedCallback on_connected_callback) {
bool on_connected_success = true;
if (interface_name == kUPowerInterfaceName) {
if (signal_name == kUPowerSignalDeviceAdded)
signal_callback_device_added = signal_callback;
else if (signal_name == kUPowerSignalDeviceRemoved)
signal_callback_device_removed = signal_callback;
else
on_connected_success = false;
} else {
on_connected_success = false;
}
if (!on_connected_success) {
LOG(WARNING) << "MockUPowerObject::" << __FUNCTION__
<< " Unexpected interface=" << interface_name
<< ", signal=" << signal_name;
}
on_connected_callback.Run(interface_name, signal_name, on_connected_success);
}
dbus::Response* MockUPowerObject::CreateCallMethodResponse(
dbus::MethodCall* method_call,
Unused) {
if (method_call->GetInterface() == kUPowerInterfaceName) {
if (method_call->GetMember() == kUPowerMethodEnumerateDevices) {
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
dbus::MessageWriter array_writer(nullptr);
writer.OpenArray("o", &array_writer);
for (const auto& device : devices)
array_writer.AppendObjectPath(dbus::ObjectPath(device));
writer.CloseContainer(&array_writer);
return response.release();
} else if (method_call->GetMember() == kUPowerMethodGetDisplayDevice) {
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
if (!display_device.empty()) {
dbus::MessageWriter writer(response.get());
writer.AppendObjectPath(dbus::ObjectPath(display_device));
}
return response.release();
}
} else if (method_call->GetInterface() == dbus::kPropertiesInterface) {
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
if (method_call->GetMember() == dbus::kPropertiesGet) {
dbus::MessageReader reader(method_call);
std::string interface_name;
std::string property_name;
if (reader.PopString(&interface_name) &&
reader.PopString(&property_name) &&
property_name == kUPowerPropertyDaemonVersion) {
writer.AppendVariantOfString(daemon_version);
return response.release();
}
} else if (method_call->GetMember() == dbus::kPropertiesGetAll) {
dbus::MessageWriter array_writer(nullptr);
dbus::MessageWriter dict_entry_writer(nullptr);
writer.OpenArray("{sv}", &array_writer);
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString(kUPowerPropertyDaemonVersion);
dict_entry_writer.AppendVariantOfString(daemon_version);
array_writer.CloseContainer(&dict_entry_writer);
writer.CloseContainer(&array_writer);
return response.release();
}
}
LOG(ERROR) << "Unexpected method call: " << method_call->ToString();
return nullptr;
}
void MockUPowerObject::SignalDeviceAdded(const std::string& added_device_path) {
dbus::Signal signal(kUPowerInterfaceName, kUPowerSignalDeviceAdded);
signal.SetPath(proxy->object_path());
dbus::MessageWriter writer(&signal);
writer.AppendObjectPath(dbus::ObjectPath(added_device_path));
signal_callback_device_added.Run(&signal);
}
void MockUPowerObject::SignalDeviceRemoved(
const std::string& removed_device_path) {
dbus::Signal signal(kUPowerInterfaceName, kUPowerSignalDeviceRemoved);
signal.SetPath(proxy->object_path());
dbus::MessageWriter writer(&signal);
writer.AppendObjectPath(dbus::ObjectPath(removed_device_path));
signal_callback_device_removed.Run(&signal);
}
struct MockBatteryProperties {
bool is_present = true;
double percentage = 100;
uint32_t state = UPowerDeviceState::UPOWER_DEVICE_STATE_UNKNOWN;
int64_t time_to_empty = 0;
int64_t time_to_full = 0;
uint32_t type = UPowerDeviceType::UPOWER_DEVICE_TYPE_BATTERY;
};
class MockBatteryObject {
public:
MockBatteryObject(dbus::Bus* bus,
const std::string& object_path,
MockBatteryProperties* properties);
void ConnectToSignal(
const std::string& interface_name,
const std::string& signal_name,
dbus::ObjectProxy::SignalCallback signal_callback,
dbus::ObjectProxy::OnConnectedCallback on_connected_callback);
dbus::Response* CreateCallMethodResponse(dbus::MethodCall* method_call,
Unused);
MockBatteryObject& ExpectConnectToSignalChanged();
MockBatteryObject& ExpectConnectToSignalPropertyChanged();
void SignalChanged();
void SignalPropertyChanged(const std::string& property_name);
scoped_refptr<dbus::MockObjectProxy> proxy;
MockBatteryProperties* properties;
dbus::ObjectProxy::SignalCallback signal_callback_changed;
dbus::ObjectProxy::SignalCallback signal_callback_properties_changed;
private:
void AppendPropertyToWriter(dbus::MessageWriter* writer,
const std::string& property_name);
void AppendAllPropertiesToWriter(dbus::MessageWriter* writer);
DISALLOW_COPY_AND_ASSIGN(MockBatteryObject);
};
MockBatteryObject::MockBatteryObject(dbus::Bus* bus,
const std::string& object_path,
MockBatteryProperties* properties)
: proxy(new NiceMock<dbus::MockObjectProxy>(bus,
kUPowerServiceName,
dbus::ObjectPath(object_path))),
properties(properties) {}
void MockBatteryObject::ConnectToSignal(
const std::string& interface_name,
const std::string& signal_name,
dbus::ObjectProxy::SignalCallback signal_callback,
dbus::ObjectProxy::OnConnectedCallback on_connected_callback) {
bool on_connected_success = true;
if (interface_name == kUPowerDeviceInterfaceName &&
signal_name == kUPowerDeviceSignalChanged)
signal_callback_changed = signal_callback;
else if (interface_name == dbus::kPropertiesInterface &&
signal_name == dbus::kPropertiesChanged)
signal_callback_properties_changed = signal_callback;
else
on_connected_success = false;
if (!on_connected_success) {
LOG(WARNING) << "MockBatteryObject::" << __FUNCTION__
<< " Unexpected interface=" << interface_name
<< ", signal=" << signal_name;
}
on_connected_callback.Run(interface_name, signal_name, on_connected_success);
}
dbus::Response* MockBatteryObject::CreateCallMethodResponse(
dbus::MethodCall* method_call,
Unused) {
if (method_call->GetInterface() == dbus::kPropertiesInterface) {
if (method_call->GetMember() == dbus::kPropertiesGet) {
if (!properties)
return nullptr;
dbus::MessageReader reader(method_call);
std::string interface_name;
std::string property_name;
if (reader.PopString(&interface_name) &&
reader.PopString(&property_name)) {
std::unique_ptr<dbus::Response> response =
dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
AppendPropertyToWriter(&writer, property_name);
return response.release();
}
} else if (method_call->GetMember() == dbus::kPropertiesGetAll) {
if (!properties)
return nullptr;
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
AppendAllPropertiesToWriter(&writer);
return response.release();
}
}
LOG(ERROR) << "Unexpected method call: " << method_call->ToString();
return nullptr;
}
MockBatteryObject& MockBatteryObject::ExpectConnectToSignalChanged() {
EXPECT_CALL(*proxy.get(), ConnectToSignal(kUPowerDeviceInterfaceName,
kUPowerDeviceSignalChanged, _, _))
.WillOnce(Invoke(this, &MockBatteryObject::ConnectToSignal));
return *this;
}
MockBatteryObject& MockBatteryObject::ExpectConnectToSignalPropertyChanged() {
EXPECT_CALL(*proxy.get(), ConnectToSignal(dbus::kPropertiesInterface,
dbus::kPropertiesChanged, _, _))
.WillOnce(Invoke(this, &MockBatteryObject::ConnectToSignal));
return *this;
}
void MockBatteryObject::SignalChanged() {
dbus::Signal signal(kUPowerInterfaceName, kUPowerDeviceSignalChanged);
signal.SetPath(proxy->object_path());
dbus::MessageWriter writer(&signal);
writer.AppendString(kUPowerDeviceInterfaceName);
signal_callback_changed.Run(&signal);
}
void MockBatteryObject::SignalPropertyChanged(
const std::string& property_name) {
dbus::Signal signal(dbus::kPropertiesInterface, dbus::kPropertiesChanged);
signal.SetPath(proxy->object_path());
dbus::MessageWriter writer(&signal);
writer.AppendString(kUPowerDeviceInterfaceName);
// Dictionary {sv} of property-name => new value:
dbus::MessageWriter array_writer(nullptr);
writer.OpenArray("{sv}", &array_writer);
writer.CloseContainer(&array_writer);
// Array of invalidated properties:
writer.OpenArray("s", &array_writer);
array_writer.AppendString(property_name);
writer.CloseContainer(&array_writer);
signal_callback_properties_changed.Run(&signal);
}
void MockBatteryObject::AppendPropertyToWriter(
dbus::MessageWriter* writer,
const std::string& property_name) {
if (property_name == kUPowerDevicePropertyIsPresent)
writer->AppendVariantOfBool(properties->is_present);
else if (property_name == kUPowerDevicePropertyPercentage)
writer->AppendVariantOfDouble(properties->percentage);
else if (property_name == kUPowerDevicePropertyState)
writer->AppendVariantOfUint32(properties->state);
else if (property_name == kUPowerDevicePropertyTimeToEmpty)
writer->AppendVariantOfInt64(properties->time_to_empty);
else if (property_name == kUPowerDevicePropertyTimeToFull)
writer->AppendVariantOfInt64(properties->time_to_full);
else if (property_name == kUPowerDevicePropertyType)
writer->AppendVariantOfUint32(properties->type);
else
LOG(WARNING) << __FUNCTION__ << " unknown property: " << property_name;
}
void MockBatteryObject::AppendAllPropertiesToWriter(
dbus::MessageWriter* writer) {
dbus::MessageWriter array_writer(nullptr);
dbus::MessageWriter dict_entry_writer(nullptr);
writer->OpenArray("{sv}", &array_writer);
for (auto property_name :
{kUPowerDevicePropertyIsPresent, kUPowerDevicePropertyState,
kUPowerDevicePropertyTimeToEmpty, kUPowerDevicePropertyTimeToFull,
kUPowerDevicePropertyType}) {
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString(property_name);
AppendPropertyToWriter(&dict_entry_writer, property_name);
array_writer.CloseContainer(&dict_entry_writer);
}
writer->CloseContainer(&array_writer);
}
} // namespace
class BatteryStatusManagerLinuxTest : public testing::Test {
public:
BatteryStatusManagerLinuxTest() {}
void SetUp() override;
MockBatteryObject& SetUpDisplayDeviceProxy(MockBatteryProperties* properties);
void AddDevicePath(const std::string& object_path);
void PushFrontDevicePath(const std::string& object_path);
MockBatteryObject& AddDeviceProxy(const std::string& object_path,
MockBatteryProperties* properties);
MockBatteryObject& PushFrontDeviceProxy(const std::string& object_path,
MockBatteryProperties* properties);
void ExpectGetObjectProxy(const std::string& object_path,
MockBatteryObject* mock_object);
void ExpectGetObjectProxy(const std::string& object_path,
dbus::ObjectProxy* object_proxy);
void DeviceSignalChanged(MockBatteryObject* device);
void DeviceSignalPropertyChanged(MockBatteryObject* device,
const std::string& property_name);
void UPowerSignalDeviceAdded(const std::string& device_path);
void UPowerSignalDeviceRemoved(const std::string& device_path);
void StartBatteryStatusManagerLinux();
int count_battery_updates() const { return count_battery_updates_; }
const BatteryStatus& last_battery_status() const { return last_status_; }
protected:
scoped_refptr<dbus::MockBus> mock_bus_;
MockUPowerObject mock_upower_;
std::unique_ptr<MockBatteryObject> mock_display_device_;
std::list<std::unique_ptr<MockBatteryObject>> mock_battery_devices_;
private:
std::unique_ptr<MockBatteryObject> CreateMockBatteryObject(
const std::string& object_path,
MockBatteryProperties* properties);
void BatteryUpdateCallback(const BatteryStatus& status);
void SyncWithNotifierThread();
std::unique_ptr<BatteryStatusManagerLinux> manager_;
int count_battery_updates_ = 0;
BatteryStatus last_status_;
DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinuxTest);
};
void BatteryStatusManagerLinuxTest::SetUp() {
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SYSTEM;
options.connection_type = dbus::Bus::PRIVATE;
mock_bus_ = new NiceMock<dbus::MockBus>(options);
mock_upower_.proxy = new NiceMock<dbus::MockObjectProxy>(
mock_bus_.get(), kUPowerServiceName, dbus::ObjectPath(kUPowerPath));
ExpectGetObjectProxy(kUPowerPath, mock_upower_.proxy.get());
EXPECT_CALL(*mock_upower_.proxy.get(), MockCallMethodAndBlock(_, _))
.WillRepeatedly(
Invoke(&mock_upower_, &MockUPowerObject::CreateCallMethodResponse));
EXPECT_CALL(
*mock_upower_.proxy.get(),
ConnectToSignal(kUPowerInterfaceName, kUPowerSignalDeviceAdded, _, _))
.WillOnce(Invoke(&mock_upower_, &MockUPowerObject::ConnectToSignal));
EXPECT_CALL(
*mock_upower_.proxy.get(),
ConnectToSignal(kUPowerInterfaceName, kUPowerSignalDeviceRemoved, _, _))
.WillOnce(Invoke(&mock_upower_, &MockUPowerObject::ConnectToSignal));
}
MockBatteryObject& BatteryStatusManagerLinuxTest::SetUpDisplayDeviceProxy(
MockBatteryProperties* properties) {
mock_upower_.display_device = kUPowerDisplayDevicePath;
mock_display_device_ =
CreateMockBatteryObject(mock_upower_.display_device, properties);
return *mock_display_device_.get();
}
void BatteryStatusManagerLinuxTest::AddDevicePath(
const std::string& object_path) {
mock_upower_.devices.push_back(object_path);
}
void BatteryStatusManagerLinuxTest::PushFrontDevicePath(
const std::string& object_path) {
mock_upower_.devices.push_front(object_path);
}
MockBatteryObject& BatteryStatusManagerLinuxTest::AddDeviceProxy(
const std::string& object_path,
MockBatteryProperties* properties) {
AddDevicePath(object_path);
mock_battery_devices_.push_back(
CreateMockBatteryObject(object_path, properties));
return *mock_battery_devices_.back().get();
}
TEST(BatteryStatusManagerLinuxTest, EmptyDictionary) {
base::DictionaryValue dictionary;
MockBatteryObject& BatteryStatusManagerLinuxTest::PushFrontDeviceProxy(
const std::string& object_path,
MockBatteryProperties* properties) {
PushFrontDevicePath(object_path);
mock_battery_devices_.push_front(
CreateMockBatteryObject(object_path, properties));
return *mock_battery_devices_.front().get();
}
void BatteryStatusManagerLinuxTest::ExpectGetObjectProxy(
const std::string& object_path,
MockBatteryObject* mock_object) {
ExpectGetObjectProxy(object_path, mock_object->proxy.get());
}
void BatteryStatusManagerLinuxTest::ExpectGetObjectProxy(
const std::string& object_path,
dbus::ObjectProxy* object_proxy) {
EXPECT_CALL(*mock_bus_.get(),
GetObjectProxy(kUPowerServiceName, dbus::ObjectPath(object_path)))
.WillOnce(Return(object_proxy));
}
void BatteryStatusManagerLinuxTest::DeviceSignalChanged(
MockBatteryObject* device) {
manager_->GetNotifierThreadForTesting()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&MockBatteryObject::SignalChanged, base::Unretained(device)));
SyncWithNotifierThread();
}
void BatteryStatusManagerLinuxTest::DeviceSignalPropertyChanged(
MockBatteryObject* device,
const std::string& property_name) {
manager_->GetNotifierThreadForTesting()->task_runner()->PostTask(
FROM_HERE, base::Bind(&MockBatteryObject::SignalPropertyChanged,
base::Unretained(device), property_name));
SyncWithNotifierThread();
}
void BatteryStatusManagerLinuxTest::UPowerSignalDeviceAdded(
const std::string& device_path) {
ASSERT_FALSE(mock_upower_.signal_callback_device_added.is_null());
manager_->GetNotifierThreadForTesting()->task_runner()->PostTask(
FROM_HERE, base::Bind(&MockUPowerObject::SignalDeviceAdded,
base::Unretained(&mock_upower_), device_path));
SyncWithNotifierThread();
}
void BatteryStatusManagerLinuxTest::UPowerSignalDeviceRemoved(
const std::string& device_path) {
ASSERT_FALSE(mock_upower_.signal_callback_device_removed.is_null());
manager_->GetNotifierThreadForTesting()->task_runner()->PostTask(
FROM_HERE, base::Bind(&MockUPowerObject::SignalDeviceRemoved,
base::Unretained(&mock_upower_), device_path));
SyncWithNotifierThread();
}
void BatteryStatusManagerLinuxTest::StartBatteryStatusManagerLinux() {
manager_ = BatteryStatusManagerLinux::CreateForTesting(
base::Bind(&BatteryStatusManagerLinuxTest::BatteryUpdateCallback,
base::Unretained(this)),
mock_bus_.get());
manager_->StartListeningBatteryChange();
SyncWithNotifierThread();
}
std::unique_ptr<MockBatteryObject>
BatteryStatusManagerLinuxTest::CreateMockBatteryObject(
const std::string& object_path,
MockBatteryProperties* properties) {
std::unique_ptr<MockBatteryObject> mock_object(
new MockBatteryObject(mock_bus_.get(), object_path, properties));
ExpectGetObjectProxy(object_path, mock_object.get());
EXPECT_CALL(*mock_object->proxy.get(), MockCallMethodAndBlock(_, _))
.WillRepeatedly(Invoke(mock_object.get(),
&MockBatteryObject::CreateCallMethodResponse));
return mock_object;
}
void BatteryStatusManagerLinuxTest::BatteryUpdateCallback(
const BatteryStatus& status) {
++count_battery_updates_;
last_status_ = status;
}
void BatteryStatusManagerLinuxTest::SyncWithNotifierThread() {
ASSERT_TRUE(manager_ != nullptr);
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
manager_->GetNotifierThreadForTesting()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event)));
event.Wait();
}
TEST_F(BatteryStatusManagerLinuxTest, NoBattery) {
BatteryStatus default_status;
BatteryStatus status = ComputeWebBatteryStatus(dictionary);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_EQ(default_status.charging, status.charging);
EXPECT_EQ(default_status.charging_time, status.charging_time);
......@@ -22,13 +546,18 @@ TEST(BatteryStatusManagerLinuxTest, EmptyDictionary) {
EXPECT_EQ(default_status.level, status.level);
}
TEST(BatteryStatusManagerLinuxTest, ChargingHalfFull) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_CHARGING);
dictionary.SetDouble("TimeToFull", 0);
dictionary.SetDouble("Percentage", 50);
TEST_F(BatteryStatusManagerLinuxTest, ChargingHalfFull) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_CHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.percentage = 50;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
BatteryStatus status = ComputeWebBatteryStatus(dictionary);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_TRUE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
......@@ -36,13 +565,18 @@ TEST(BatteryStatusManagerLinuxTest, ChargingHalfFull) {
EXPECT_EQ(0.5, status.level);
}
TEST(BatteryStatusManagerLinuxTest, ChargingTimeToFull) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_CHARGING);
dictionary.SetDouble("TimeToFull", 100.f);
dictionary.SetDouble("Percentage", 1);
TEST_F(BatteryStatusManagerLinuxTest, ChargingTimeToFull) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_CHARGING;
battery_bat0_properties.time_to_full = 100;
battery_bat0_properties.percentage = 1;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
BatteryStatus status = ComputeWebBatteryStatus(dictionary);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_TRUE(status.charging);
EXPECT_EQ(100, status.charging_time);
......@@ -50,14 +584,18 @@ TEST(BatteryStatusManagerLinuxTest, ChargingTimeToFull) {
EXPECT_EQ(.01, status.level);
}
TEST(BatteryStatusManagerLinuxTest, FullyCharged) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_FULL);
dictionary.SetDouble("TimeToFull", 100);
dictionary.SetDouble("TimeToEmpty", 200);
dictionary.SetDouble("Percentage", 100);
TEST_F(BatteryStatusManagerLinuxTest, FullyCharged) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state = UPowerDeviceState::UPOWER_DEVICE_STATE_FULL;
battery_bat0_properties.time_to_full = 100;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 100;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
BatteryStatus status = ComputeWebBatteryStatus(dictionary);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_TRUE(status.charging);
EXPECT_EQ(0, status.charging_time);
......@@ -65,14 +603,19 @@ TEST(BatteryStatusManagerLinuxTest, FullyCharged) {
EXPECT_EQ(1, status.level);
}
TEST(BatteryStatusManagerLinuxTest, Discharging) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_DISCHARGING);
dictionary.SetDouble("TimeToFull", 0);
dictionary.SetDouble("TimeToEmpty", 200);
dictionary.SetDouble("Percentage", 90);
TEST_F(BatteryStatusManagerLinuxTest, Discharging) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 90;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
BatteryStatus status = ComputeWebBatteryStatus(dictionary);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
......@@ -80,14 +623,19 @@ TEST(BatteryStatusManagerLinuxTest, Discharging) {
EXPECT_EQ(.9, status.level);
}
TEST(BatteryStatusManagerLinuxTest, DischargingTimeToEmptyUnknown) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_DISCHARGING);
dictionary.SetDouble("TimeToFull", 0);
dictionary.SetDouble("TimeToEmpty", 0);
dictionary.SetDouble("Percentage", 90);
TEST_F(BatteryStatusManagerLinuxTest, DischargingTimeToEmptyUnknown) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 0;
battery_bat0_properties.percentage = 90;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
BatteryStatus status = ComputeWebBatteryStatus(dictionary);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
......@@ -95,14 +643,19 @@ TEST(BatteryStatusManagerLinuxTest, DischargingTimeToEmptyUnknown) {
EXPECT_EQ(.9, status.level);
}
TEST(BatteryStatusManagerLinuxTest, DeviceStateUnknown) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_UNKNOWN);
dictionary.SetDouble("TimeToFull", 0);
dictionary.SetDouble("TimeToEmpty", 0);
dictionary.SetDouble("Percentage", 50);
TEST_F(BatteryStatusManagerLinuxTest, DeviceStateUnknown) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_UNKNOWN;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 0;
battery_bat0_properties.percentage = 50;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
BatteryStatus status = ComputeWebBatteryStatus(dictionary);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_TRUE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
......@@ -110,14 +663,18 @@ TEST(BatteryStatusManagerLinuxTest, DeviceStateUnknown) {
EXPECT_EQ(.5, status.level);
}
TEST(BatteryStatusManagerLinuxTest, DeviceStateEmpty) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_EMPTY);
dictionary.SetDouble("TimeToFull", 0);
dictionary.SetDouble("TimeToEmpty", 0);
dictionary.SetDouble("Percentage", 0);
TEST_F(BatteryStatusManagerLinuxTest, DeviceStateEmpty) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state = UPowerDeviceState::UPOWER_DEVICE_STATE_EMPTY;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 0;
battery_bat0_properties.percentage = 0;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
BatteryStatus status = ComputeWebBatteryStatus(dictionary);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
......@@ -125,12 +682,17 @@ TEST(BatteryStatusManagerLinuxTest, DeviceStateEmpty) {
EXPECT_EQ(0, status.level);
}
TEST(BatteryStatusManagerLinuxTest, LevelRoundedToThreeSignificantDigits) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_DISCHARGING);
dictionary.SetDouble("Percentage", 14.56);
TEST_F(BatteryStatusManagerLinuxTest, LevelRoundedToThreeSignificantDigits) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.percentage = 14.56;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
BatteryStatus status = ComputeWebBatteryStatus(dictionary);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
......@@ -138,6 +700,522 @@ TEST(BatteryStatusManagerLinuxTest, LevelRoundedToThreeSignificantDigits) {
EXPECT_EQ(0.15, status.level);
}
} // namespace
TEST_F(BatteryStatusManagerLinuxTest, UsingFirstBatteryDevice) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 70;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
MockBatteryProperties battery_bat1_properties;
battery_bat1_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_CHARGING;
battery_bat1_properties.time_to_full = 100;
battery_bat1_properties.time_to_empty = 0;
battery_bat1_properties.percentage = 80;
AddDeviceProxy(kUPowerDeviceBattery1Path, &battery_bat1_properties);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(0.7, status.level);
}
TEST_F(BatteryStatusManagerLinuxTest, SkipNonBatteryDevice) {
MockBatteryProperties line_power_AC_properties;
line_power_AC_properties.type =
UPowerDeviceType::UPOWER_DEVICE_TYPE_LINE_POWER;
AddDeviceProxy(kUPowerDeviceACLinePath, &line_power_AC_properties);
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 70;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(0.7, status.level);
}
TEST_F(BatteryStatusManagerLinuxTest, UpdateDevicePropertyState) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_CHARGING;
battery_bat0_properties.time_to_full = 100;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 80;
MockBatteryObject& battery_bat0 =
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_TRUE(status.charging);
EXPECT_EQ(100, status.charging_time);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.discharging_time);
EXPECT_EQ(.8, status.level);
int last_count = count_battery_updates();
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
DeviceSignalPropertyChanged(&battery_bat0, kUPowerDevicePropertyState);
status = last_battery_status();
EXPECT_LT(last_count, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(.8, status.level);
}
TEST_F(BatteryStatusManagerLinuxTest, UpdateDevicePropertyPercentage) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 100;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 80;
MockBatteryObject& battery_bat0 =
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(.8, status.level);
int last_count = count_battery_updates();
battery_bat0_properties.percentage = 70;
DeviceSignalPropertyChanged(&battery_bat0, kUPowerDevicePropertyPercentage);
status = last_battery_status();
EXPECT_LT(last_count, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(.7, status.level);
}
TEST_F(BatteryStatusManagerLinuxTest, UpdateDevicePropertyTimeToEmpty) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 100;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 80;
MockBatteryObject& battery_bat0 =
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(.8, status.level);
int last_count = count_battery_updates();
battery_bat0_properties.time_to_empty = 150;
DeviceSignalPropertyChanged(&battery_bat0, kUPowerDevicePropertyTimeToEmpty);
status = last_battery_status();
EXPECT_LT(last_count, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(150, status.discharging_time);
EXPECT_EQ(.8, status.level);
}
TEST_F(BatteryStatusManagerLinuxTest, UpdateDevicePropertyTimeToFull) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_CHARGING;
battery_bat0_properties.time_to_full = 100;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 80;
MockBatteryObject& battery_bat0 =
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_TRUE(status.charging);
EXPECT_EQ(100, status.charging_time);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.discharging_time);
EXPECT_EQ(.8, status.level);
int last_count = count_battery_updates();
battery_bat0_properties.time_to_full = 50;
DeviceSignalPropertyChanged(&battery_bat0, kUPowerDevicePropertyTimeToFull);
status = last_battery_status();
EXPECT_LT(last_count, count_battery_updates());
EXPECT_TRUE(status.charging);
EXPECT_EQ(50, status.charging_time);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.discharging_time);
EXPECT_EQ(.8, status.level);
}
TEST_F(BatteryStatusManagerLinuxTest, OldDaemonDeviceSignalChanged) {
mock_upower_.daemon_version = "0.9.23";
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 100;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 80;
MockBatteryObject& battery_bat0 =
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalChanged()
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(.8, status.level);
int last_count = count_battery_updates();
battery_bat0_properties.percentage = 70;
DeviceSignalChanged(&battery_bat0);
status = last_battery_status();
EXPECT_LT(last_count, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(.7, status.level);
}
TEST_F(BatteryStatusManagerLinuxTest, DisplayDeviceNoBattery) {
MockBatteryProperties display_device_properties;
display_device_properties.type = UPowerDeviceType::UPOWER_DEVICE_TYPE_UNKNOWN;
SetUpDisplayDeviceProxy(&display_device_properties);
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 90;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(0.9, status.level);
}
TEST_F(BatteryStatusManagerLinuxTest, DisplayDeviceBattery) {
MockBatteryProperties display_device_properties;
display_device_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
display_device_properties.time_to_full = 0;
display_device_properties.time_to_empty = 200;
display_device_properties.percentage = 90;
SetUpDisplayDeviceProxy(&display_device_properties)
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(.9, status.level);
}
TEST_F(BatteryStatusManagerLinuxTest, DisplayDeviceBatterySkipsEnumerate) {
MockBatteryProperties display_device_properties;
display_device_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
display_device_properties.time_to_full = 0;
display_device_properties.time_to_empty = 200;
display_device_properties.percentage = 90;
SetUpDisplayDeviceProxy(&display_device_properties)
.ExpectConnectToSignalPropertyChanged();
AddDevicePath(kUPowerDeviceACLinePath);
AddDevicePath(kUPowerDeviceBattery0Path);
AddDevicePath(kUPowerDeviceBattery1Path);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(.9, status.level);
}
// Adding a display-device will make the BatteryStatusManagerLinux switch to
// the display-device.
TEST_F(BatteryStatusManagerLinuxTest, SignalDeviceAddedDisplayDevice) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 70;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(0.7, status.level);
int last_count = count_battery_updates();
MockBatteryProperties display_device_properties;
display_device_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_CHARGING;
display_device_properties.time_to_full = 100;
display_device_properties.time_to_empty = 150;
display_device_properties.percentage = 80;
SetUpDisplayDeviceProxy(&display_device_properties)
.ExpectConnectToSignalPropertyChanged();
UPowerSignalDeviceAdded(mock_upower_.display_device);
status = last_battery_status();
EXPECT_LT(last_count, count_battery_updates());
EXPECT_TRUE(status.charging);
EXPECT_EQ(100, status.charging_time);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.discharging_time);
EXPECT_EQ(0.8, status.level);
}
// Prepending a battery should switch to that battery.
TEST_F(BatteryStatusManagerLinuxTest, SignalDeviceAddedBatteryAtFront) {
MockBatteryProperties battery_bat1_properties;
battery_bat1_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat1_properties.time_to_full = 0;
battery_bat1_properties.time_to_empty = 200;
battery_bat1_properties.percentage = 70;
AddDeviceProxy(kUPowerDeviceBattery1Path, &battery_bat1_properties)
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(0.7, status.level);
int last_count = count_battery_updates();
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 150;
battery_bat0_properties.percentage = 50;
PushFrontDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
UPowerSignalDeviceAdded(kUPowerDeviceBattery0Path);
status = last_battery_status();
EXPECT_LT(last_count, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(150, status.discharging_time);
EXPECT_EQ(0.5, status.level);
}
// Appending a battery should keep the current battery.
TEST_F(BatteryStatusManagerLinuxTest, SignalDeviceAddedBatteryAtBack) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 150;
battery_bat0_properties.percentage = 50;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(150, status.discharging_time);
EXPECT_EQ(0.5, status.level);
int last_count = count_battery_updates();
MockBatteryProperties battery_bat1_properties;
battery_bat1_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat1_properties.time_to_full = 0;
battery_bat1_properties.time_to_empty = 200;
battery_bat1_properties.percentage = 70;
AddDeviceProxy(kUPowerDeviceBattery1Path, &battery_bat1_properties);
UPowerSignalDeviceAdded(kUPowerDeviceBattery1Path);
status = last_battery_status();
EXPECT_LT(last_count, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(150, status.discharging_time);
EXPECT_EQ(0.5, status.level);
}
// Adding a device that is no battery should not change anything.
TEST_F(BatteryStatusManagerLinuxTest, SignalDeviceAddedNoBattery) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 70;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(0.7, status.level);
int last_count = count_battery_updates();
MockBatteryProperties line_power_AC_properties;
line_power_AC_properties.type =
UPowerDeviceType::UPOWER_DEVICE_TYPE_LINE_POWER;
PushFrontDeviceProxy(kUPowerDeviceACLinePath, &line_power_AC_properties);
UPowerSignalDeviceAdded(kUPowerDeviceACLinePath);
status = last_battery_status();
EXPECT_LT(last_count, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(0.7, status.level);
}
TEST_F(BatteryStatusManagerLinuxTest, SignalDeviceRemovedBattery) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 70;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
MockBatteryProperties battery_bat1_properties;
battery_bat1_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_CHARGING;
battery_bat1_properties.time_to_full = 100;
battery_bat1_properties.time_to_empty = 0;
battery_bat1_properties.percentage = 80;
MockBatteryObject& battery_bat1 =
AddDeviceProxy(kUPowerDeviceBattery1Path, &battery_bat1_properties);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(0.7, status.level);
int last_count = count_battery_updates();
ExpectGetObjectProxy(kUPowerDeviceBattery1Path, &battery_bat1);
battery_bat1.ExpectConnectToSignalPropertyChanged();
EXPECT_EQ(kUPowerDeviceBattery0Path, mock_upower_.devices.front());
mock_upower_.devices.pop_front();
UPowerSignalDeviceRemoved(kUPowerDeviceBattery0Path);
status = last_battery_status();
EXPECT_LT(last_count, count_battery_updates());
EXPECT_TRUE(status.charging);
EXPECT_EQ(100, status.charging_time);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.discharging_time);
EXPECT_EQ(0.8, status.level);
}
TEST_F(BatteryStatusManagerLinuxTest, SignalDeviceRemovedOther) {
MockBatteryProperties battery_bat0_properties;
battery_bat0_properties.state =
UPowerDeviceState::UPOWER_DEVICE_STATE_DISCHARGING;
battery_bat0_properties.time_to_full = 0;
battery_bat0_properties.time_to_empty = 200;
battery_bat0_properties.percentage = 70;
AddDeviceProxy(kUPowerDeviceBattery0Path, &battery_bat0_properties)
.ExpectConnectToSignalPropertyChanged();
MockBatteryProperties line_power_AC_properties;
line_power_AC_properties.type =
UPowerDeviceType::UPOWER_DEVICE_TYPE_LINE_POWER;
AddDeviceProxy(kUPowerDeviceACLinePath, &line_power_AC_properties);
StartBatteryStatusManagerLinux();
BatteryStatus status = last_battery_status();
EXPECT_LE(1, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(0.7, status.level);
int last_count = count_battery_updates();
mock_upower_.devices.pop_back();
UPowerSignalDeviceRemoved(kUPowerDeviceACLinePath);
status = last_battery_status();
EXPECT_EQ(last_count, count_battery_updates());
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.charging_time);
EXPECT_EQ(200, status.discharging_time);
EXPECT_EQ(0.7, status.level);
}
} // namespace device
......@@ -192,7 +192,11 @@
['exclude', '^hid/'],
],
}],
['use_dbus==0', {
['OS=="linux" and use_dbus==1', {
'dependencies': [
'../dbus/dbus.gyp:dbus_test_support',
],
}, { # OS != "linux" or use_dbus==0
'sources!': [
'battery/battery_status_manager_linux_unittest.cc',
],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment