// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/power/peripheral_battery_listener.h"
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include "ash/shell.h"
#include "ash/system/power/peripheral_battery_tests.h"
#include "ash/test/ash_test_base.h"
#include "base/scoped_observation.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/devices/device_data_manager_test_api.h"
#include "ui/events/devices/touchscreen_device.h"
#include "ui/message_center/public/cpp/notification.h"
using testing::_;
using testing::AllOf;
using testing::AnyNumber;
using testing::Eq;
using testing::Expectation;
using testing::Field;
using testing::Ge;
using testing::Gt;
using testing::InSequence;
using testing::Le;
using testing::Lt;
using testing::NiceMock;
using testing::Optional;
using testing::Sequence;
using testing::StrictMock;
using BI = ash::PeripheralBatteryListener::BatteryInfo;
using BatteryInfo = device::BluetoothDevice::BatteryInfo;
using BatteryType = device::BluetoothDevice::BatteryType;
// Annotate testing::Field invocations to improve feedback.
#define AFIELD(element, test) testing::Field(#element, element, test)
namespace {
class MockPeripheralBatteryObserver
: public ash::PeripheralBatteryListener::Observer {
public:
MockPeripheralBatteryObserver() {}
// ash::PeripheralBatteryListener::Observer:
MOCK_METHOD(void,
OnAddingBattery,
(const ash::PeripheralBatteryListener::BatteryInfo& battery));
MOCK_METHOD(void,
OnRemovingBattery,
(const ash::PeripheralBatteryListener::BatteryInfo& battery));
MOCK_METHOD(void,
OnUpdatedBatteryLevel,
(const ash::PeripheralBatteryListener::BatteryInfo& battery));
};
} // namespace
namespace ash {
class PeripheralBatteryListenerTest : public AshTestBase {
public:
// Constants for active field of PeripheralBatteryStylusReceived().
const bool kBluetoothBatteryUpdate = true;
const bool kBatteryPolledUpdate = false;
const bool kBatteryEventUpdate = true;
PeripheralBatteryListenerTest()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
PeripheralBatteryListenerTest(const PeripheralBatteryListenerTest&) = delete;
PeripheralBatteryListenerTest& operator=(
const PeripheralBatteryListenerTest&) = delete;
~PeripheralBatteryListenerTest() override = default;
void SetUp() override {
chromeos::PowerManagerClient::InitializeFake();
AshTestBase::SetUp();
ASSERT_TRUE(ui::DeviceDataManager::HasInstance());
// Simulate the complete listing of input devices, required by the listener.
if (complete_devices_)
ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
mock_adapter_ =
base::MakeRefCounted<NiceMock<device::MockBluetoothAdapter>>();
mock_device_1_ = std::make_unique<NiceMock<device::MockBluetoothDevice>>(
mock_adapter_.get(), /*bluetooth_class=*/0, kBluetoothDeviceName1,
kBluetoothDeviceAddress1, /*paired=*/true, /*connected=*/true);
mock_device_2_ = std::make_unique<NiceMock<device::MockBluetoothDevice>>(
mock_adapter_.get(), /*bluetooth_class=*/0, kBluetoothDeviceName2,
kBluetoothDeviceAddress2, /*paired=*/true, /*connected=*/true);
device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
battery_listener_ = std::make_unique<PeripheralBatteryListener>();
}
void TearDown() override {
battery_listener_.reset();
AshTestBase::TearDown();
chromeos::PowerManagerClient::Shutdown();
}
base::TimeTicks GetTestingClock() { return base::TimeTicks::Now(); }
void ClockAdvance(base::TimeDelta delta) {
task_environment()->AdvanceClock(delta);
}
void CreateInternalTouchscreen(bool garage) {
// Add an internal stylus to our test device manager.
ui::TouchscreenDevice stylus(/*id=*/0, ui::INPUT_DEVICE_INTERNAL,
kTestStylusName, gfx::Size(),
/*touch_points=*/1, /*has_stylus=*/true,
/*has_stylus_garage_switch=*/garage);
stylus.sys_path = base::FilePath(kTestStylusBatteryPath);
ui::DeviceDataManagerTestApi().SetTouchscreenDevices({stylus});
}
void CreateExternalTouchscreen() {
// Add an external stylus to our test device manager.
ui::TouchscreenDevice stylus(/*id=*/0, ui::INPUT_DEVICE_USB,
kTestStylusName, gfx::Size(),
/*touch_points=*/1, /*has_stylus=*/true);
stylus.sys_path = base::FilePath(kTestStylusBatteryPath);
ui::DeviceDataManagerTestApi().SetTouchscreenDevices({stylus});
}
protected:
scoped_refptr<NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
std::unique_ptr<device::MockBluetoothDevice> mock_device_1_;
std::unique_ptr<device::MockBluetoothDevice> mock_device_2_;
std::unique_ptr<PeripheralBatteryListener> battery_listener_;
void set_complete_devices(bool complete_devices) {
complete_devices_ = complete_devices;
}
// SetUp() doesn't complete devices if this is set to false.
bool complete_devices_ = true;
};
class PeripheralBatteryListenerIncompleteDevicesTest
: public PeripheralBatteryListenerTest {
public:
PeripheralBatteryListenerIncompleteDevicesTest() {
set_complete_devices(false);
}
PeripheralBatteryListenerIncompleteDevicesTest(
const PeripheralBatteryListenerIncompleteDevicesTest&) = delete;
PeripheralBatteryListenerIncompleteDevicesTest& operator=(
const PeripheralBatteryListenerIncompleteDevicesTest&) = delete;
~PeripheralBatteryListenerIncompleteDevicesTest() override {}
};
TEST_F(PeripheralBatteryListenerTest, Basic) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
// Level 50 at time 100, listener should be notified.
ClockAdvance(base::Seconds(100));
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestBatteryId))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestBatteryId)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::last_active_update_timestamp, Eq(std::nullopt)),
AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status, Eq(kTestBatteryStatusOut)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 50, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
// Level 5 at time 110, listener should be notified.
ClockAdvance(base::Seconds(10));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestBatteryId)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::last_active_update_timestamp, Eq(std::nullopt)),
AFIELD(&BI::level, Eq(5)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 5, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
// Level -1 at time 115, listener should be notified.
ClockAdvance(base::Seconds(5));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestBatteryId)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::last_active_update_timestamp, Eq(std::nullopt)),
AFIELD(&BI::level, Eq(std::nullopt)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, -1, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
// Level 50 at time 120, listener should be notified.
ClockAdvance(base::Seconds(5));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestBatteryId)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::last_active_update_timestamp, Eq(std::nullopt)),
AFIELD(&BI::level, Eq(50)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 50, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
}
TEST_F(PeripheralBatteryListenerTest, ActiveUpdates) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
// Level 50 at time 100, listener should be notified.
ClockAdvance(base::Seconds(100));
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestBatteryId))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kTestBatteryId)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::charge_status, Eq(kTestBatteryStatusOut)),
AFIELD(&BI::last_active_update_timestamp, Eq(std::nullopt)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 50, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
// Level 5 at time 110, listener should be notified.
ClockAdvance(base::Seconds(10));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestBatteryId)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::last_active_update_timestamp,
Optional(GetTestingClock())))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 5, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryEventUpdate);
// Level -1 at time 115, listener should be notified.
ClockAdvance(base::Seconds(5));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestBatteryId)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::last_active_update_timestamp,
Optional(GetTestingClock())))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, -1, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryEventUpdate);
auto prior_active_update_time = GetTestingClock();
// Level 50 at time 120, listener should be notified.
ClockAdvance(base::Seconds(5));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestBatteryId)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::last_active_update_timestamp,
Optional(prior_active_update_time)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 50, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
}
TEST_F(PeripheralBatteryListenerTest, FirstActiveUpdates) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
// Level 50 at time 100, listener should be notified.
ClockAdvance(base::Seconds(100));
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestBatteryId))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestBatteryId)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::last_active_update_timestamp,
Optional(GetTestingClock())))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 50, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryEventUpdate);
}
TEST_F(PeripheralBatteryListenerTest, InvalidBatteryInfo) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
const std::string invalid_path1 = "invalid-path";
const std::string invalid_path2 = "/sys/class/power_supply/hid-battery";
EXPECT_CALL(listener_observer_mock, OnAddingBattery(_)).Times(0);
EXPECT_CALL(listener_observer_mock, OnUpdatedBatteryLevel(_)).Times(0);
battery_listener_->PeripheralBatteryStatusReceived(
invalid_path1, kTestDeviceName, 10, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
battery_listener_->PeripheralBatteryStatusReceived(
invalid_path2, kTestDeviceName, 10, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, -2, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 101, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
// Note that -1 is a valid battery level for the Listener, so not checked.
}
// Verify that for Bluetooth devices, the correct address gets stored in the
// BatteryInfo's bluetooth_address member, and for non-Bluetooth devices, that
// bluetooth_address member is empty.
TEST_F(PeripheralBatteryListenerTest, ExtractBluetoothAddress) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
const std::string bluetooth_path =
"/sys/class/power_supply/hid-A0:b1:C2:d3:E4:f5-battery";
const std::string expected_bluetooth_address = "a0:b1:c2:d3:e4:f5";
const std::string expected_bluetooth_id =
"battery_bluetooth-a0:b1:c2:d3:e4:f5";
const std::string non_bluetooth_path =
"/sys/class/power_supply/hid-notbluetooth-battery";
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(expected_bluetooth_id))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::key, Eq(expected_bluetooth_id)),
AFIELD(&BI::level, Eq(10)))));
battery_listener_->PeripheralBatteryStatusReceived(
bluetooth_path, kTestDeviceName, 10, kTestBatteryStatusIn,
/*serial_number=*/"", kBluetoothBatteryUpdate);
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(non_bluetooth_path))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::key, Eq(non_bluetooth_path)),
AFIELD(&BI::bluetooth_address, Eq("")))));
battery_listener_->PeripheralBatteryStatusReceived(
non_bluetooth_path, kTestDeviceName, 10, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
}
TEST_F(PeripheralBatteryListenerTest, DeviceRemove) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestBatteryId))));
EXPECT_CALL(listener_observer_mock, OnUpdatedBatteryLevel(_));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 5, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
EXPECT_CALL(listener_observer_mock,
OnRemovingBattery(AFIELD(&BI::key, Eq(kTestBatteryId))));
battery_listener_->RemoveBluetoothBattery(kTestBatteryAddress);
}
TEST_F(PeripheralBatteryListenerTest, StylusNotification) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
const std::string kTestStylusBatteryPath =
"/sys/class/power_supply/hid-AAAA:BBBB:CCCC.DDDD-battery";
const std::string kTestStylusName = "test_stylus";
const auto kTestStylusBatteryStatusDischargingIn = power_manager::
PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_DISCHARGING;
const auto kTestStylusBatteryStatusDischargingOut =
BI::ChargeStatus::kDischarging;
// Add an external stylus to our test device manager.
ui::TouchscreenDevice stylus(/*id=*/0, ui::INPUT_DEVICE_USB, kTestStylusName,
gfx::Size(),
/*touch_points=*/1, /*has_stylus=*/true);
stylus.sys_path = base::FilePath(kTestStylusBatteryPath);
ui::DeviceDataManagerTestApi().SetTouchscreenDevices({stylus});
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestStylusBatteryPath))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status,
Eq(kTestStylusBatteryStatusDischargingOut)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen)),
AFIELD(&BI::bluetooth_address, Eq("")))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 50,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryPolledUpdate);
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::level, Eq(5)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 5,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryEventUpdate);
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::level, Eq(std::nullopt)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, -1,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryEventUpdate);
}
TEST_F(PeripheralBatteryListenerTest,
Bluetooth_CreatesANotificationForEachDevice) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)), AFIELD(&BI::level, Eq(5)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kOther)),
AFIELD(&BI::name, Eq(kBluetoothDeviceName116)),
AFIELD(&BI::bluetooth_address, Eq(kBluetoothDeviceAddress1)))));
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId2))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId2)), AFIELD(&BI::level, Eq(0)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kOther)),
AFIELD(&BI::name, Eq(kBluetoothDeviceName216)),
AFIELD(&BI::bluetooth_address, Eq(kBluetoothDeviceAddress2)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/5));
mock_device_2_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/0));
}
TEST_F(PeripheralBatteryListenerTest,
Bluetooth_RemovesNotificationForDisconnectedDevices) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)), AFIELD(&BI::level, Eq(5)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kOther)),
AFIELD(&BI::name, Eq(kBluetoothDeviceName116)),
AFIELD(&BI::bluetooth_address, Eq(kBluetoothDeviceAddress1)))));
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId2))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId2)), AFIELD(&BI::level, Eq(0)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kOther)),
AFIELD(&BI::name, Eq(kBluetoothDeviceName216)),
AFIELD(&BI::bluetooth_address, Eq(kBluetoothDeviceAddress2)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/5));
mock_device_2_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/0));
EXPECT_CALL(listener_observer_mock,
OnRemovingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
// Verify only the notification for device 1 gets removed.
battery_listener_->DeviceConnectedStateChanged(mock_adapter_.get(),
mock_device_1_.get(), false);
EXPECT_CALL(listener_observer_mock,
OnRemovingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId2))));
// Remove the second notification.
battery_listener_->DeviceRemoved(mock_adapter_.get(), mock_device_2_.get());
}
TEST_F(PeripheralBatteryListenerTest,
Bluetooth_RemovesNotificationForDisconnectedDevicesInOtherOrder) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::bluetooth_address, Eq(kBluetoothDeviceAddress1)))));
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId2))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kBluetoothDeviceId2)),
AFIELD(&BI::bluetooth_address, Eq(kBluetoothDeviceAddress2)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/5));
mock_device_2_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/5));
EXPECT_CALL(listener_observer_mock,
OnRemovingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId2))));
// Remove the second notification.
battery_listener_->DeviceRemoved(mock_adapter_.get(), mock_device_2_.get());
EXPECT_CALL(listener_observer_mock,
OnRemovingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
// Verify only the notification for device 1 gets removed.
battery_listener_->DeviceConnectedStateChanged(mock_adapter_.get(),
mock_device_1_.get(), false);
}
TEST_F(PeripheralBatteryListenerTest, Bluetooth_RemoveAndReconnect) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::bluetooth_address, Eq(kBluetoothDeviceAddress1)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/5));
EXPECT_CALL(listener_observer_mock,
OnRemovingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
battery_listener_->DeviceConnectedStateChanged(mock_adapter_.get(),
mock_device_1_.get(), false);
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::bluetooth_address, Eq(kBluetoothDeviceAddress1)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/1));
}
TEST_F(PeripheralBatteryListenerTest,
Bluetooth_CancelNotificationForInvalidBatteryLevel) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::level, Eq(1)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/1));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::level, Eq(std::nullopt)))));
mock_device_1_->RemoveBatteryInfo(BatteryType::kDefault);
}
// Do notify observer if the battery level drops again under the
// threshold before kNotificationInterval is completed.
TEST_F(PeripheralBatteryListenerTest, EnsureUpdatesWithinSmallTimeIntervals) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
ClockAdvance(base::Seconds(100));
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::level, Eq(1)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/1));
ClockAdvance(base::Seconds(1));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::level, Eq(std::nullopt)))));
mock_device_1_->RemoveBatteryInfo(BatteryType::kDefault);
ClockAdvance(base::Seconds(1));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::level, Eq(1)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/1));
}
// Notify observer if the battery is under threshold, then unknown level and
// then is again under the threshold after kNotificationInterval is completed.
// (Listener should not pay attention to kNotificationInterval anyway.)
TEST_F(PeripheralBatteryListenerTest,
PostNotificationIfBatteryGoesFromUnknownLevelToBelowThreshold) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
ClockAdvance(base::Seconds(100));
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::level, Eq(1)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/1));
ClockAdvance(base::Seconds(1));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::level, Eq(std::nullopt)))));
mock_device_1_->RemoveBatteryInfo(BatteryType::kDefault);
ClockAdvance(base::Seconds(100));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::level, Eq(1)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/1));
}
// If there is an existing notification and the battery level remains low,
// update its content.
TEST_F(PeripheralBatteryListenerTest, UpdateNotificationIfVisible) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
ClockAdvance(base::Seconds(100));
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::level, Eq(5)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/5));
// The battery level remains low, should update the notification.
ClockAdvance(base::Seconds(100));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)),
AFIELD(&BI::last_update_timestamp, Eq(GetTestingClock())),
AFIELD(&BI::level, Eq(3)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/3));
}
TEST_F(PeripheralBatteryListenerTest, MultipleObserversCoexist) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock_1;
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock_2;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs_1{&listener_observer_mock_1};
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs_2{&listener_observer_mock_2};
scoped_listener_obs_1.Observe(battery_listener_.get());
scoped_listener_obs_2.Observe(battery_listener_.get());
EXPECT_CALL(listener_observer_mock_1,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestBatteryId))));
EXPECT_CALL(listener_observer_mock_2,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestBatteryId))));
EXPECT_CALL(listener_observer_mock_1,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::key, Eq(kTestBatteryId)),
AFIELD(&BI::level, Eq(50)))));
EXPECT_CALL(listener_observer_mock_2,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::key, Eq(kTestBatteryId)),
AFIELD(&BI::level, Eq(50)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 50, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
}
TEST_F(PeripheralBatteryListenerTest, ObserverationLifetimeObeyed) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
testing::InSequence sequence;
// Connect observer, add and remove battery
scoped_listener_obs.Observe(battery_listener_.get());
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestBatteryId))));
EXPECT_CALL(listener_observer_mock, OnUpdatedBatteryLevel(_));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 5, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
EXPECT_CALL(listener_observer_mock,
OnRemovingBattery(AFIELD(&BI::key, Eq(kTestBatteryId))));
battery_listener_->RemoveBluetoothBattery(kTestBatteryAddress);
// Disconnect observer, add and remove battery
scoped_listener_obs.Reset();
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 5, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
battery_listener_->RemoveBluetoothBattery(kTestBatteryAddress);
// Reconnect observer, add and remove battery
scoped_listener_obs.Observe(battery_listener_.get());
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestBatteryId))));
EXPECT_CALL(listener_observer_mock, OnUpdatedBatteryLevel(_));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 5, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
EXPECT_CALL(listener_observer_mock,
OnRemovingBattery(AFIELD(&BI::key, Eq(kTestBatteryId))));
battery_listener_->RemoveBluetoothBattery(kTestBatteryAddress);
}
// Check that observers only see events occuring while they are connected.
TEST_F(PeripheralBatteryListenerTest, PartialObserverationLifetimeObeyed) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
testing::InSequence sequence;
// Connect observer, add and remove battery.
scoped_listener_obs.Observe(battery_listener_.get());
EXPECT_CALL(listener_observer_mock, OnAddingBattery(_));
EXPECT_CALL(listener_observer_mock, OnUpdatedBatteryLevel(_));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 5, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
// Disconnect observer before we remove battery.
scoped_listener_obs.Reset();
battery_listener_->RemoveBluetoothBattery(kTestBatteryAddress);
// Reconnect battery.
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 5, kTestBatteryStatusIn,
/*serial_number=*/"", true);
// Reconnect observer, add and remove battery.
EXPECT_CALL(listener_observer_mock, OnAddingBattery(_));
EXPECT_CALL(listener_observer_mock, OnUpdatedBatteryLevel(_));
scoped_listener_obs.Observe(battery_listener_.get());
EXPECT_CALL(listener_observer_mock, OnRemovingBattery(_));
battery_listener_->RemoveBluetoothBattery(kTestBatteryAddress);
}
// Check that observers will get events to 'catch up' on batteries they missed.
TEST_F(PeripheralBatteryListenerTest, PartialObserverationLifetimeCatchUp) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
testing::InSequence sequence;
// Connect battery.
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 5, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryEventUpdate);
EXPECT_CALL(listener_observer_mock, OnAddingBattery(_));
EXPECT_CALL(listener_observer_mock, OnUpdatedBatteryLevel(_));
scoped_listener_obs.Observe(battery_listener_.get());
EXPECT_CALL(listener_observer_mock, OnRemovingBattery(_));
battery_listener_->RemoveBluetoothBattery(kTestBatteryAddress);
}
TEST_F(PeripheralBatteryListenerTest, MultipleObserverationLifetimeObeyed) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock_1;
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock_2;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs_1{&listener_observer_mock_1};
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs_2{&listener_observer_mock_2};
testing::InSequence sequence;
scoped_listener_obs_1.Observe(battery_listener_.get());
EXPECT_CALL(listener_observer_mock_1, OnAddingBattery(_));
EXPECT_CALL(listener_observer_mock_1, OnUpdatedBatteryLevel(_));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 5, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
EXPECT_CALL(listener_observer_mock_2, OnAddingBattery(_));
EXPECT_CALL(listener_observer_mock_2, OnUpdatedBatteryLevel(_));
scoped_listener_obs_2.Observe(battery_listener_.get());
EXPECT_CALL(listener_observer_mock_1, OnRemovingBattery(_));
EXPECT_CALL(listener_observer_mock_2, OnRemovingBattery(_));
battery_listener_->RemoveBluetoothBattery(kTestBatteryAddress);
scoped_listener_obs_1.Reset();
EXPECT_CALL(listener_observer_mock_2, OnAddingBattery(_));
EXPECT_CALL(listener_observer_mock_2, OnUpdatedBatteryLevel(_));
battery_listener_->PeripheralBatteryStatusReceived(
kTestBatteryPath, kTestDeviceName, 5, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryPolledUpdate);
EXPECT_CALL(listener_observer_mock_2, OnRemovingBattery(_));
battery_listener_->RemoveBluetoothBattery(kTestBatteryAddress);
}
TEST_F(PeripheralBatteryListenerTest, Charger) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestChargerId))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kTestChargerId)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kCharging)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 50,
power_manager::
PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING,
/*serial_number=*/"", kBatteryEventUpdate);
}
// TODO(b/215381232): Temporarily support both 'PCHG' name and 'peripheral' name
// till upstream kernel driver is merged. Remove test case when upstream kernel
// driver is merged.
TEST_F(PeripheralBatteryListenerTest, Charger_PCHG) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestChargerId))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kTestChargerId)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kCharging)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestPCHGChargerPath, kTestChargerName, 50,
power_manager::
PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING,
/*serial_number=*/"", kBatteryEventUpdate);
}
TEST_F(PeripheralBatteryListenerTest, ChargerError) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestChargerId))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestChargerId)), AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kError)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 50,
power_manager::PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_ERROR,
/*serial_number=*/"", kBatteryPolledUpdate);
}
// Check that zero value from charger is accepted, this may happen
// if it is charging from a deep discharge.
TEST_F(PeripheralBatteryListenerTest, StylusChargingFromDeepDischarge) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestChargerId))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestChargerId)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::level, Eq(0)),
AFIELD(&BI::charge_status, Eq(PeripheralBatteryListener::BatteryInfo::
ChargeStatus::kCharging)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 0,
power_manager::
PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING,
/*serial_number=*/"", kBatteryEventUpdate);
}
// Check that zero value from charger is accepted, even if there was
// an existing non-zero value from another stylus.
TEST_F(PeripheralBatteryListenerTest, StylusChargingFromDeepDischarge2) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
const int nonZeroBatteryLevel = 74;
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestChargerId))));
// First establish a stylus is charging at a normal level
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestChargerId)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::level, Eq(nonZeroBatteryLevel)),
AFIELD(&BI::charge_status, Eq(PeripheralBatteryListener::BatteryInfo::
ChargeStatus::kCharging)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, nonZeroBatteryLevel,
power_manager::
PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING,
/*serial_number=*/"", kBatteryEventUpdate);
// Then put on a stylus that is in deep discharge
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::level, Eq(0)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 0,
power_manager::
PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING,
/*serial_number=*/"", kBatteryEventUpdate);
}
TEST_F(PeripheralBatteryListenerTest, ChargerErrorTransition) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestChargerId))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestChargerId)), AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kCharging)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 50,
power_manager::
PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING,
/*serial_number=*/"", kBatteryPolledUpdate);
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestChargerId)), AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kError)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 50,
power_manager::PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_ERROR,
/*serial_number=*/"", kBatteryEventUpdate);
}
// Stylus-via-screen updates of level zero should be translated to
// nullopt as zero is not a valid level, but may come through during
// boot or other device creation scenarios.
TEST_F(PeripheralBatteryListenerTest, StylusDiscardsZeros) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
CreateExternalTouchscreen();
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestStylusBatteryPath))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen)),
AFIELD(&BI::level, Eq(std::nullopt)),
AFIELD(&BI::charge_status,
Eq(kTestStylusBatteryStatusDischargingOut)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 0,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryEventUpdate);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::level, Eq(50)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 50,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryEventUpdate);
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::level, Eq(std::nullopt)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 0,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryEventUpdate);
}
// Stylus-via-charger updates of level zero should translate to nullopt if
// no value is known; otherwise they should be ignored as not providing
// information.
TEST_F(PeripheralBatteryListenerTest, StylusChargerDoesNullZeros) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestChargerId))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestChargerId)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::level, Eq(std::nullopt)),
AFIELD(&BI::charge_status, Eq(kTestBatteryStatusOut)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 0, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryEventUpdate);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::level, Eq(50)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 50, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryEventUpdate);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::level, Eq(50)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 0, kTestBatteryStatusIn,
/*serial_number=*/"", kBatteryEventUpdate);
}
// Bluetooth/other HID updates of level zero should come through as expected, as
// we don't know that 0 is invalid.
TEST_F(PeripheralBatteryListenerTest, BluetoothDoesNotDiscardZeros) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)), AFIELD(&BI::level, Eq(0)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kOther)),
AFIELD(&BI::name, Eq(kBluetoothDeviceName116)),
AFIELD(&BI::bluetooth_address, Eq(kBluetoothDeviceAddress1)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/0));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(AFIELD(&BI::level, Eq(5)))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/5));
}
// Stylus garage charging
TEST_F(PeripheralBatteryListenerIncompleteDevicesTest,
DoNotSynthesizeGarageCharger) {
// Create touchscreen w/ stylus, w/o dockswitch
// Verify Stylus Garage does not exist
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
testing::InSequence sequence;
CreateInternalTouchscreen(false);
ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
scoped_listener_obs.Observe(battery_listener_.get());
// Level 50 at time 100, listener should be notified.
ClockAdvance(base::Seconds(100));
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestStylusBatteryPath))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status,
Eq(kTestStylusBatteryStatusDischargingOut)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen)),
AFIELD(&BI::bluetooth_address, Eq("")))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 50,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryPolledUpdate);
}
TEST_F(PeripheralBatteryListenerIncompleteDevicesTest,
DoSynthesizeGarageCharger) {
// Create touchscreen w/ stylus, w/ dockswitch
// Stylus is not garaged at start
// Trigger touchscreen stylus update event
// Verify Stylus Garage does exist
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
CreateInternalTouchscreen(true);
ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
scoped_listener_obs.Observe(battery_listener_.get());
// Level 50 at time 100, listener should be notified.
ClockAdvance(base::Seconds(100));
Expectation a = EXPECT_CALL(
listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kStylusChargerDeviceName))));
Expectation b = EXPECT_CALL(
listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestStylusBatteryPath))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(std::nullopt)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kUnknown)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.After(a);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status,
Eq(kTestStylusBatteryStatusDischargingOut)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.After(b);
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 50,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryPolledUpdate);
}
TEST_F(PeripheralBatteryListenerIncompleteDevicesTest, GarageCharging) {
// Create touchscreen w/ stylus, w/ dockswitch
// Stylus not in dock at beginning
// Put stylus on charger, do not have it touch screen
// Wait for it to come to a full charge
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
CreateInternalTouchscreen(true);
ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
scoped_listener_obs.Observe(battery_listener_.get());
// Level 50 at time 100, listener should be notified.
ClockAdvance(base::Seconds(100));
Sequence a, b;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kStylusChargerDeviceName))))
.InSequence(a);
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestStylusBatteryPath))))
.InSequence(b);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(std::nullopt)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kUnknown)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.InSequence(a);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status,
Eq(kTestStylusBatteryStatusDischargingOut)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.InSequence(b);
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 50,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryPolledUpdate);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(std::nullopt)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kCharging)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.InSequence(a, b);
battery_listener_->OnStylusStateChanged(ui::StylusState::INSERTED);
}
TEST_F(PeripheralBatteryListenerIncompleteDevicesTest, GarageChargesFully) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
CreateInternalTouchscreen(true);
ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
scoped_listener_obs.Observe(battery_listener_.get());
// Level 50 at time 100, listener should be notified.
ClockAdvance(base::Seconds(100));
Sequence a, b;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kStylusChargerDeviceName))))
.InSequence(a);
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestStylusBatteryPath))))
.InSequence(b);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(std::nullopt)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kUnknown)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.InSequence(a);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status,
Eq(kTestStylusBatteryStatusDischargingOut)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.InSequence(b);
// This is a polled update, so it doesn't count as timely information
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 50,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryPolledUpdate);
// This will be called once the stylus is inserted, and called repeatedly
// until the stylus is estimated to be fully charged. Since we started
// without a known level for the stylus, the level will start from 1, counting
// up to 99 until the charge is believed complete.
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Lt(100)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kCharging)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)))))
.Times(AnyNumber())
.InSequence(a, b);
battery_listener_->OnStylusStateChanged(ui::StylusState::INSERTED);
// Then we should have one update at 100% charge.
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(100)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kFull)))))
.InSequence(a, b);
// Move time forward more than enough to fully charge, ensuring timers fire.
task_environment()->FastForwardBy(base::Seconds(kFullGarageChargeTime));
}
TEST_F(PeripheralBatteryListenerIncompleteDevicesTest,
GarageChargesFullyFromFiftyPercent) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
CreateInternalTouchscreen(true);
ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
scoped_listener_obs.Observe(battery_listener_.get());
// Level 50 at time 100, listener should be notified.
ClockAdvance(base::Seconds(100));
Sequence a, b;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kStylusChargerDeviceName))))
.InSequence(a);
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestStylusBatteryPath))))
.InSequence(b);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(std::nullopt)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kUnknown)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.InSequence(a);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status,
Eq(kTestStylusBatteryStatusDischargingOut)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.InSequence(b);
// This is an active update, so states that the stylus level is definitely
// 50%.
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 50,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryEventUpdate);
// The rest of these are strictly sequential
testing::InSequence sequence;
// This will be called once the stylus is inserted, and called repeatedly
// until the stylus is estimated to be fully charged. Since we started
// with a known level for the stylus the level start there, indicating that
// original level until the charge is complete.
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Ge(50)), AFIELD(&BI::level, Le(99)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kCharging)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)))))
.Times(AnyNumber());
battery_listener_->OnStylusStateChanged(ui::StylusState::INSERTED);
// Then we should have one update at 100% charge.
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(100)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kFull)))));
// Move time forward more than enough to fully charge, ensuring timers fire.
task_environment()->FastForwardBy(base::Seconds(kFullGarageChargeTime));
}
TEST_F(PeripheralBatteryListenerIncompleteDevicesTest,
GarageChargingInterrupted) {
// Create touchscreen w/ stylus, w/ dockswitch, w/o stylus in garage
// Put stylus on in garage
// Wait for it to start charging
// Remove from charger
// Ensure it stops charging
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
CreateInternalTouchscreen(true);
ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
scoped_listener_obs.Observe(battery_listener_.get());
Sequence a, b;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kStylusChargerDeviceName))))
.InSequence(a);
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestStylusBatteryPath))))
.InSequence(b);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(std::nullopt)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kUnknown)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.InSequence(a);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::level, Eq(1)),
AFIELD(&BI::charge_status,
Eq(kTestStylusBatteryStatusDischargingOut)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.InSequence(b);
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 1,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryEventUpdate);
ClockAdvance(base::Seconds(100));
// The rest of these are strictly sequential
testing::InSequence sequence;
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(1)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kCharging)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)))))
.Times(AnyNumber());
battery_listener_->OnStylusStateChanged(ui::StylusState::INSERTED);
// Move time forward more than enough to start charging.
task_environment()->FastForwardBy(base::Seconds(3));
// Remove stylus from garage
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(1)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kUnknown)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)))));
battery_listener_->OnStylusStateChanged(ui::StylusState::REMOVED);
// Move time forward enough for anything to go wrong with the timers.
task_environment()->FastForwardBy(base::Seconds(kPartialGarageChargeTime));
}
TEST_F(PeripheralBatteryListenerIncompleteDevicesTest, GarageChargingResumed) {
// Create touchscreen w/ stylus, w/ dockswitch, w/o stylus in garage
// Put stylus on in garage
// Wait for it to start charging
// Remove from charger
// Replace on charger
// Ensure it finishes charging
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
CreateInternalTouchscreen(true);
ui::DeviceDataManagerTestApi().OnDeviceListsComplete();
scoped_listener_obs.Observe(battery_listener_.get());
Sequence a, b;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kStylusChargerDeviceName))))
.InSequence(a);
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestStylusBatteryPath))))
.InSequence(b);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(std::nullopt)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kUnknown)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.InSequence(a);
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::level, Eq(1)),
AFIELD(&BI::charge_status,
Eq(kTestStylusBatteryStatusDischargingOut)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen)),
AFIELD(&BI::bluetooth_address, Eq("")))))
.InSequence(b);
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 1,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryEventUpdate);
ClockAdvance(base::Seconds(100));
// The rest of these are strictly sequential
testing::InSequence sequence;
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(1)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kCharging)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)))))
.Times(AnyNumber());
battery_listener_->OnStylusStateChanged(ui::StylusState::INSERTED);
// Move time forward more than enough to start charging.
task_environment()->FastForwardBy(base::Seconds(kPartialGarageChargeTime));
// Remove stylus from garage
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(1)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kUnknown)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)))));
battery_listener_->OnStylusStateChanged(ui::StylusState::REMOVED);
// Move time forward enough for anything to go wrong with the timers.
task_environment()->FastForwardBy(base::Seconds(kPartialGarageChargeTime));
// Replace stylus, let run to full charge.
// The level at the start should be unchanged, it's still the last known
// level and it won't update until charge is definitely complete.
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(1)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kCharging)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaCharger)))))
.Times(AnyNumber());
// Then we should have one update at 100% charge.
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kStylusChargerDeviceName)),
AFIELD(&BI::level, Eq(100)),
AFIELD(&BI::charge_status, Eq(BI::ChargeStatus::kFull)))));
battery_listener_->OnStylusStateChanged(ui::StylusState::INSERTED);
// Move time forward more than enough to fully charge.
task_environment()->FastForwardBy(base::Seconds(kFullGarageChargeTime));
}
// NOTE: Cannot yet mock OzonePlatform::GetInstance()->GetInputController(),
// so cannot test scenarios involving stylus on charger from 'boot'.
#if 0
TEST_F(PeripheralBatteryListenerIncompleteDevicesTest,
StylusGaragedOnBoot) {
// Create touchscreen w/ stylus, w/ dockswitch
// Have stylus on charger from boot
// Ensure that it starts on full charge
}
#endif
TEST_F(PeripheralBatteryListenerTest, StylusBatteryEligibility) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
const std::string kTestStylusBatteryPath =
"/sys/class/power_supply/hid-AAAA:BBBB:CCCC.DDDD-battery";
const std::string kTestStylusName = "test_stylus";
const auto kTestStylusBatteryStatusDischargingIn = power_manager::
PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_DISCHARGING;
const auto kTestStylusBatteryStatusDischargingOut =
BI::ChargeStatus::kDischarging;
// Add an external stylus to our test device manager.
ui::TouchscreenDevice stylus(/*id=*/0, ui::INPUT_DEVICE_USB, kTestStylusName,
gfx::Size(),
/*touch_points=*/1, /*has_stylus=*/true);
stylus.sys_path = base::FilePath(kTestStylusBatteryPath);
ui::DeviceDataManagerTestApi().SetTouchscreenDevices({stylus});
testing::InSequence sequence;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kTestStylusBatteryPath))));
for (const char* sn : kStylusEligibleSerialNumbers) {
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::battery_report_eligible, Eq(true)),
AFIELD(&BI::level, Eq(50)),
AFIELD(&BI::charge_status,
Eq(kTestStylusBatteryStatusDischargingOut)),
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen)),
AFIELD(&BI::bluetooth_address, Eq("")))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 50,
kTestStylusBatteryStatusDischargingIn, sn, kBatteryPolledUpdate);
}
for (const char* sn : kStylusIneligibleSerialNumbers) {
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(
AllOf(AFIELD(&BI::key, Eq(kTestStylusBatteryPath)),
AFIELD(&BI::level, Eq(5)),
AFIELD(&BI::battery_report_eligible, Eq(false)))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 5,
kTestStylusBatteryStatusDischargingIn, sn, kBatteryEventUpdate);
}
}
TEST_F(PeripheralBatteryListenerTest,
PostNofiticationWhenDeviceIsConnectedWithLowBattery) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
Sequence a;
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)), AFIELD(&BI::level, Eq(5)),
AFIELD(&BI::battery_report_eligible, Eq(true)),
AFIELD(&BI::bluetooth_address, Eq(kBluetoothDeviceAddress1)))))
.InSequence(a);
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(&BI::key, Eq(kBluetoothDeviceId1))));
mock_device_1_->SetBatteryInfo(
BatteryInfo(BatteryType::kDefault, /*percentage=*/5));
Sequence b;
EXPECT_CALL(
listener_observer_mock,
OnUpdatedBatteryLevel(AllOf(
AFIELD(&BI::key, Eq(kBluetoothDeviceId1)), AFIELD(&BI::level, Eq(5)),
AFIELD(&BI::battery_report_eligible, Eq(true)),
AFIELD(&BI::bluetooth_address, Eq(kBluetoothDeviceAddress1)))))
.InSequence(b);
battery_listener_->DeviceConnectedStateChanged(
mock_adapter_.get(), mock_device_1_.get(), /*is_now_connected=*/true);
}
TEST_F(PeripheralBatteryListenerTest,
IneligibleBatteryReportingStylusViaScreenNoSerialNumber) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
base::HistogramTester histogram_tester_;
ui::TouchscreenDevice stylus(/*id=*/0, ui::INPUT_DEVICE_USB, kTestStylusName,
gfx::Size(),
/*touch_points=*/1, /*has_stylus=*/true);
stylus.sys_path = base::FilePath(kTestStylusBatteryPath);
ui::DeviceDataManagerTestApi().SetTouchscreenDevices({stylus});
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 50,
kTestStylusBatteryStatusDischargingIn, /*serial_number=*/"",
kBatteryPolledUpdate);
histogram_tester_.ExpectUniqueSample(
kStylusBatteryReportingEligibilityHistogramName,
StylusBatteryReportingEligibility::kIneligibleDueToScreen, 1);
}
TEST_F(PeripheralBatteryListenerTest,
EligibleBatteryReportingStylusViaChargerNoSerialNumber) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
base::HistogramTester histogram_tester_;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(
&BI::type, Eq(BI::PeripheralType::kStylusViaCharger))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AFIELD(
&BI::type, Eq(BI::PeripheralType::kStylusViaCharger))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 50,
power_manager::
PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING,
/*serial_number=*/"", kBatteryEventUpdate);
histogram_tester_.ExpectUniqueSample(
kStylusBatteryReportingEligibilityHistogramName,
StylusBatteryReportingEligibility::kEligible, 1);
}
TEST_F(PeripheralBatteryListenerTest,
IncorrectReportingStylusViaChargerWithIneligibleSerialNumber) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
base::HistogramTester histogram_tester_;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(
&BI::type, Eq(BI::PeripheralType::kStylusViaCharger))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AFIELD(
&BI::type, Eq(BI::PeripheralType::kStylusViaCharger))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 50,
power_manager::
PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING,
kStylusIneligibleSerialNumbers[0], kBatteryEventUpdate);
histogram_tester_.ExpectUniqueSample(
kStylusBatteryReportingEligibilityHistogramName,
StylusBatteryReportingEligibility::kIncorrectReports, 1);
}
TEST_F(PeripheralBatteryListenerTest,
IncorrectReportingStylusViaScreenWithIneligibleSerialNumber) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
base::HistogramTester histogram_tester_;
ui::TouchscreenDevice stylus(/*id=*/0, ui::INPUT_DEVICE_USB, kTestStylusName,
gfx::Size(),
/*touch_points=*/1, /*has_stylus=*/true);
stylus.sys_path = base::FilePath(kTestStylusBatteryPath);
ui::DeviceDataManagerTestApi().SetTouchscreenDevices({stylus});
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 50,
kTestStylusBatteryStatusDischargingIn, kStylusIneligibleSerialNumbers[0],
kBatteryPolledUpdate);
histogram_tester_.ExpectUniqueSample(
kStylusBatteryReportingEligibilityHistogramName,
StylusBatteryReportingEligibility::kIncorrectReports, 1);
}
TEST_F(PeripheralBatteryListenerTest,
EligibleBatteryReportingStylusViaChargerWithEligibleSerialNumber) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
base::HistogramTester histogram_tester_;
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(AFIELD(
&BI::type, Eq(BI::PeripheralType::kStylusViaCharger))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(AFIELD(
&BI::type, Eq(BI::PeripheralType::kStylusViaCharger))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestChargerPath, kTestChargerName, 50,
power_manager::
PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING,
kStylusEligibleSerialNumbers[0], kBatteryEventUpdate);
histogram_tester_.ExpectUniqueSample(
kStylusBatteryReportingEligibilityHistogramName,
StylusBatteryReportingEligibility::kEligible, 1);
}
TEST_F(PeripheralBatteryListenerTest,
EligibleBatteryReportingStylusViaScreenWithEligibleSerialNumber) {
testing::StrictMock<MockPeripheralBatteryObserver> listener_observer_mock;
base::ScopedObservation<PeripheralBatteryListener,
PeripheralBatteryListener::Observer>
scoped_listener_obs{&listener_observer_mock};
scoped_listener_obs.Observe(battery_listener_.get());
base::HistogramTester histogram_tester_;
ui::TouchscreenDevice stylus(/*id=*/0, ui::INPUT_DEVICE_USB, kTestStylusName,
gfx::Size(),
/*touch_points=*/1, /*has_stylus=*/true);
stylus.sys_path = base::FilePath(kTestStylusBatteryPath);
ui::DeviceDataManagerTestApi().SetTouchscreenDevices({stylus});
EXPECT_CALL(listener_observer_mock,
OnAddingBattery(
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen))));
EXPECT_CALL(listener_observer_mock,
OnUpdatedBatteryLevel(
AFIELD(&BI::type, Eq(BI::PeripheralType::kStylusViaScreen))));
battery_listener_->PeripheralBatteryStatusReceived(
kTestStylusBatteryPath, kTestStylusName, 50,
kTestStylusBatteryStatusDischargingIn, kStylusEligibleSerialNumbers[0],
kBatteryPolledUpdate);
histogram_tester_.ExpectUniqueSample(
kStylusBatteryReportingEligibilityHistogramName,
StylusBatteryReportingEligibility::kEligible, 1);
}
// TODO: Test needed for eligibility behaviour of stylus chargers.
} // namespace ash