pebble/tests/fw/comm/test_gatt_client_accessors.c
2025-01-27 11:38:16 -08:00

319 lines
13 KiB
C

/*
* Copyright 2024 Google LLC
*
* 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 <bluetooth/gatt.h>
#include "comm/ble/gatt_client_accessors.h"
#include "comm/ble/gatt_client_discovery.h"
#include "comm/ble/gatt_service_changed.h"
#include "comm/ble/gap_le_connection.h"
#include "kernel/events.h"
#include "clar.h"
#include <btutil/bt_device.h>
#include <btutil/bt_uuid.h>
// Fakes
///////////////////////////////////////////////////////////
#include "fake_GAPAPI.h"
#include "fake_GATTAPI.h"
#include "fake_GATTAPI_test_vectors.h"
#include "fake_events.h"
#include "fake_new_timer.h"
#include "fake_system_task.h"
#include "fake_pbl_malloc.h"
// Stubs
///////////////////////////////////////////////////////////
#include "stubs_bluetopia_interface.h"
#include "stubs_bt_driver_gatt.h"
#include "stubs_bt_lock.h"
#include "stubs_gatt_client_subscriptions.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_regular_timer.h"
void core_dump_reset(bool is_forced) {
}
void launcher_task_add_callback(void (*callback)(void *data), void *data) {
callback(data);
}
uint16_t gaps_get_starting_att_handle(void) {
return 4;
}
// Helpers
///////////////////////////////////////////////////////////
#define TEST_GATT_CONNECTION_ID (1234)
static BTDeviceInternal prv_dummy_device(uint8_t octet) {
BTDeviceAddress address = {
.octets = {
[0] = octet,
[1] = octet,
[2] = octet,
[3] = octet,
[4] = octet,
[5] = octet,
},
};
BTDevice device = bt_device_init_with_address(address, true /* is_random */);
return *(BTDeviceInternal *)(&device);
}
static BTDeviceInternal prv_connected_dummy_device(uint8_t octet) {
BTDeviceInternal device = prv_dummy_device(octet);
gap_le_connection_add(&device, NULL, true /* local_is_master */);
GAPLEConnection *connection = gap_le_connection_by_device(&device);
connection->gatt_connection_id = TEST_GATT_CONNECTION_ID;
return device;
}
static void prv_mock_put_service_discovery_events(void) {
// Simulate discovery of Blood Pressure service:
fake_gatt_put_discovery_indication_blood_pressure_service(TEST_GATT_CONNECTION_ID);
fake_gatt_put_discovery_indication_health_thermometer_service(TEST_GATT_CONNECTION_ID);
fake_gatt_put_discovery_indication_random_128bit_uuid_service(TEST_GATT_CONNECTION_ID);
fake_gatt_put_discovery_complete_event(GATT_SERVICE_DISCOVERY_STATUS_SUCCESS,
TEST_GATT_CONNECTION_ID);
}
// Tests
///////////////////////////////////////////////////////////
void test_gatt_client_accessors__initialize(void) {
fake_gatt_init();
fake_event_init();
gap_le_connection_init();
}
void test_gatt_client_accessors__cleanup(void) {
gap_le_connection_deinit();
}
void test_gatt_client_accessors__copy_service_refs(void) {
BTDeviceInternal device = prv_connected_dummy_device(1);
// Start discovery:
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
prv_mock_put_service_discovery_events();
const Service *bp_service = fake_gatt_get_blood_pressure_service();
const Service *thermo_service = fake_gatt_get_health_thermometer_service();
const Service *random_128bit_service = fake_gatt_get_random_128bit_uuid_service();
const Service *services[] = {
bp_service,
thermo_service,
random_128bit_service,
};
const uint8_t num_services = 3;
// Test gatt_client_copy_service_refs():
BLEService service_refs[num_services];
uint8_t num_found_services = gatt_client_copy_service_refs(&device, service_refs, num_services);
cl_assert_equal_i(num_found_services, num_services);
for (uint8_t s = 0; s < num_found_services; ++s) {
BLEService service_ref = service_refs[s];
const Service *expected_service = services[s];
// Test gatt_client_service_get_uuid():
const Uuid uuid = gatt_client_service_get_uuid(service_ref);
cl_assert(uuid_equal(&uuid, &expected_service->uuid));
// Test gatt_client_service_get_device():
BTDeviceInternal returned_device = gatt_client_service_get_device(service_ref);
cl_assert(bt_device_equal(&returned_device.opaque, &device.opaque));
// Check Characteristics:
const uint8_t num_characteristics = expected_service->num_characteristics;
BLECharacteristic characteristic_refs[num_characteristics];
// Test gatt_client_service_get_characteristics():
const uint8_t num_found_characteristics =
gatt_client_service_get_characteristics(service_ref, characteristic_refs, num_characteristics);
cl_assert_equal_i(num_characteristics, num_found_characteristics);
for (uint8_t c = 0; c < num_found_characteristics; ++c) {
BLECharacteristic characteristic_ref = characteristic_refs[c];
const Characteristic *expected_characteristic = &expected_service->characteristics[c];
// Test gatt_client_characteristic_get_uuid():
const Uuid uuid = gatt_client_characteristic_get_uuid(characteristic_ref);
cl_assert(uuid_equal(&uuid, &expected_characteristic->uuid));
// Test gatt_client_characteristic_get_properties():
cl_assert_equal_i(gatt_client_characteristic_get_properties(characteristic_ref),
expected_characteristic->properties);
// Test gatt_client_characteristic_get_service():
cl_assert_equal_i(gatt_client_characteristic_get_service(characteristic_ref), service_ref);
// Test gatt_client_characteristic_get_device():
BTDeviceInternal returned_device = gatt_client_characteristic_get_device(characteristic_ref);
cl_assert(bt_device_equal(&returned_device.opaque, &device.opaque));
// Test gatt_client_characteristic_get_descriptors():
const uint8_t num_descriptors = expected_characteristic->num_descriptors;
BLEDescriptor descriptor_refs[num_descriptors];
const uint8_t num_found_descriptors =
gatt_client_characteristic_get_descriptors(characteristic_ref, descriptor_refs, num_descriptors);
cl_assert_equal_i(num_descriptors, num_found_descriptors);
for (uint8_t d = 0; d < num_descriptors; ++d) {
const Descriptor *expected_descriptor = &expected_characteristic->descriptors[d];
const BLEDescriptor descriptor_ref = descriptor_refs[d];
// Test gatt_client_descriptor_get_uuid():
const Uuid uuid = gatt_client_descriptor_get_uuid(descriptor_ref);
cl_assert(uuid_equal(&uuid, &expected_descriptor->uuid));
// Test gatt_client_descriptor_get_characteristic():
cl_assert_equal_i(gatt_client_descriptor_get_characteristic(descriptor_ref), characteristic_ref);
}
}
// Test gatt_client_service_get_included_services():
const uint8_t num_inc_services = expected_service->num_included_services;
BLEService inc_service_refs[num_inc_services];
const uint8_t num_found_included_services =
gatt_client_service_get_included_services(service_ref, inc_service_refs, num_inc_services);
cl_assert_equal_i(num_inc_services, num_found_included_services);
for (uint8_t i = 0; i < num_inc_services; ++i) {
const Service *expected_inc_service = expected_service->included_services[i];
BLEService inc_service = inc_service_refs[i];
// Only check the Service UUID:
const Uuid uuid = gatt_client_service_get_uuid(inc_service);
cl_assert(uuid_equal(&uuid, &expected_inc_service->uuid));
}
}
}
void test_gatt_client_accessors__copy_service_refs_matching(void) {
BTDeviceInternal device = prv_connected_dummy_device(1);
// Start discovery:
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
prv_mock_put_service_discovery_events();
const uint8_t num_services = 1;
// Test gatt_client_copy_service_refs():
BLEService service_refs[num_services];
const Service *bp_service = fake_gatt_get_blood_pressure_service();
uint8_t num_found_services = gatt_client_copy_service_refs_matching_uuid(&device, service_refs,
num_services,
&bp_service->uuid);
cl_assert_equal_i(num_found_services, num_services);
// Test that the UUID matches the Blood Pressure UUID:
const Uuid uuid = gatt_client_service_get_uuid(service_refs[0]);
cl_assert(uuid_equal(&uuid, &bp_service->uuid));
}
void test_gatt_client_accessors__get_characteristics_matching_uuids(void) {
BTDeviceInternal device = prv_connected_dummy_device(1);
// Start discovery:
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
prv_mock_put_service_discovery_events();
const Service *bp_service = fake_gatt_get_blood_pressure_service();
// Get the reference to the Blood Pressure service:
const uint8_t num_services = 1;
BLEService service_refs[num_services];
gatt_client_copy_service_refs_matching_uuid(&device, service_refs,
num_services, &bp_service->uuid);
Uuid matching_uuids[3];
matching_uuids[0] = bp_service->characteristics[1].uuid;
matching_uuids[1] = bt_uuid_expand_16bit(0xffff); // not expected to match
matching_uuids[2] = bp_service->characteristics[0].uuid;
BLECharacteristic characteristics[3];
const uint8_t found =
gatt_client_service_get_characteristics_matching_uuids(service_refs[0], characteristics,
matching_uuids, 3);
cl_assert_equal_i(found, 2);
// Expect the order of the matching_uuids array is preserved:
cl_assert(uuid_equal(&matching_uuids[0], &bp_service->characteristics[1].uuid));
cl_assert(uuid_equal(&matching_uuids[2], &bp_service->characteristics[0].uuid));
// Expect the 0xffff UUID to return "no match":
cl_assert_equal_i(characteristics[1], BLE_CHARACTERISTIC_INVALID);
}
extern uint8_t gatt_client_copy_service_refs_by_discovery_generation(
const BTDeviceInternal *device, BLEService services_out[],
uint8_t num_services, uint8_t discovery_gen);
extern void gatt_client_discovery_discover_range(GAPLEConnection *connection,
ATTHandleRange *hdl_range);
void test_gatt_client_accessors__get_service_refs_by_discovery_gen(void) {
BTDeviceInternal device = prv_connected_dummy_device(1);
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
fake_gatt_put_discovery_indication_blood_pressure_service(TEST_GATT_CONNECTION_ID);
fake_gatt_put_discovery_complete_event(GATT_SERVICE_DISCOVERY_STATUS_SUCCESS, TEST_GATT_CONNECTION_ID);
ATTHandleRange range = {
.start = 0x1,
.end = 0xC000,
};
gatt_client_discovery_discover_range(gap_le_connection_by_device(&device), &range);
fake_gatt_put_discovery_indication_health_thermometer_service(TEST_GATT_CONNECTION_ID);
fake_gatt_put_discovery_indication_random_128bit_uuid_service(TEST_GATT_CONNECTION_ID);
fake_gatt_put_discovery_complete_event(GATT_SERVICE_DISCOVERY_STATUS_SUCCESS, TEST_GATT_CONNECTION_ID);
const Service *bp_service = fake_gatt_get_blood_pressure_service();
const Service *thermo_service = fake_gatt_get_health_thermometer_service();
const Service *random_128bit_service = fake_gatt_get_random_128bit_uuid_service();
BLEService service_refs_out[3];
// Only the BP service should be part of the first generation
uint8_t refs_out = gatt_client_copy_service_refs_by_discovery_generation(
&device, service_refs_out, 3, 0);
cl_assert_equal_i(1, refs_out);
const Uuid uuid = gatt_client_service_get_uuid(service_refs_out[0]);
cl_assert(uuid_equal(&uuid, &bp_service->uuid));
// Thermo & Random 128 bit service should be part of the second gen
refs_out = gatt_client_copy_service_refs_by_discovery_generation(
&device, service_refs_out, 3, 1);
cl_assert_equal_i(2, refs_out);
const Uuid uuid1 = gatt_client_service_get_uuid(service_refs_out[0]);
const Uuid uuid2 = gatt_client_service_get_uuid(service_refs_out[0]);
cl_assert(uuid_equal(&uuid1, &thermo_service->uuid) ||
uuid_equal(&uuid1, &random_128bit_service->uuid));
cl_assert(uuid_equal(&uuid2, &thermo_service->uuid) ||
uuid_equal(&uuid2, &random_128bit_service->uuid));
}