blob: ff64040c907ebcd02b4cd96cf4c6c29f3c8cdfe5 [file] [log] [blame]
// Copyright 2019 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.
#include "ash/power/gatt_battery_percentage_fetcher.h"
#include "base/bind.h"
#include "base/macros.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_characteristic.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_connection.h"
#include "testing/gtest/include/gtest/gtest.h"
using device::BluetoothDevice;
using device::BluetoothRemoteGattCharacteristic;
using testing::_;
using testing::DoAll;
using testing::NiceMock;
using testing::Return;
using testing::SaveArg;
namespace {
constexpr char kServiceID[] = "service id";
constexpr char kCharacteristicID[] = "characteristic id";
constexpr char kDeviceAddress[] = "AA:BB:CC:DD:EE:FF";
constexpr char kBatteryServiceUUID[] = "180F";
constexpr char kBatteryLevelUUID[] = "2A19";
const uint8_t kBatteryPercentage = 100;
ACTION_TEMPLATE(MoveArg,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(pointer)) {
*pointer = std::move(::std::get<k>(args));
}
const device::BluetoothUUID& GetBatteryServiceUUID() {
static const device::BluetoothUUID battery_service_uuid(kBatteryServiceUUID);
return battery_service_uuid;
}
const device::BluetoothUUID& GetBatteryLevelUUID() {
static const device::BluetoothUUID battery_level_uuid(kBatteryLevelUUID);
return battery_level_uuid;
}
} // namespace
namespace ash {
class GattBatteryPercentageFetcherTest : public testing::Test {
protected:
GattBatteryPercentageFetcherTest() = default;
~GattBatteryPercentageFetcherTest() override = default;
void SetUp() override {
mock_adapter_ =
base::MakeRefCounted<NiceMock<device::MockBluetoothAdapter>>();
// By default, |mock_device_| is paired, connected, with Gatt Services
// Discovery completed and returns |mock_service_| when requested for
// for available services. These behaviors can be overridden in tests.
mock_device_ = std::make_unique<NiceMock<device::MockBluetoothDevice>>(
mock_adapter_.get(), 0 /* bluetooth_class */, "device_name",
kDeviceAddress, true /* paired */, true /* connected */);
ON_CALL(*mock_adapter_, GetDevice(kDeviceAddress))
.WillByDefault(Return(mock_device_.get()));
ON_CALL(*mock_device_, IsGattServicesDiscoveryComplete())
.WillByDefault(Return(true));
ASSERT_FALSE(mock_device_->battery_percentage());
// By default, |mock_service_| returns a vector containing
// |mock_characteristic_| when requested for the battery level
// characteristic. This behavior can be overridden in tests.
mock_service_ =
std::make_unique<NiceMock<device::MockBluetoothGattService>>(
mock_device_.get(), kServiceID, GetBatteryServiceUUID(),
true /* is_primary */, false /* is_local */);
std::vector<device::BluetoothRemoteGattService*> services = {
mock_service_.get()};
ON_CALL(*mock_device_, GetGattServices()).WillByDefault(Return(services));
mock_characteristic_ =
std::make_unique<NiceMock<device::MockBluetoothGattCharacteristic>>(
mock_service_.get(), kCharacteristicID, GetBatteryLevelUUID(),
false /* is_local */,
BluetoothRemoteGattCharacteristic::PROPERTY_READ,
BluetoothRemoteGattCharacteristic::PERMISSION_READ);
std::vector<BluetoothRemoteGattCharacteristic*> characteristics = {
mock_characteristic_.get()};
ON_CALL(*mock_service_, GetCharacteristicsByUUID(GetBatteryLevelUUID()))
.WillByDefault(Return(characteristics));
device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
// Create a GattBatteryPercentageFetcher.
ExpectGattConnection();
fetcher_ = GattBatteryPercentageFetcher::Factory::NewInstance(
mock_adapter_, kDeviceAddress,
base::BindOnce(
&GattBatteryPercentageFetcherTest::OnBatteryPercentageFetched,
base::Unretained(this)));
}
void ExpectGattConnection() {
EXPECT_CALL(*mock_device_, CreateGattConnection(_, _))
.WillOnce(DoAll(SaveArg<0>(&create_gatt_connection_success_callback_),
SaveArg<1>(&create_gatt_connection_error_callback_)));
}
void ExpectReadCharacteristic() {
EXPECT_CALL(*mock_characteristic_, ReadRemoteCharacteristic_(_, _))
.WillOnce(
DoAll(MoveArg<0>(&read_remote_characteristic_callback_),
MoveArg<1>(&read_remote_characteristic_error_callback_)));
}
void OnBatteryPercentageFetched(base::Optional<uint8_t> battery_percentage) {
battery_percentage_ = battery_percentage;
battery_percentage_callback_called_ = true;
}
void VerifyFetchResult(base::Optional<uint8_t> expected_result) {
EXPECT_TRUE(battery_percentage_callback_called_);
EXPECT_EQ(expected_result, battery_percentage_);
}
std::unique_ptr<GattBatteryPercentageFetcher> fetcher_;
base::Optional<uint8_t> battery_percentage_;
bool battery_percentage_callback_called_ = false;
scoped_refptr<NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
std::unique_ptr<device::MockBluetoothDevice> mock_device_;
std::unique_ptr<device::MockBluetoothGattService> mock_service_;
std::unique_ptr<device::MockBluetoothGattCharacteristic> mock_characteristic_;
BluetoothDevice::GattConnectionCallback
create_gatt_connection_success_callback_;
BluetoothDevice::ConnectErrorCallback create_gatt_connection_error_callback_;
BluetoothRemoteGattCharacteristic::ValueCallback
read_remote_characteristic_callback_;
BluetoothRemoteGattCharacteristic::ErrorCallback
read_remote_characteristic_error_callback_;
private:
DISALLOW_COPY_AND_ASSIGN(GattBatteryPercentageFetcherTest);
};
TEST_F(GattBatteryPercentageFetcherTest,
ReadBattery_GattServicesDiscoveredOnGattConnection) {
ExpectReadCharacteristic();
create_gatt_connection_success_callback_.Run(
std::make_unique<NiceMock<device::MockBluetoothGattConnection>>(
mock_adapter_, kDeviceAddress));
std::move(read_remote_characteristic_callback_).Run({kBatteryPercentage});
VerifyFetchResult(kBatteryPercentage);
}
TEST_F(GattBatteryPercentageFetcherTest,
ReadBattery_GattServicesDiscoveredAfterGattConnection) {
ExpectReadCharacteristic();
ON_CALL(*mock_device_, IsGattServicesDiscoveryComplete())
.WillByDefault(Return(false));
// GattServicesDiscoveryComplete() should not have run yet.
EXPECT_TRUE(read_remote_characteristic_error_callback_.is_null());
EXPECT_TRUE(read_remote_characteristic_callback_.is_null());
create_gatt_connection_success_callback_.Run(
std::make_unique<NiceMock<device::MockBluetoothGattConnection>>(
mock_adapter_, kDeviceAddress));
mock_adapter_->NotifyGattServicesDiscovered(mock_device_.get());
// GattServicesDiscoveryComplete() should have run.
std::move(read_remote_characteristic_callback_).Run({kBatteryPercentage});
VerifyFetchResult(kBatteryPercentage);
}
TEST_F(GattBatteryPercentageFetcherTest,
IgnoreGattServicesDiscoveredBeforeGattConnection) {
// Case where the GATT connection has not been established yet, but the
// services are done with discovery. Should not try reading the battery level.
EXPECT_CALL(*mock_characteristic_, ReadRemoteCharacteristic_(_, _)).Times(0);
mock_adapter_->NotifyGattServicesDiscovered(mock_device_.get());
}
TEST_F(GattBatteryPercentageFetcherTest, ErrorOpeningGattConnection) {
create_gatt_connection_error_callback_.Run(
BluetoothDevice::ERROR_AUTH_TIMEOUT);
VerifyFetchResult(base::nullopt /* expected_result */);
}
TEST_F(GattBatteryPercentageFetcherTest, BatteryServiceUnavailable) {
ON_CALL(*mock_device_, GetGattServices())
.WillByDefault(
Return(std::vector<device::BluetoothRemoteGattService*>()));
create_gatt_connection_success_callback_.Run(
std::make_unique<NiceMock<device::MockBluetoothGattConnection>>(
mock_adapter_, kDeviceAddress));
VerifyFetchResult(base::nullopt /* expected_result */);
}
TEST_F(GattBatteryPercentageFetcherTest, MissingBatteryLevelCharacteristic) {
ON_CALL(*mock_service_, GetCharacteristicsByUUID(GetBatteryLevelUUID()))
.WillByDefault(Return(std::vector<BluetoothRemoteGattCharacteristic*>()));
EXPECT_CALL(*mock_characteristic_, ReadRemoteCharacteristic_(_, _)).Times(0);
create_gatt_connection_success_callback_.Run(
std::make_unique<NiceMock<device::MockBluetoothGattConnection>>(
mock_adapter_, kDeviceAddress));
VerifyFetchResult(base::nullopt /* expected_result */);
}
TEST_F(GattBatteryPercentageFetcherTest, ErrorReadingRemoteCharacteristic) {
ExpectReadCharacteristic();
create_gatt_connection_success_callback_.Run(
std::make_unique<NiceMock<device::MockBluetoothGattConnection>>(
mock_adapter_, kDeviceAddress));
std::move(read_remote_characteristic_error_callback_)
.Run(device::BluetoothGattService::GATT_ERROR_UNKNOWN);
VerifyFetchResult(base::nullopt /* expected_result */);
}
TEST_F(GattBatteryPercentageFetcherTest,
BadFormatForBatteryLevelValue_MadeOfMultipleBytes) {
ExpectReadCharacteristic();
create_gatt_connection_success_callback_.Run(
std::make_unique<NiceMock<device::MockBluetoothGattConnection>>(
mock_adapter_, kDeviceAddress));
// Battery value made of a multibyte vector.
std::move(read_remote_characteristic_callback_)
.Run({kBatteryPercentage, kBatteryPercentage});
VerifyFetchResult(base::nullopt /* expected_result */);
}
TEST_F(GattBatteryPercentageFetcherTest,
BadFormatForBatteryLevelValue_ValueAbove100Percent) {
ExpectReadCharacteristic();
create_gatt_connection_success_callback_.Run(
std::make_unique<NiceMock<device::MockBluetoothGattConnection>>(
mock_adapter_, kDeviceAddress));
uint8_t new_battery_percentage = 101;
std::move(read_remote_characteristic_callback_).Run({new_battery_percentage});
VerifyFetchResult(base::nullopt /* expected_result */);
}
} // namespace ash