blob: b43aa6a8dd2020369061baf00ed6cc3107c7f98a [file] [log] [blame]
// Copyright 2016 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 "device/bluetooth/device.h"
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.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 "device/bluetooth/test/mock_bluetooth_gatt_service.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::Return;
namespace bluetooth {
using NiceMockBluetoothAdapter =
testing::NiceMock<device::MockBluetoothAdapter>;
using NiceMockBluetoothDevice = testing::NiceMock<device::MockBluetoothDevice>;
using NiceMockBluetoothGattService =
testing::NiceMock<device::MockBluetoothGattService>;
using NiceMockBluetoothGattCharacteristic =
testing::NiceMock<device::MockBluetoothGattCharacteristic>;
using NiceMockBluetoothGattConnection =
testing::NiceMock<device::MockBluetoothGattConnection>;
using Properties = device::BluetoothGattCharacteristic::Properties;
using Property = device::BluetoothGattCharacteristic::Property;
namespace {
const char kTestLeDeviceAddress0[] = "11:22:33:44:55:66";
const char kTestLeDeviceName0[] = "Test LE Device 0";
const char kTestServiceId0[] = "service_id0";
const char kTestServiceUuid0[] = "1234";
const char kTestServiceId1[] = "service_id1";
const char kTestServiceUuid1[] = "5678";
const char kTestCharacteristicId0[] = "characteristic_id0";
const char kTestCharacteristicUuid0[] = "1234";
const char kTestCharacteristicId1[] = "characteristic_id1";
const char kTestCharacteristicUuid1[] = "5678";
const char kTestCharacteristicId2[] = "characteristic_id2";
const char kTestCharacteristicUuid2[] = "9012";
const Properties kReadWriteProperties =
Property::PROPERTY_READ | Property::PROPERTY_WRITE;
const Properties kAllProperties = Property::NUM_PROPERTY - 1;
class BluetoothInterfaceDeviceTest : public testing::Test {
public:
enum class Call { EXPECTED, NOT_EXPECTED };
BluetoothInterfaceDeviceTest()
: adapter_(new NiceMockBluetoothAdapter),
device_(adapter_.get(),
0,
kTestLeDeviceName0,
kTestLeDeviceAddress0,
false,
true),
weak_factory_(this) {
ON_CALL(*adapter_, GetDevice(kTestLeDeviceAddress0))
.WillByDefault(Return(&device_));
auto service1 = std::make_unique<NiceMockBluetoothGattService>(
&device_, kTestServiceId0, device::BluetoothUUID(kTestServiceUuid0),
true /* is_primary */, false /* is_local */);
auto characteristic1 =
std::make_unique<NiceMockBluetoothGattCharacteristic>(
service1.get(), kTestCharacteristicId0,
device::BluetoothUUID(kTestCharacteristicUuid0),
false /* is_local */, kReadWriteProperties, 0 /* permissions */);
auto characteristic2 =
std::make_unique<NiceMockBluetoothGattCharacteristic>(
service1.get(), kTestCharacteristicId1,
device::BluetoothUUID(kTestCharacteristicUuid1),
false /* is_local */, kReadWriteProperties, 0 /* permissions */);
service1->AddMockCharacteristic(std::move(characteristic1));
service1->AddMockCharacteristic(std::move(characteristic2));
auto service2 = std::make_unique<NiceMockBluetoothGattService>(
&device_, kTestServiceId1, device::BluetoothUUID(kTestServiceUuid1),
true /* is_primary */, false /* is_local */);
auto characteristic3 =
std::make_unique<NiceMockBluetoothGattCharacteristic>(
service2.get(), kTestCharacteristicId2,
device::BluetoothUUID(kTestCharacteristicUuid2),
false /* is_local */, kAllProperties, 0 /* permissions */);
service2->AddMockCharacteristic(std::move(characteristic3));
device_.AddMockService(std::move(service1));
device_.AddMockService(std::move(service2));
EXPECT_CALL(device_, GetGattServices())
.WillRepeatedly(
Invoke(&device_, &device::MockBluetoothDevice::GetMockServices));
EXPECT_CALL(device_, GetGattService(testing::_))
.WillRepeatedly(
Invoke(&device_, &device::MockBluetoothDevice::GetMockService));
auto connection = std::make_unique<NiceMockBluetoothGattConnection>(
adapter_, device_.GetAddress());
Device::Create(adapter_, std::move(connection), mojo::MakeRequest(&proxy_));
proxy_.set_connection_error_handler(
base::BindOnce(&BluetoothInterfaceDeviceTest::OnConnectionError,
weak_factory_.GetWeakPtr()));
}
void TearDown() override {
EXPECT_EQ(expected_success_callback_calls_, actual_success_callback_calls_);
EXPECT_EQ(message_pipe_closed_, expect_device_service_deleted_);
proxy_.reset();
base::RunLoop().RunUntilIdle();
}
protected:
void OnConnectionError() { message_pipe_closed_ = true; }
void SimulateGattServicesDiscovered() {
for (auto& observer : adapter_->GetObservers())
observer.GattServicesDiscovered(adapter_.get(), &device_);
}
void SimulateDeviceChanged() {
for (auto& observer : adapter_->GetObservers())
observer.DeviceChanged(adapter_.get(), &device_);
}
void CheckGetServicesCountImpl(Call expected,
size_t expected_service_count,
int num_of_preceding_calls,
std::vector<mojom::ServiceInfoPtr> services) {
EXPECT_EQ(num_of_preceding_calls, actual_callback_count_);
++actual_callback_count_;
if (expected == Call::EXPECTED)
++actual_success_callback_calls_;
EXPECT_EQ(expected_service_count, services.size());
}
Device::GetServicesCallback CheckGetServicesCount(Call expected) {
if (expected == Call::EXPECTED)
++expected_success_callback_calls_;
return base::BindOnce(
&BluetoothInterfaceDeviceTest::CheckGetServicesCountImpl,
weak_factory_.GetWeakPtr(), expected, 2 /* expected_service_count */,
expected_callback_count_++);
}
scoped_refptr<NiceMockBluetoothAdapter> adapter_;
NiceMockBluetoothDevice device_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
mojom::DevicePtr proxy_;
mojo::StrongBindingPtr<mojom::Device> binding_ptr_;
bool message_pipe_closed_ = false;
bool expect_device_service_deleted_ = false;
int expected_success_callback_calls_ = 0;
int actual_success_callback_calls_ = 0;
int actual_callback_count_ = 0;
int expected_callback_count_ = 0;
base::WeakPtrFactory<BluetoothInterfaceDeviceTest> weak_factory_;
};
} // namespace
TEST_F(BluetoothInterfaceDeviceTest, GetServices) {
EXPECT_CALL(device_, IsGattServicesDiscoveryComplete())
.WillRepeatedly(Return(true));
proxy_->GetServices(CheckGetServicesCount(Call::EXPECTED));
base::RunLoop().RunUntilIdle();
}
TEST_F(BluetoothInterfaceDeviceTest, GetServicesNotDiscovered) {
EXPECT_CALL(device_, IsGattServicesDiscoveryComplete())
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
// Client: Sends multiple requests for services.
proxy_->GetServices(CheckGetServicesCount(Call::EXPECTED));
proxy_->GetServices(CheckGetServicesCount(Call::EXPECTED));
base::RunLoop().RunUntilIdle();
SimulateGattServicesDiscovered();
// No more GetServices calls will complete.
SimulateGattServicesDiscovered();
base::RunLoop().RunUntilIdle();
// Client: Sends more requests which run immediately.
proxy_->GetServices(CheckGetServicesCount(Call::EXPECTED));
proxy_->GetServices(CheckGetServicesCount(Call::EXPECTED));
base::RunLoop().RunUntilIdle();
// No more GetServices calls will complete.
SimulateGattServicesDiscovered();
// Wait for message pipe to process error.
base::RunLoop().RunUntilIdle();
}
TEST_F(BluetoothInterfaceDeviceTest,
GetServicesLostConnectionWithPendingRequests) {
EXPECT_CALL(device_, IsGattServicesDiscoveryComplete())
.WillRepeatedly(Return(false));
// Client: Sends multiple requests for services.
proxy_->GetServices(CheckGetServicesCount(Call::NOT_EXPECTED));
proxy_->GetServices(CheckGetServicesCount(Call::NOT_EXPECTED));
EXPECT_EQ(0, actual_callback_count_);
// Simulate connection loss.
device_.SetConnected(false);
SimulateDeviceChanged();
expect_device_service_deleted_ = true;
// Wait for message pipe to process error.
base::RunLoop().RunUntilIdle();
}
TEST_F(BluetoothInterfaceDeviceTest,
GetServicesForcedDisconnectionWithPendingRequests) {
EXPECT_CALL(device_, IsGattServicesDiscoveryComplete())
.WillRepeatedly(Return(false));
// Client: Sends multiple requests for services.
proxy_->GetServices(CheckGetServicesCount(Call::NOT_EXPECTED));
proxy_->GetServices(CheckGetServicesCount(Call::NOT_EXPECTED));
EXPECT_EQ(0, actual_callback_count_);
// Simulate connection loss.
proxy_->Disconnect();
expect_device_service_deleted_ = true;
// Wait for message pipe to process error.
base::RunLoop().RunUntilIdle();
}
} // namespace bluetooth