You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
644 lines
21 KiB
644 lines
21 KiB
/*
|
|
* Copyright 2021 HIMSA II K/S - www.himsa.com.
|
|
* Represented by EHIMA - www.ehima.com
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <base/bind.h>
|
|
#include <base/bind_helpers.h>
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "bta_gatt_api_mock.h"
|
|
#include "bta_gatt_queue_mock.h"
|
|
#include "bta_vc_api.h"
|
|
#include "btm_api_mock.h"
|
|
#include "gatt/database_builder.h"
|
|
#include "hardware/bt_gatt_types.h"
|
|
#include "types.h"
|
|
|
|
void btif_storage_add_volume_control(const RawAddress& addr, bool auto_conn) {}
|
|
|
|
namespace bluetooth {
|
|
namespace vc {
|
|
namespace internal {
|
|
namespace {
|
|
|
|
using base::Bind;
|
|
using base::Unretained;
|
|
|
|
using bluetooth::vc::ConnectionState;
|
|
using bluetooth::vc::VolumeControlCallbacks;
|
|
|
|
using testing::_;
|
|
using testing::DoAll;
|
|
using testing::DoDefault;
|
|
using testing::Invoke;
|
|
using testing::Mock;
|
|
using testing::NotNull;
|
|
using testing::Return;
|
|
using testing::SaveArg;
|
|
using testing::SetArgPointee;
|
|
using testing::WithArg;
|
|
|
|
RawAddress GetTestAddress(int index) {
|
|
CHECK_LT(index, UINT8_MAX);
|
|
RawAddress result = {
|
|
{0xC0, 0xDE, 0xC0, 0xDE, 0x00, static_cast<uint8_t>(index)}};
|
|
return result;
|
|
}
|
|
|
|
class MockVolumeControlCallbacks : public VolumeControlCallbacks {
|
|
public:
|
|
MockVolumeControlCallbacks() = default;
|
|
~MockVolumeControlCallbacks() override = default;
|
|
|
|
MOCK_METHOD((void), OnConnectionState,
|
|
(ConnectionState state, const RawAddress& address), (override));
|
|
MOCK_METHOD((void), OnVolumeStateChanged,
|
|
(const RawAddress& address, uint8_t volume, bool mute),
|
|
(override));
|
|
MOCK_METHOD((void), OnGroupVolumeStateChanged,
|
|
(int group_id, uint8_t volume, bool mute), (override));
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(MockVolumeControlCallbacks);
|
|
};
|
|
|
|
class VolumeControlTest : public ::testing::Test {
|
|
private:
|
|
void set_sample_database(uint16_t conn_id, bool vcs, bool vcs_broken,
|
|
bool aics, bool aics_broken, bool vocs,
|
|
bool vocs_broken) {
|
|
gatt::DatabaseBuilder builder;
|
|
builder.AddService(0x0001, 0x0003, Uuid::From16Bit(0x1800), true);
|
|
builder.AddCharacteristic(0x0002, 0x0003, Uuid::From16Bit(0x2a00),
|
|
GATT_CHAR_PROP_BIT_READ);
|
|
/* 0x0004-0x000f RFU */
|
|
if (vcs) {
|
|
/* VCS */
|
|
builder.AddService(0x0010, 0x0026, kVolumeControlUuid, true);
|
|
if (aics) {
|
|
/* TODO Place holder */
|
|
}
|
|
if (vocs) {
|
|
/* TODO Place holder */
|
|
}
|
|
/* 0x0015-0x001f RFU */
|
|
builder.AddCharacteristic(
|
|
0x0020, 0x0021, kVolumeControlStateUuid,
|
|
GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
|
|
builder.AddDescriptor(0x0022,
|
|
Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
|
|
if (!vcs_broken) {
|
|
builder.AddCharacteristic(0x0023, 0x0024, kVolumeControlPointUuid,
|
|
GATT_CHAR_PROP_BIT_WRITE);
|
|
}
|
|
builder.AddCharacteristic(0x0025, 0x0026, kVolumeFlagsUuid,
|
|
GATT_CHAR_PROP_BIT_READ);
|
|
/* 0x0027-0x002f RFU */
|
|
if (aics) {
|
|
/* TODO Place holder for AICS */
|
|
}
|
|
if (vocs) {
|
|
/* TODO Place holder for VOCS */
|
|
}
|
|
}
|
|
/* 0x008c-0x008f RFU */
|
|
|
|
/* GATTS */
|
|
builder.AddService(0x0090, 0x0093,
|
|
Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER), true);
|
|
builder.AddCharacteristic(0x0091, 0x0092,
|
|
Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD),
|
|
GATT_CHAR_PROP_BIT_NOTIFY);
|
|
builder.AddDescriptor(0x0093,
|
|
Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
|
|
services_map[conn_id] = builder.Build().Services();
|
|
|
|
ON_CALL(gatt_queue, ReadCharacteristic(conn_id, _, _, _))
|
|
.WillByDefault(Invoke([&](uint16_t conn_id, uint16_t handle,
|
|
GATT_READ_OP_CB cb, void* cb_data) -> void {
|
|
std::vector<uint8_t> value;
|
|
|
|
switch (handle) {
|
|
case 0x0003:
|
|
/* device name */
|
|
value.resize(20);
|
|
break;
|
|
|
|
case 0x0021:
|
|
/* volume state */
|
|
value.resize(3);
|
|
break;
|
|
|
|
case 0x0026:
|
|
/* volume flags */
|
|
value.resize(1);
|
|
break;
|
|
|
|
default:
|
|
ASSERT_TRUE(false);
|
|
return;
|
|
}
|
|
|
|
cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(),
|
|
cb_data);
|
|
}));
|
|
}
|
|
|
|
protected:
|
|
void SetUp(void) override {
|
|
bluetooth::manager::SetMockBtmInterface(&btm_interface);
|
|
gatt::SetMockBtaGattInterface(&gatt_interface);
|
|
gatt::SetMockBtaGattQueue(&gatt_queue);
|
|
callbacks.reset(new MockVolumeControlCallbacks());
|
|
|
|
// default action for GetCharacteristic function call
|
|
ON_CALL(gatt_interface, GetCharacteristic(_, _))
|
|
.WillByDefault(
|
|
Invoke([&](uint16_t conn_id,
|
|
uint16_t handle) -> const gatt::Characteristic* {
|
|
std::list<gatt::Service>& services = services_map[conn_id];
|
|
for (auto const& service : services) {
|
|
for (auto const& characteristic : service.characteristics) {
|
|
if (characteristic.value_handle == handle) {
|
|
return &characteristic;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}));
|
|
|
|
// default action for GetOwningService function call
|
|
ON_CALL(gatt_interface, GetOwningService(_, _))
|
|
.WillByDefault(Invoke(
|
|
[&](uint16_t conn_id, uint16_t handle) -> const gatt::Service* {
|
|
std::list<gatt::Service>& services = services_map[conn_id];
|
|
for (auto const& service : services) {
|
|
if (service.handle <= handle && service.end_handle >= handle) {
|
|
return &service;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}));
|
|
|
|
// default action for GetServices function call
|
|
ON_CALL(gatt_interface, GetServices(_))
|
|
.WillByDefault(WithArg<0>(
|
|
Invoke([&](uint16_t conn_id) -> std::list<gatt::Service>* {
|
|
return &services_map[conn_id];
|
|
})));
|
|
|
|
// default action for RegisterForNotifications function call
|
|
ON_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, _))
|
|
.WillByDefault(Return(GATT_SUCCESS));
|
|
|
|
// default action for DeregisterForNotifications function call
|
|
ON_CALL(gatt_interface, DeregisterForNotifications(gatt_if, _, _))
|
|
.WillByDefault(Return(GATT_SUCCESS));
|
|
|
|
// default action for WriteDescriptor function call
|
|
ON_CALL(gatt_queue, WriteDescriptor(_, _, _, _, _, _))
|
|
.WillByDefault(
|
|
Invoke([](uint16_t conn_id, uint16_t handle,
|
|
std::vector<uint8_t> value, tGATT_WRITE_TYPE write_type,
|
|
GATT_WRITE_OP_CB cb, void* cb_data) -> void {
|
|
if (cb) cb(conn_id, GATT_SUCCESS, handle, cb_data);
|
|
}));
|
|
}
|
|
|
|
void TearDown(void) override {
|
|
services_map.clear();
|
|
callbacks.reset();
|
|
gatt::SetMockBtaGattQueue(nullptr);
|
|
gatt::SetMockBtaGattInterface(nullptr);
|
|
bluetooth::manager::SetMockBtmInterface(nullptr);
|
|
}
|
|
|
|
void TestAppRegister(void) {
|
|
BtaAppRegisterCallback app_register_callback;
|
|
EXPECT_CALL(gatt_interface, AppRegister(_, _, _))
|
|
.WillOnce(DoAll(SaveArg<0>(&gatt_callback),
|
|
SaveArg<1>(&app_register_callback)));
|
|
VolumeControl::Initialize(callbacks.get());
|
|
ASSERT_TRUE(gatt_callback);
|
|
ASSERT_TRUE(app_register_callback);
|
|
app_register_callback.Run(gatt_if, GATT_SUCCESS);
|
|
ASSERT_TRUE(VolumeControl::IsVolumeControlRunning());
|
|
}
|
|
|
|
void TestAppUnregister(void) {
|
|
EXPECT_CALL(gatt_interface, AppDeregister(gatt_if));
|
|
VolumeControl::CleanUp();
|
|
ASSERT_FALSE(VolumeControl::IsVolumeControlRunning());
|
|
gatt_callback = nullptr;
|
|
}
|
|
|
|
void TestConnect(const RawAddress& address) {
|
|
// by default indicate link as encrypted
|
|
ON_CALL(btm_interface, GetSecurityFlagsByTransport(address, NotNull(), _))
|
|
.WillByDefault(
|
|
DoAll(SetArgPointee<1>(BTM_SEC_FLAG_ENCRYPTED), Return(true)));
|
|
|
|
EXPECT_CALL(gatt_interface, Open(gatt_if, address, true, _));
|
|
VolumeControl::Get()->Connect(address);
|
|
}
|
|
|
|
void TestDisconnect(const RawAddress& address, uint16_t conn_id) {
|
|
if (conn_id) {
|
|
EXPECT_CALL(gatt_interface, Close(conn_id));
|
|
} else {
|
|
EXPECT_CALL(gatt_interface, CancelOpen(gatt_if, address, _));
|
|
}
|
|
VolumeControl::Get()->Disconnect(address);
|
|
}
|
|
|
|
void TestAddFromStorage(const RawAddress& address, bool auto_connect) {
|
|
// by default indicate link as encrypted
|
|
ON_CALL(btm_interface, GetSecurityFlagsByTransport(address, NotNull(), _))
|
|
.WillByDefault(
|
|
DoAll(SetArgPointee<1>(BTM_SEC_FLAG_ENCRYPTED), Return(true)));
|
|
|
|
if (auto_connect) {
|
|
EXPECT_CALL(gatt_interface, Open(gatt_if, address, false, _));
|
|
} else {
|
|
EXPECT_CALL(gatt_interface, Open(gatt_if, address, _, _)).Times(0);
|
|
}
|
|
VolumeControl::Get()->AddFromStorage(address, auto_connect);
|
|
}
|
|
|
|
void TestSubscribeNotifications(const RawAddress& address, uint16_t conn_id,
|
|
std::map<uint16_t, uint16_t>& handle_pairs) {
|
|
SetSampleDatabase(conn_id);
|
|
TestAppRegister();
|
|
TestConnect(address);
|
|
GetConnectedEvent(address, conn_id);
|
|
|
|
EXPECT_CALL(gatt_queue, WriteDescriptor(_, _, _, _, _, _))
|
|
.WillRepeatedly(DoDefault());
|
|
EXPECT_CALL(gatt_interface, RegisterForNotifications(_, _, _))
|
|
.WillRepeatedly(DoDefault());
|
|
|
|
std::vector<uint8_t> notify_value({0x01, 0x00});
|
|
for (auto const& handles : handle_pairs) {
|
|
EXPECT_CALL(gatt_queue, WriteDescriptor(conn_id, handles.second,
|
|
notify_value, GATT_WRITE, _, _))
|
|
.WillOnce(DoDefault());
|
|
EXPECT_CALL(gatt_interface,
|
|
RegisterForNotifications(gatt_if, address, handles.first))
|
|
.WillOnce(DoDefault());
|
|
}
|
|
|
|
GetSearchCompleteEvent(conn_id);
|
|
TestAppUnregister();
|
|
}
|
|
|
|
void TestReadCharacteristic(const RawAddress& address, uint16_t conn_id,
|
|
std::vector<uint16_t> handles) {
|
|
SetSampleDatabase(conn_id);
|
|
TestAppRegister();
|
|
TestConnect(address);
|
|
GetConnectedEvent(address, conn_id);
|
|
|
|
EXPECT_CALL(gatt_queue, ReadCharacteristic(conn_id, _, _, _))
|
|
.WillRepeatedly(DoDefault());
|
|
for (auto const& handle : handles) {
|
|
EXPECT_CALL(gatt_queue, ReadCharacteristic(conn_id, handle, _, _))
|
|
.WillOnce(DoDefault());
|
|
}
|
|
|
|
GetSearchCompleteEvent(conn_id);
|
|
TestAppUnregister();
|
|
}
|
|
|
|
void GetConnectedEvent(const RawAddress& address, uint16_t conn_id) {
|
|
tBTA_GATTC_OPEN event_data = {
|
|
.status = GATT_SUCCESS,
|
|
.conn_id = conn_id,
|
|
.client_if = gatt_if,
|
|
.remote_bda = address,
|
|
.transport = GATT_TRANSPORT_LE,
|
|
.mtu = 240,
|
|
};
|
|
|
|
gatt_callback(BTA_GATTC_OPEN_EVT, (tBTA_GATTC*)&event_data);
|
|
}
|
|
|
|
void GetDisconnectedEvent(const RawAddress& address, uint16_t conn_id) {
|
|
tBTA_GATTC_CLOSE event_data = {
|
|
.status = GATT_SUCCESS,
|
|
.conn_id = conn_id,
|
|
.client_if = gatt_if,
|
|
.remote_bda = address,
|
|
.reason = GATT_CONN_TERMINATE_PEER_USER,
|
|
};
|
|
|
|
gatt_callback(BTA_GATTC_CLOSE_EVT, (tBTA_GATTC*)&event_data);
|
|
}
|
|
|
|
void GetSearchCompleteEvent(uint16_t conn_id) {
|
|
tBTA_GATTC_SEARCH_CMPL event_data = {
|
|
.status = GATT_SUCCESS,
|
|
.conn_id = conn_id,
|
|
};
|
|
|
|
gatt_callback(BTA_GATTC_SEARCH_CMPL_EVT, (tBTA_GATTC*)&event_data);
|
|
}
|
|
|
|
void SetEncryptionResult(const RawAddress& address, bool success) {
|
|
ON_CALL(btm_interface, GetSecurityFlagsByTransport(address, NotNull(), _))
|
|
.WillByDefault(DoAll(SetArgPointee<1>(0), Return(true)));
|
|
EXPECT_CALL(btm_interface,
|
|
SetEncryption(address, _, NotNull(), _, BTM_BLE_SEC_ENCRYPT))
|
|
.WillOnce(Invoke(
|
|
[&success](const RawAddress& bd_addr, tBT_TRANSPORT transport,
|
|
tBTM_SEC_CALLBACK* p_callback, void* p_ref_data,
|
|
tBTM_BLE_SEC_ACT sec_act) -> tBTM_STATUS {
|
|
p_callback(&bd_addr, transport, p_ref_data,
|
|
success ? BTM_SUCCESS : BTM_FAILED_ON_SECURITY);
|
|
return BTM_SUCCESS;
|
|
}));
|
|
}
|
|
|
|
void SetSampleDatabaseVCS(uint16_t conn_id) {
|
|
set_sample_database(conn_id, true, false, false, false, false, false);
|
|
}
|
|
|
|
void SetSampleDatabaseNoVCS(uint16_t conn_id) {
|
|
set_sample_database(conn_id, false, false, true, false, true, false);
|
|
}
|
|
|
|
void SetSampleDatabaseVCSBroken(uint16_t conn_id) {
|
|
set_sample_database(conn_id, true, true, true, false, true, false);
|
|
}
|
|
|
|
void SetSampleDatabase(uint16_t conn_id) {
|
|
set_sample_database(conn_id, true, false, true, false, true, false);
|
|
}
|
|
|
|
std::unique_ptr<MockVolumeControlCallbacks> callbacks;
|
|
bluetooth::manager::MockBtmInterface btm_interface;
|
|
gatt::MockBtaGattInterface gatt_interface;
|
|
gatt::MockBtaGattQueue gatt_queue;
|
|
tBTA_GATTC_CBACK* gatt_callback;
|
|
const uint8_t gatt_if = 0xff;
|
|
std::map<uint16_t, std::list<gatt::Service>> services_map;
|
|
};
|
|
|
|
TEST_F(VolumeControlTest, test_get_uninitialized) {
|
|
ASSERT_DEATH(VolumeControl::Get(), "");
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_initialize) {
|
|
VolumeControl::Initialize(callbacks.get());
|
|
ASSERT_TRUE(VolumeControl::IsVolumeControlRunning());
|
|
VolumeControl::CleanUp();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_initialize_twice) {
|
|
VolumeControl::Initialize(callbacks.get());
|
|
VolumeControl* volume_control_p = VolumeControl::Get();
|
|
VolumeControl::Initialize(callbacks.get());
|
|
ASSERT_EQ(volume_control_p, VolumeControl::Get());
|
|
VolumeControl::CleanUp();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_cleanup_initialized) {
|
|
VolumeControl::Initialize(callbacks.get());
|
|
VolumeControl::CleanUp();
|
|
ASSERT_FALSE(VolumeControl::IsVolumeControlRunning());
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_cleanup_uninitialized) {
|
|
VolumeControl::CleanUp();
|
|
ASSERT_FALSE(VolumeControl::IsVolumeControlRunning());
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_app_registration) {
|
|
TestAppRegister();
|
|
TestAppUnregister();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_connect) {
|
|
TestAppRegister();
|
|
TestConnect(GetTestAddress(0));
|
|
TestAppUnregister();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_add_from_storage) {
|
|
TestAppRegister();
|
|
TestAddFromStorage(GetTestAddress(0), true);
|
|
TestAddFromStorage(GetTestAddress(1), false);
|
|
TestAppUnregister();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_disconnect_non_connected) {
|
|
const RawAddress test_address = GetTestAddress(0);
|
|
TestAppRegister();
|
|
TestConnect(test_address);
|
|
EXPECT_CALL(*callbacks,
|
|
OnConnectionState(ConnectionState::DISCONNECTED, test_address));
|
|
TestDisconnect(test_address, 0);
|
|
TestAppUnregister();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_disconnect_connected) {
|
|
const RawAddress test_address = GetTestAddress(0);
|
|
TestAppRegister();
|
|
TestConnect(test_address);
|
|
GetConnectedEvent(test_address, 1);
|
|
EXPECT_CALL(*callbacks,
|
|
OnConnectionState(ConnectionState::DISCONNECTED, test_address));
|
|
TestDisconnect(test_address, 1);
|
|
TestAppUnregister();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_disconnected) {
|
|
const RawAddress test_address = GetTestAddress(0);
|
|
TestAppRegister();
|
|
TestConnect(test_address);
|
|
GetConnectedEvent(test_address, 1);
|
|
EXPECT_CALL(*callbacks,
|
|
OnConnectionState(ConnectionState::DISCONNECTED, test_address));
|
|
GetDisconnectedEvent(test_address, 1);
|
|
TestAppUnregister();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_disconnected_while_autoconnect) {
|
|
const RawAddress test_address = GetTestAddress(0);
|
|
TestAppRegister();
|
|
TestAddFromStorage(test_address, true);
|
|
GetConnectedEvent(test_address, 1);
|
|
// autoconnect - don't indicate disconnection
|
|
EXPECT_CALL(*callbacks,
|
|
OnConnectionState(ConnectionState::DISCONNECTED, test_address))
|
|
.Times(0);
|
|
GetDisconnectedEvent(test_address, 1);
|
|
TestAppUnregister();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_reconnect_after_encryption_failed) {
|
|
const RawAddress test_address = GetTestAddress(0);
|
|
TestAppRegister();
|
|
TestAddFromStorage(test_address, true);
|
|
SetEncryptionResult(test_address, false);
|
|
// autoconnect - don't indicate disconnection
|
|
EXPECT_CALL(*callbacks,
|
|
OnConnectionState(ConnectionState::DISCONNECTED, test_address))
|
|
.Times(0);
|
|
GetConnectedEvent(test_address, 1);
|
|
Mock::VerifyAndClearExpectations(&btm_interface);
|
|
SetEncryptionResult(test_address, true);
|
|
GetConnectedEvent(test_address, 1);
|
|
TestAppUnregister();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_discovery_vcs_found) {
|
|
const RawAddress test_address = GetTestAddress(0);
|
|
SetSampleDatabaseVCS(1);
|
|
TestAppRegister();
|
|
TestConnect(test_address);
|
|
EXPECT_CALL(*callbacks,
|
|
OnConnectionState(ConnectionState::CONNECTED, test_address));
|
|
GetConnectedEvent(test_address, 1);
|
|
GetSearchCompleteEvent(1);
|
|
Mock::VerifyAndClearExpectations(callbacks.get());
|
|
TestAppUnregister();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_discovery_vcs_not_found) {
|
|
const RawAddress test_address = GetTestAddress(0);
|
|
SetSampleDatabaseNoVCS(1);
|
|
TestAppRegister();
|
|
TestConnect(test_address);
|
|
EXPECT_CALL(*callbacks,
|
|
OnConnectionState(ConnectionState::DISCONNECTED, test_address));
|
|
GetConnectedEvent(test_address, 1);
|
|
|
|
GetSearchCompleteEvent(1);
|
|
Mock::VerifyAndClearExpectations(callbacks.get());
|
|
TestAppUnregister();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_discovery_vcs_broken) {
|
|
const RawAddress test_address = GetTestAddress(0);
|
|
SetSampleDatabaseVCSBroken(1);
|
|
TestAppRegister();
|
|
TestConnect(test_address);
|
|
EXPECT_CALL(*callbacks,
|
|
OnConnectionState(ConnectionState::DISCONNECTED, test_address));
|
|
GetConnectedEvent(test_address, 1);
|
|
GetSearchCompleteEvent(1);
|
|
Mock::VerifyAndClearExpectations(callbacks.get());
|
|
TestAppUnregister();
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_subscribe_vcs_volume_state) {
|
|
std::map<uint16_t, uint16_t> handles({{0x0021, 0x0022}});
|
|
TestSubscribeNotifications(GetTestAddress(0), 1, handles);
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_read_vcs_volume_state) {
|
|
const RawAddress test_address = GetTestAddress(0);
|
|
EXPECT_CALL(*callbacks, OnVolumeStateChanged(test_address, _, _));
|
|
std::vector<uint16_t> handles({0x0021});
|
|
TestReadCharacteristic(test_address, 1, handles);
|
|
}
|
|
|
|
TEST_F(VolumeControlTest, test_read_vcs_volume_flags) {
|
|
std::vector<uint16_t> handles({0x0026});
|
|
TestReadCharacteristic(GetTestAddress(0), 1, handles);
|
|
}
|
|
|
|
class VolumeControlCallbackTest : public VolumeControlTest {
|
|
protected:
|
|
const RawAddress test_address = GetTestAddress(0);
|
|
uint16_t conn_id = 22;
|
|
|
|
void SetUp(void) override {
|
|
VolumeControlTest::SetUp();
|
|
SetSampleDatabase(conn_id);
|
|
TestAppRegister();
|
|
TestConnect(test_address);
|
|
GetConnectedEvent(test_address, conn_id);
|
|
GetSearchCompleteEvent(conn_id);
|
|
}
|
|
|
|
void TearDown(void) override {
|
|
TestAppUnregister();
|
|
VolumeControlTest::TearDown();
|
|
}
|
|
|
|
void GetNotificationEvent(uint16_t handle, std::vector<uint8_t>& value) {
|
|
tBTA_GATTC_NOTIFY event_data = {
|
|
.conn_id = conn_id,
|
|
.bda = test_address,
|
|
.handle = handle,
|
|
.len = (uint8_t)value.size(),
|
|
.is_notify = true,
|
|
};
|
|
|
|
std::copy(value.begin(), value.end(), event_data.value);
|
|
gatt_callback(BTA_GATTC_NOTIF_EVT, (tBTA_GATTC*)&event_data);
|
|
}
|
|
};
|
|
|
|
TEST_F(VolumeControlCallbackTest, test_volume_state_changed) {
|
|
std::vector<uint8_t> value({0x03, 0x01, 0x02});
|
|
EXPECT_CALL(*callbacks, OnVolumeStateChanged(test_address, 0x03, true));
|
|
GetNotificationEvent(0x0021, value);
|
|
}
|
|
|
|
TEST_F(VolumeControlCallbackTest, test_volume_state_changed_malformed) {
|
|
EXPECT_CALL(*callbacks, OnVolumeStateChanged(test_address, _, _)).Times(0);
|
|
std::vector<uint8_t> too_short({0x03, 0x01});
|
|
GetNotificationEvent(0x0021, too_short);
|
|
std::vector<uint8_t> too_long({0x03, 0x01, 0x02, 0x03});
|
|
GetNotificationEvent(0x0021, too_long);
|
|
}
|
|
|
|
class VolumeControlValueSetTest : public VolumeControlTest {
|
|
protected:
|
|
const RawAddress test_address = GetTestAddress(0);
|
|
uint16_t conn_id = 22;
|
|
|
|
void SetUp(void) override {
|
|
VolumeControlTest::SetUp();
|
|
SetSampleDatabase(conn_id);
|
|
TestAppRegister();
|
|
TestConnect(test_address);
|
|
GetConnectedEvent(test_address, conn_id);
|
|
GetSearchCompleteEvent(conn_id);
|
|
}
|
|
|
|
void TearDown(void) override {
|
|
TestAppUnregister();
|
|
VolumeControlTest::TearDown();
|
|
}
|
|
};
|
|
|
|
TEST_F(VolumeControlValueSetTest, test_set_volume) {
|
|
std::vector<uint8_t> expected_data({0x04, 0x00, 0x10});
|
|
EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, expected_data,
|
|
GATT_WRITE, _, _));
|
|
VolumeControl::Get()->SetVolume(test_address, 0x10);
|
|
}
|
|
} // namespace
|
|
} // namespace internal
|
|
} // namespace vc
|
|
} // namespace bluetooth
|