blob: 3c0f9e80a77ccae3befef104e6835cb991f3f3c7 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include <unordered_map>
#include <base/callback.h>
#include <base/check.h>
#include <base/memory/ref_counted.h>
#include <dbus/message.h>
#include <dbus/mock_bus.h>
#include <dbus/mock_object_proxy.h>
#include <dbus/power_manager/dbus-constants.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "federated/device_status_monitor.h"
#include "power_manager/proto_bindings/power_supply_properties.pb.h"
namespace federated {
namespace {
using ::power_manager::kPowerManagerInterface;
using ::power_manager::kPowerSupplyPollSignal;
using ::testing::_;
using ::testing::SaveArg;
using ::testing::StrictMock;
using ::testing::Test;
// Generates a PowerSupplyProperties proto with the given battery percent and
// state.
power_manager::PowerSupplyProperties GeneratePowerSupplyProto(
const double battery_percent,
power_manager::PowerSupplyProperties::BatteryState battery_state) {
power_manager::PowerSupplyProperties power_supply_proto;
power_supply_proto.set_battery_percent(battery_percent);
power_supply_proto.set_battery_state(battery_state);
return power_supply_proto;
}
// Writes the given string (a serialized proto or "") to dbus signal.
void WriteSerializedProtoToSignal(const std::string& serialized_proto,
dbus::Signal* signal) {
dbus::MessageWriter writer(signal);
writer.AppendArrayOfBytes(
reinterpret_cast<const uint8_t*>(serialized_proto.data()),
serialized_proto.size());
}
} // namespace
class DeviceStatusMonitorTest : public Test {
public:
DeviceStatusMonitorTest()
: mock_dbus_(new StrictMock<dbus::MockBus>(dbus::Bus::Options())),
dbus_object_proxy_(new StrictMock<dbus::MockObjectProxy>(
mock_dbus_.get(),
power_manager::kPowerManagerServiceName,
dbus::ObjectPath(power_manager::kPowerManagerServicePath))) {}
DeviceStatusMonitorTest(const DeviceStatusMonitorTest&) = delete;
DeviceStatusMonitorTest& operator=(const DeviceStatusMonitorTest&) = delete;
void SetUp() override {
EXPECT_CALL(*mock_dbus_,
GetObjectProxy(
power_manager::kPowerManagerServiceName,
dbus::ObjectPath(power_manager::kPowerManagerServicePath)))
.WillOnce(Return(dbus_object_proxy_.get()));
EXPECT_CALL(*dbus_object_proxy_,
DoConnectToSignal(power_manager::kPowerManagerInterface,
power_manager::kPowerSupplyPollSignal, _, _))
.WillOnce(SaveArg<2>(
&on_signal_callbacks_[power_manager::kPowerSupplyPollSignal]));
device_status_monitor_ =
std::make_unique<DeviceStatusMonitor>(mock_dbus_.get());
}
void InvokeSignal(const std::string& signal_name, dbus::Signal* signal) {
ASSERT_TRUE(signal);
auto callback_iter = on_signal_callbacks_.find(signal_name);
ASSERT_NE(callback_iter, on_signal_callbacks_.end());
callback_iter->second.Run(signal);
}
DeviceStatusMonitor* device_status_monitor() const {
DCHECK(device_status_monitor_);
return device_status_monitor_.get();
}
private:
scoped_refptr<StrictMock<dbus::MockBus>> mock_dbus_;
scoped_refptr<StrictMock<dbus::MockObjectProxy>> dbus_object_proxy_;
std::unique_ptr<DeviceStatusMonitor> device_status_monitor_;
// Currently DeviceStatusMonitor only connects to one signal, but may use more
// in the future.
std::unordered_map<std::string,
base::RepeatingCallback<void(dbus::Signal* signal)>>
on_signal_callbacks_;
};
// Tests DeviceStatusMonitor is initialized correctly and can handle unexpected
// empty proto messages.
TEST_F(DeviceStatusMonitorTest, EmptySignal) {
dbus::Signal signal(kPowerManagerInterface, kPowerSupplyPollSignal);
// Initialized as false;
EXPECT_FALSE(device_status_monitor()->TrainingConditionsSatisfied());
// Invoke kPowerSupplyPollSignal without a valid proto message.
InvokeSignal(kPowerSupplyPollSignal, &signal);
EXPECT_FALSE(device_status_monitor()->TrainingConditionsSatisfied());
// Invoke kPowerSupplyPollSignal with a valid empty proto message.
WriteSerializedProtoToSignal("", &signal);
InvokeSignal(kPowerSupplyPollSignal, &signal);
EXPECT_FALSE(device_status_monitor()->TrainingConditionsSatisfied());
}
TEST_F(DeviceStatusMonitorTest, PowerSupplySatisfied) {
// Note: here we must create new signals ranther than write to the old one,
// because the WriteSerializedProtoToSignal is effectively "append", only the
// first written string can be read by the DeviceStatusMonitor.
// battery = 95, state = discharging, satisfied.
dbus::Signal signal_1(kPowerManagerInterface, kPowerSupplyPollSignal);
WriteSerializedProtoToSignal(
GeneratePowerSupplyProto(
95.0, power_manager::PowerSupplyProperties::DISCHARGING)
.SerializeAsString(),
&signal_1);
InvokeSignal(kPowerSupplyPollSignal, &signal_1);
EXPECT_TRUE(device_status_monitor()->TrainingConditionsSatisfied());
// battery = 50, state = charging, satisfied.
dbus::Signal signal_2(kPowerManagerInterface, kPowerSupplyPollSignal);
WriteSerializedProtoToSignal(
GeneratePowerSupplyProto(50.0,
power_manager::PowerSupplyProperties::CHARGING)
.SerializeAsString(),
&signal_2);
InvokeSignal(kPowerSupplyPollSignal, &signal_2);
EXPECT_TRUE(device_status_monitor()->TrainingConditionsSatisfied());
// battery = 100, state = full, satisfied.
dbus::Signal signal_3(kPowerManagerInterface, kPowerSupplyPollSignal);
WriteSerializedProtoToSignal(
GeneratePowerSupplyProto(100.0,
power_manager::PowerSupplyProperties::FULL)
.SerializeAsString(),
&signal_3);
InvokeSignal(kPowerSupplyPollSignal, &signal_3);
EXPECT_TRUE(device_status_monitor()->TrainingConditionsSatisfied());
}
TEST_F(DeviceStatusMonitorTest, PowerSupplyNotSatisfied) {
// battery = 80, state = discharging, unsatisfied.
dbus::Signal signal(kPowerManagerInterface, kPowerSupplyPollSignal);
WriteSerializedProtoToSignal(
GeneratePowerSupplyProto(
80.0, power_manager::PowerSupplyProperties::DISCHARGING)
.SerializeAsString(),
&signal);
InvokeSignal(kPowerSupplyPollSignal, &signal);
EXPECT_FALSE(device_status_monitor()->TrainingConditionsSatisfied());
}
} // namespace federated