mirror of
https://github.com/google/pebble.git
synced 2025-03-22 11:42:19 +00:00
520 lines
20 KiB
C
520 lines
20 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 "comm/ble/gatt_client_discovery.h"
|
|
#include "comm/ble/gatt_service_changed.h"
|
|
#include "comm/ble/gap_le_connection.h"
|
|
#include "comm/ble/gap_le_task.h"
|
|
|
|
#include "kernel/events.h"
|
|
|
|
#include "clar.h"
|
|
|
|
#include <btutil/bt_device.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_pbl_malloc.h"
|
|
#include "fake_system_task.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_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
|
|
///////////////////////////////////////////////////////////
|
|
|
|
extern TimerID bt_driver_gatt_get_watchdog_timer_id(void);
|
|
|
|
#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_assert_no_event(void) {
|
|
PebbleEvent event = fake_event_get_last();
|
|
cl_assert_equal_i(event.type, PEBBLE_NULL_EVENT);
|
|
}
|
|
|
|
static void prv_assert_event(const BTDeviceInternal *device, BTErrno status) {
|
|
PebbleEvent event = fake_event_get_last();
|
|
cl_assert_equal_i(event.type, PEBBLE_BLE_GATT_CLIENT_EVENT);
|
|
cl_assert_equal_i(event.bluetooth.le.gatt_client_service.subtype,
|
|
PebbleBLEGATTClientEventTypeServiceChange);
|
|
cl_assert_equal_i(event.bluetooth.le.gatt_client_service.info->status,
|
|
status);
|
|
const BTDeviceInternal event_device = event.bluetooth.le.gatt_client_service.info->device;
|
|
const bool equal_devices = bt_device_equal(&device->opaque,
|
|
&event_device.opaque);
|
|
cl_assert_equal_b(equal_devices, true);
|
|
|
|
// clear the event
|
|
fake_event_clear_last();
|
|
fake_event_reset_count();
|
|
}
|
|
|
|
static void prv_simulate_and_assert_discovery_of_one_service(const BTDeviceInternal *device) {
|
|
// Simulate discovery of Blood Pressure service:
|
|
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);
|
|
prv_assert_event(device, BTErrnoOK);
|
|
}
|
|
|
|
// Tests
|
|
///////////////////////////////////////////////////////////
|
|
|
|
void test_gatt_client_discovery__initialize(void) {
|
|
fake_gatt_init();
|
|
fake_event_init();
|
|
gap_le_connection_init();
|
|
}
|
|
|
|
void test_gatt_client_discovery__cleanup(void) {
|
|
gap_le_connection_deinit();
|
|
|
|
// make sure we haven't leaked any memory!
|
|
fake_pbl_malloc_check_net_allocs();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Edge cases
|
|
|
|
void test_gatt_client_discovery__not_connected(void) {
|
|
BTDeviceInternal device = prv_dummy_device(1);
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device),
|
|
BTErrnoInvalidParameter);
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), false);
|
|
}
|
|
|
|
void test_gatt_client_discovery__already_in_progress(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
|
|
// Start discovery for device:
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), true);
|
|
|
|
// Start again (expect to fail):
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device),
|
|
BTErrnoInvalidState);
|
|
|
|
// take down the connection and a disconnection event should be emitted
|
|
gap_le_connection_remove(&device);
|
|
prv_assert_event(&device, BTErrnoServiceDiscoveryDisconnected);
|
|
}
|
|
|
|
void test_gatt_client_discovery__event_is_sent_when_already_discovered(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
|
|
// Start discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), true);
|
|
|
|
// Simulate discovery of 1 service:
|
|
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);
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), false);
|
|
prv_assert_event(&device, BTErrnoOK);
|
|
|
|
fake_event_clear_last();
|
|
|
|
// Start discovery again, expect not to run (already discovered):
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), false);
|
|
|
|
// Expect event:
|
|
prv_assert_event(&device, BTErrnoOK);
|
|
}
|
|
|
|
void test_gatt_client_discovery__disconnected_during_discovery(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
|
|
// Start discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
// Simulate disconnection:
|
|
gap_le_connection_remove(&device);
|
|
// Process racing discovery indication:
|
|
fake_gatt_put_discovery_indication_blood_pressure_service(TEST_GATT_CONNECTION_ID);
|
|
// Bluetopia's GATT module does *NOT* emit a service discovery completion event for disconnections
|
|
|
|
// Test that our API *does* emit an service discovery event with "disconnected" reason:
|
|
prv_assert_event(&device, BTErrnoServiceDiscoveryDisconnected);
|
|
}
|
|
|
|
void test_gatt_client_discovery__complete_error(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
|
|
// Start discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
// Simulate getting one service indication...
|
|
fake_gatt_put_discovery_indication_blood_pressure_service(TEST_GATT_CONNECTION_ID);
|
|
// ... then a failure:
|
|
fake_gatt_put_discovery_complete_event(GATT_SERVICE_DISCOVERY_STATUS_RESPONSE_TIMEOUT,
|
|
TEST_GATT_CONNECTION_ID);
|
|
// Expect event with error status and 0 services:
|
|
prv_assert_event(&device,
|
|
BTErrnoWithBluetopiaError(GATT_SERVICE_DISCOVERY_STATUS_RESPONSE_TIMEOUT));
|
|
// Expect service discovery to be stopped:
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), false);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// Watchdog timeout tests
|
|
|
|
static void prv_fire_watchdog_timeouts(const BTDeviceInternal *device, int retries) {
|
|
for (int i = 0; i <= retries; ++i) {
|
|
const int start_count = fake_gatt_is_service_discovery_start_count();
|
|
const int stop_count = fake_gatt_is_service_discovery_stop_count();
|
|
|
|
// Fire the watchdog timer:
|
|
const TimerID watchdog_timer = bt_driver_gatt_get_watchdog_timer_id();
|
|
stub_new_timer_fire(watchdog_timer);
|
|
|
|
// Check whether GATT_Stop_Service_Discovery has been called:
|
|
cl_assert_equal_i(stop_count + 1, fake_gatt_is_service_discovery_stop_count());
|
|
|
|
if (i < GATT_CLIENT_DISCOVERY_MAX_RETRY) {
|
|
// Check whether GATT_Start_Service_Discovery has been called, except for the last iteration:
|
|
cl_assert_equal_i(start_count + 1, fake_gatt_is_service_discovery_start_count());
|
|
// No client event:
|
|
prv_assert_no_event();
|
|
} else {
|
|
// Last iteration: expect event with error status and 0 services:
|
|
prv_assert_event(device, BTErrnoServiceDiscoveryTimeout);
|
|
}
|
|
}
|
|
}
|
|
|
|
void test_gatt_client_discovery__watchdog_error_out_after_max_retries(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
|
|
// Start discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
prv_fire_watchdog_timeouts(&device, GATT_CLIENT_DISCOVERY_MAX_RETRY);
|
|
}
|
|
|
|
void test_gatt_client_discovery__watchdog_retry_counter_not_affecting_successive_process(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
|
|
// Start discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
prv_fire_watchdog_timeouts(&device, GATT_CLIENT_DISCOVERY_MAX_RETRY);
|
|
|
|
// Make sure the previous retry counter doesn't affect any new discovery process:
|
|
fake_event_clear_last();
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
|
|
// Fire watchdog one less time than the maximum:
|
|
prv_fire_watchdog_timeouts(&device, GATT_CLIENT_DISCOVERY_MAX_RETRY - 1);
|
|
|
|
// Finally make the Bluetopia discovery events come in:
|
|
prv_simulate_and_assert_discovery_of_one_service(&device);
|
|
}
|
|
|
|
void test_gatt_client_discovery__watchdog_race_with_stopping(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
|
|
// Start discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
|
|
// Make Bluetopia's GATT_Stop_Service_Discovery fail:
|
|
// (Service discovery has finished in the mean time, disconnected, ...)
|
|
fake_gatt_set_stop_return_value(BTGATT_ERROR_INVALID_PARAMETER);
|
|
|
|
// Fire the watchdog timer:
|
|
const TimerID watchdog_timer = bt_driver_gatt_get_watchdog_timer_id();
|
|
stub_new_timer_fire(watchdog_timer);
|
|
|
|
// No event should be generated, because the finishing / disconnecting / ... should cause
|
|
// Bluetopia to call back to prv_handle_bluetopia_discovery_event() and therefore the normal
|
|
// path will be taken.
|
|
prv_assert_no_event();
|
|
|
|
// take down the connection and a disconnection event should be emitted
|
|
gap_le_connection_remove(&device);
|
|
prv_assert_event(&device, BTErrnoServiceDiscoveryDisconnected);
|
|
}
|
|
|
|
void test_gatt_client_discovery__watchdog_race_with_restarting(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
|
|
// Start discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
|
|
// Make Bluetopia's GATT_Start_Service_Discovery fail:
|
|
// (Disconnected in the mean time, ...)
|
|
fake_gatt_set_start_return_value(BTGATT_ERROR_INVALID_PARAMETER);
|
|
|
|
// Fire the watchdog timer:
|
|
const TimerID watchdog_timer = bt_driver_gatt_get_watchdog_timer_id();
|
|
stub_new_timer_fire(watchdog_timer);
|
|
|
|
// Stopping did not fail, but restarting did. In this case we need to generate an event that
|
|
// the discovery process failed. The error from GATT_Start_Service_Discovery is expected to be
|
|
// passed in the event.
|
|
prv_assert_event(&device, BTErrnoWithBluetopiaError(BTGATT_ERROR_INVALID_PARAMETER));
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// Re-discovery
|
|
|
|
extern BTErrno gatt_client_discovery_rediscover_all(const BTDeviceInternal *device);
|
|
|
|
void test_gatt_client_discovery__rediscover_not_already_running(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
|
|
// Start discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
prv_simulate_and_assert_discovery_of_one_service(&device);
|
|
|
|
GAPLEConnection *connection = gap_le_connection_by_gatt_id(TEST_GATT_CONNECTION_ID);
|
|
// Expect one service, Blood Pressure:
|
|
cl_assert_equal_i(list_count(&connection->gatt_remote_services->node), 1);
|
|
|
|
fake_event_clear_last();
|
|
|
|
// Re-discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_rediscover_all(&device), BTErrnoOK);
|
|
|
|
// Expect "Database Changed" event:
|
|
prv_assert_event(&device, BTErrnoServiceDiscoveryDatabaseChanged);
|
|
// Expect all services nodes to be cleaned up:
|
|
cl_assert_equal_i(list_count(&connection->gatt_remote_services->node), 0);
|
|
|
|
// Put one, expect one:
|
|
prv_simulate_and_assert_discovery_of_one_service(&device);
|
|
cl_assert_equal_i(list_count(&connection->gatt_remote_services->node), 1);
|
|
}
|
|
|
|
void test_gatt_client_discovery__rediscover_already_running(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
|
|
// Start discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
// Put one service, but do not finish...
|
|
fake_gatt_put_discovery_indication_blood_pressure_service(TEST_GATT_CONNECTION_ID);
|
|
prv_assert_no_event();
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), true);
|
|
const int stop_count_before_rediscovery = fake_gatt_is_service_discovery_stop_count();
|
|
|
|
// Re-discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_rediscover_all(&device), BTErrnoOK);
|
|
|
|
// Assert the previous process has been stopped:
|
|
cl_assert_equal_i(stop_count_before_rediscovery + 1, fake_gatt_is_service_discovery_stop_count());
|
|
|
|
// Expect "Database Changed" event:
|
|
prv_assert_event(&device, BTErrnoServiceDiscoveryDatabaseChanged);
|
|
|
|
// Put one, expect one:
|
|
prv_simulate_and_assert_discovery_of_one_service(&device);
|
|
}
|
|
|
|
extern void gatt_client_discovery_discover_range(GAPLEConnection *connection,
|
|
ATTHandleRange *hdl_range);
|
|
|
|
void test_gatt_client_discovery__multiple_jobs_pending(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
GAPLEConnection *connection = gap_le_connection_by_device(&device);
|
|
|
|
ATTHandleRange range = {
|
|
.start = 0x1,
|
|
.end = 0x3000
|
|
};
|
|
ATTHandleRange range_alt = {
|
|
.start = 0x3001,
|
|
.end = 0x4000
|
|
};
|
|
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), false);
|
|
|
|
// start a discovery job, pretend nothing is found
|
|
gatt_client_discovery_discover_range(connection, &range);
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), true);
|
|
// pend up another service discovery job
|
|
gatt_client_discovery_discover_range(connection, &range);
|
|
cl_assert_equal_i(1, fake_gatt_is_service_discovery_start_count());
|
|
|
|
fake_gatt_put_discovery_complete_event(GATT_SERVICE_DISCOVERY_STATUS_SUCCESS,
|
|
TEST_GATT_CONNECTION_ID);
|
|
// Nothing was found so we should just have a completion event
|
|
cl_assert_equal_i(1, fake_event_get_count());
|
|
|
|
prv_assert_event(&device, BTErrnoOK);
|
|
cl_assert_equal_i(2, fake_gatt_is_service_discovery_start_count());
|
|
|
|
// next job should be in progress
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), true);
|
|
// BP service was discovered
|
|
fake_gatt_put_discovery_indication_blood_pressure_service(TEST_GATT_CONNECTION_ID);
|
|
// start another job, should be pended
|
|
gatt_client_discovery_discover_range(connection, &range);
|
|
fake_gatt_put_discovery_complete_event(GATT_SERVICE_DISCOVERY_STATUS_SUCCESS,
|
|
TEST_GATT_CONNECTION_ID);
|
|
|
|
// Since we are restarting discovery over the same range and discovered one
|
|
// service two events should be generated, one about the discovery complete
|
|
// and one invalidating the just discovered service
|
|
cl_assert_equal_i(2, fake_event_get_count());
|
|
prv_assert_event(&device, BTErrnoOK);
|
|
cl_assert_equal_i(3, fake_gatt_is_service_discovery_start_count());
|
|
|
|
fake_gatt_put_discovery_indication_blood_pressure_service(TEST_GATT_CONNECTION_ID);
|
|
|
|
gatt_client_discovery_discover_range(connection, &range_alt);
|
|
|
|
// BP service should have been discovered
|
|
fake_gatt_put_discovery_complete_event(GATT_SERVICE_DISCOVERY_STATUS_SUCCESS,
|
|
TEST_GATT_CONNECTION_ID);
|
|
// Only one event should have been pended since we were not rediscovering the
|
|
// same handle range
|
|
cl_assert_equal_i(1, fake_event_get_count());
|
|
prv_assert_event(&device, BTErrnoOK);
|
|
|
|
// Nothing discovered for final query
|
|
fake_gatt_put_discovery_complete_event(GATT_SERVICE_DISCOVERY_STATUS_SUCCESS,
|
|
TEST_GATT_CONNECTION_ID);
|
|
prv_assert_event(&device, BTErrnoOK);
|
|
|
|
cl_assert_equal_i(4, fake_gatt_is_service_discovery_start_count());
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), false);
|
|
}
|
|
|
|
void test_gatt_client_discovery__partial_and_full_discovery_jobs_intermixed(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
GAPLEConnection *connection = gap_le_connection_by_device(&device);
|
|
|
|
// queue up a few jobs - note only one should be running at any time
|
|
ATTHandleRange range = {
|
|
.start = 0x1,
|
|
.end = 0x3000
|
|
};
|
|
for (int i = 0; i < 10; i++) {
|
|
gatt_client_discovery_discover_range(connection, &range);
|
|
}
|
|
|
|
// kick off a full discovery
|
|
cl_assert_equal_i(gatt_client_discovery_rediscover_all(&device), BTErrnoOK);
|
|
|
|
// Assert the previous process has been stopped:
|
|
cl_assert_equal_i(1, fake_gatt_is_service_discovery_stop_count());
|
|
|
|
// Expect "Database Changed" event:
|
|
prv_assert_event(&device, BTErrnoServiceDiscoveryDatabaseChanged);
|
|
|
|
prv_simulate_and_assert_discovery_of_one_service(&device);
|
|
|
|
// None of the batched up jobs should have run
|
|
cl_assert_equal_i(2, fake_gatt_is_service_discovery_start_count());
|
|
|
|
// nothing should still be running
|
|
cl_assert_equal_b(fake_gatt_is_service_discovery_running(), false);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// Test vectors
|
|
|
|
static void prv_assert_blood_pressure_service(const GATTService *service) {
|
|
const Service *bp_service = fake_gatt_get_blood_pressure_service();
|
|
|
|
const uint16_t service_handle = bp_service->handle;
|
|
cl_assert_equal_i(service->att_handle, service_handle);
|
|
cl_assert_equal_b(uuid_equal(&service->uuid, &bp_service->uuid), true);
|
|
cl_assert_equal_i(service->num_att_handles_included_services, bp_service->num_included_services);
|
|
cl_assert_equal_i(service->num_characteristics, bp_service->num_characteristics);
|
|
|
|
const GATTCharacteristic *characteristic_one = service->characteristics;
|
|
const Characteristic *expected_characteristic1 = &bp_service->characteristics[0];
|
|
cl_assert_equal_i(characteristic_one->att_handle_offset, expected_characteristic1->handle - service_handle);
|
|
cl_assert_equal_i(characteristic_one->num_descriptors, expected_characteristic1->num_descriptors);
|
|
cl_assert_equal_i(characteristic_one->descriptors[0].att_handle_offset,
|
|
expected_characteristic1->descriptors[0].handle - service_handle);
|
|
cl_assert_equal_i(characteristic_one->properties, expected_characteristic1->properties);
|
|
cl_assert_equal_b(uuid_equal(&characteristic_one->uuid, &expected_characteristic1->uuid), true);
|
|
|
|
// Second characteristic is tacked right after the first one:
|
|
const GATTCharacteristic *characteristic_two =
|
|
(const GATTCharacteristic *) &characteristic_one->descriptors[1];
|
|
const Characteristic *expected_characteristic2 = &bp_service->characteristics[1];
|
|
cl_assert_equal_i(characteristic_two->att_handle_offset, expected_characteristic2->handle - service_handle);
|
|
cl_assert_equal_i(characteristic_two->num_descriptors, expected_characteristic2->num_descriptors);
|
|
cl_assert_equal_i(characteristic_two->descriptors[0].att_handle_offset,
|
|
expected_characteristic2->descriptors[0].handle - service_handle);
|
|
cl_assert_equal_i(characteristic_two->properties, expected_characteristic2->properties);
|
|
cl_assert_equal_b(uuid_equal(&characteristic_two->uuid, &expected_characteristic2->uuid), true);
|
|
}
|
|
|
|
void test_gatt_client_discovery__single_blood_pressure_service(void) {
|
|
BTDeviceInternal device = prv_connected_dummy_device(1);
|
|
|
|
// Start discovery:
|
|
cl_assert_equal_i(gatt_client_discovery_discover_all(&device), BTErrnoOK);
|
|
|
|
prv_simulate_and_assert_discovery_of_one_service(&device);
|
|
|
|
GAPLEConnection *connection = gap_le_connection_by_gatt_id(TEST_GATT_CONNECTION_ID);
|
|
// Expect one service, Blood Pressure:
|
|
cl_assert_equal_i(list_count(&connection->gatt_remote_services->node), 1);
|
|
const GATTService *service = connection->gatt_remote_services->service;
|
|
prv_assert_blood_pressure_service(service);
|
|
}
|