pebble/tests/fw/applib/bluetooth/test_ble_ad_parse.c
2025-01-27 11:38:16 -08:00

333 lines
11 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 "applib/bluetooth/ble_ad_parse.h"
#include "system/hexdump.h"
#include "clar.h"
#include <btutil/bt_uuid.h>
// Stubs
///////////////////////////////////////////////////////////
#include "stubs_ble_syscalls.h"
#include "stubs_ble_syscalls.h"
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_pebble_tasks.h"
#include "stubs_print.h"
#include "stubs_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_serial.h"
// The test data and descriptions in this file are captured using the FrontLine
// Bluetooth sniffer.
static const size_t s_buffer_size = sizeof(BLEAdData) +
(2 * GAP_LE_AD_REPORT_DATA_MAX_LENGTH);
static uint8_t s_buffer[s_buffer_size];
static BLEAdData * const s_ad_data = (BLEAdData *)s_buffer;
static void set_ad_data(uint8_t *data, size_t length) {
memcpy(s_ad_data->data, data, length);
s_ad_data->ad_data_length = length;
}
void test_ble_ad_parse__initialize(void) {
memset(s_ad_data, 0, sizeof(s_buffer_size));
}
// -----------------------------------------------------------------------------
// Consuming BLEAdData:
// -----------------------------------------------------------------------------
void test_ble_ad_parse__16_bit_uuid_and_device_name(void) {
// AD Element, Length: 2, AD Type: Flags (0x1a)
// AD Element, Length: 3, AD Type: Complete list of 16-bit UUID, [0x7b29]
// AD Element, Length: 10, AD Type: Complete local name, Text: LightBlue
uint8_t data[] =
"\x02\x01\x1a\x03\x03\x29\x7b\x0a\x09\x4c\x69\x67\x68\x74\x42\x6c\x75\x65";
set_ad_data(data, sizeof(data));
// Test ble_ad_get_raw_data_size:
cl_assert_equal_i(ble_ad_get_raw_data_size(s_ad_data), sizeof(data));
// Test ble_ad_copy_raw_data:
uint8_t buffer[GAP_LE_AD_REPORT_DATA_MAX_LENGTH * 2];
size_t size = ble_ad_copy_raw_data(s_ad_data, buffer, sizeof(buffer));
cl_assert_equal_i(size, sizeof(data));
cl_assert_equal_i(memcmp(buffer, data, sizeof(data)), 0);
// Test ble_ad_copy_local_name, destination buffer large enough:
char local_name[64];
size = ble_ad_copy_local_name(s_ad_data, local_name, 64);
cl_assert_equal_s(local_name, "LightBlue");
cl_assert_equal_i(size, strlen("LightBlue") + 1);
// Test ble_ad_copy_local_name, destination buffer too small:
size = ble_ad_copy_local_name(s_ad_data, local_name, 6);
cl_assert_equal_s(local_name, "Light");
cl_assert_equal_i(size, strlen("Light") + 1);
// Test ble_ad_includes_service:
Uuid included_uuid = bt_uuid_expand_16bit(0x7b29);
cl_assert(ble_ad_includes_service(s_ad_data, &included_uuid));
Uuid missing_uuid = bt_uuid_expand_16bit(0xabcd);
cl_assert(!ble_ad_includes_service(s_ad_data, &missing_uuid));
// Test ble_ad_copy_service_uuids, destination array sized larged enough:
const uint8_t count = 4;
Uuid copied_uuids[count];
uint8_t found = ble_ad_copy_service_uuids(s_ad_data, copied_uuids, count);
cl_assert_equal_i(found, 1);
// Test ble_ad_copy_service_uuids, destination array too small:
found = ble_ad_copy_service_uuids(s_ad_data, copied_uuids, 0);
cl_assert_equal_i(found, 1);
// Test ble_ad_get_tx_power_level returns false, when no TX Power Level:
int8_t tx_power_level_out;
cl_assert(!ble_ad_get_tx_power_level(s_ad_data, &tx_power_level_out));
}
void test_ble_ad_parse__128_bit_uuid(void) {
// AD Element, Length: 2, AD Type: Flags
// AD Element, Length: 17, AD Type: More 128-bit UUIDs available,
// Value: 0x68753a444d6f12269c600050e4c00067
uint8_t data[GAP_LE_AD_REPORT_DATA_MAX_LENGTH] =
"\x02\x01\x1a\x11\x06\x67\x00\xc0\xe4\x50\x00\x60\x9c\x26\x12\x6f\x4d\x44" \
"\x3a\x75\x68";
set_ad_data(data, sizeof(data));
// Test ble_ad_includes_service:
uint8_t uuid_bytes[] = "\x68\x75\x3a\x44\x4d\x6f\x12\x26\x9c\x60\x00\x50" \
"\xe4\xc0\x00\x67";
Uuid included_uuid = UuidMakeFromBEBytes(uuid_bytes);
cl_assert(ble_ad_includes_service(s_ad_data, &included_uuid));
Uuid missing_uuid = bt_uuid_expand_16bit(0xabcd);
cl_assert(!ble_ad_includes_service(s_ad_data, &missing_uuid));
}
// -----------------------------------------------------------------------------
// Creating BLEAdData:
// -----------------------------------------------------------------------------
void test_ble_ad_parse__ad_and_scan_resp_boundaries(void) {
}
void test_ble_ad_parse__start_scan_response(void) {
BLEAdData *ad = ble_ad_create();
ble_ad_start_scan_response(ad);
uint8_t expected_scan_resp_data[] = {
1 /* +1 for Type byte */ + strlen("Pebble 1234"),
0x09, // Local Name, Complete
'P', 'e', 'b', 'b', 'l', 'e', ' ', '1', '2', '3', '4'
};
// Should fit fine, expect true:
cl_assert_equal_b(ble_ad_set_local_name(ad, "Pebble 1234"), true);
// Expect no advertisement data:
cl_assert_equal_i(ad->ad_data_length, 0);
// Expect scan response data:
cl_assert_equal_i(ad->scan_resp_data_length, sizeof(expected_scan_resp_data));
// Compare scan response data:
cl_assert_equal_i(memcmp(expected_scan_resp_data,
ad->data + ad->ad_data_length,
ad->scan_resp_data_length), 0);
ble_ad_destroy(ad);
}
void test_ble_ad_parse__set_service_uuids_128_bit(void) {
BLEAdData *ad = ble_ad_create();
uint8_t uuid_bytes[] = "\x97\x6e\xbb\x18\xd3\xe9\x43\xc0\x8a\x63\x8d\x2b" \
"\x60\xd9\x04\x2a";
Uuid uuid[2];
uuid[0] = UuidMakeFromBEBytes(uuid_bytes);
uuid[1] = UuidMakeFromLEBytes(uuid_bytes); // just reversed, laziness
// 2x 128-bit UUIDs is not going to fit, expect false:
cl_assert_equal_b(ble_ad_set_service_uuids(ad, uuid, 2), false);
// Hand-construct expected raw advertisement data:
uint8_t expected_ad_data[sizeof(Uuid) + 2 /* +1 Length, +1 Type bytes */] = {
sizeof(Uuid) + 1 /* +1 for Type byte */,
0x07, // Service UUIDs, 128-bit, Complete
};
memcpy(&expected_ad_data[2], uuid_bytes, sizeof(Uuid));
// One should fit though:
cl_assert_equal_b(ble_ad_set_service_uuids(ad, uuid, 1), true);
cl_assert_equal_i(memcmp(expected_ad_data, ad->data,
sizeof(expected_ad_data)), 0);
cl_assert_equal_i(ad->ad_data_length, sizeof(expected_ad_data));
cl_assert_equal_i(ad->scan_resp_data_length, 0);
ble_ad_destroy(ad);
}
void test_ble_ad_parse__set_service_uuids_32_bit(void) {
BLEAdData *ad;
Uuid uuid[8];
for (int i = 0; i < 8; ++i) {
uuid[i] = bt_uuid_expand_32bit(0x12346700 + i);
}
// Hand-construct expected raw advertisement data:
uint8_t expected_ad_data[] = {
(2 * sizeof(uint32_t)) + 1 /* +1 for Type byte */,
0x05, // Service UUIDs, 32-bit, Complete
0x00, 0x67, 0x34, 0x12, // Little endian
0x01, 0x67, 0x34, 0x12,
};
// 2x 32-bit UUIDs should fit fine, expect true:
ad = ble_ad_create();
cl_assert_equal_b(ble_ad_set_service_uuids(ad, uuid, 2), true);
cl_assert_equal_i(memcmp(expected_ad_data, ad->data,
sizeof(expected_ad_data)), 0);
cl_assert_equal_i(ad->ad_data_length, sizeof(expected_ad_data));
cl_assert_equal_i(ad->scan_resp_data_length, 0);
ble_ad_destroy(ad);
// 7x 32-bit UUIDs should fit, expect true:
ad = ble_ad_create();
cl_assert_equal_b(ble_ad_set_service_uuids(ad, uuid, 7), true);
ble_ad_destroy(ad);
// 8x 32-bit UUIDs does not fit, expect false:
ad = ble_ad_create();
cl_assert_equal_b(ble_ad_set_service_uuids(ad, uuid, 8), false);
ble_ad_destroy(ad);
}
void test_ble_ad_parse__set_service_uuids_16_bit(void) {
BLEAdData *ad;
Uuid uuid[15];
for (int i = 0; i < 15; ++i) {
uuid[i] = bt_uuid_expand_16bit(0x1800 + i);
}
// Hand-construct expected raw advertisement data:
uint8_t expected_ad_data[] = {
(2 * sizeof(uint16_t)) + 1 /* +1 for Type byte */,
0x03, // Service UUIDs, 16-bit, Complete
0x00, 0x18, // Little endian
0x01, 0x18,
};
// 2x 16-bit UUIDs should fit fine, expect true:
ad = ble_ad_create();
cl_assert_equal_b(ble_ad_set_service_uuids(ad, uuid, 2), true);
cl_assert_equal_i(memcmp(expected_ad_data, ad->data,
sizeof(expected_ad_data)), 0);
cl_assert_equal_i(ad->ad_data_length, sizeof(expected_ad_data));
cl_assert_equal_i(ad->scan_resp_data_length, 0);
ble_ad_destroy(ad);
// 14x 16-bit UUIDs should fit, expect true:
ad = ble_ad_create();
cl_assert_equal_b(ble_ad_set_service_uuids(ad, uuid, 14), true);
ble_ad_destroy(ad);
// 15x 16-bit UUIDs does not fit, expect false:
ad = ble_ad_create();
cl_assert_equal_b(ble_ad_set_service_uuids(ad, uuid, 15), false);
ble_ad_destroy(ad);
}
void test_ble_ad_parse__set_local_name(void) {
BLEAdData *ad;
ad = ble_ad_create();
uint8_t expected_ad_data[] = {
1 /* +1 for Type byte */ + strlen("Pebble 1234"),
0x09, // Local Name, Complete
'P', 'e', 'b', 'b', 'l', 'e', ' ', '1', '2', '3', '4'
};
// Should fit fine, expect true:
cl_assert_equal_b(ble_ad_set_local_name(ad, "Pebble 1234"), true);
cl_assert_equal_i(memcmp(expected_ad_data, ad->data, ad->ad_data_length), 0);
ble_ad_destroy(ad);
}
void test_ble_ad_parse__set_tx_power_level(void) {
BLEAdData *ad;
ad = ble_ad_create();
uint8_t expected_ad_data[] = {
1 /* +1 for Type byte */ + 1 /* int8_t with value */,
0x0a, // TX Power Level
-55,
};
// Should fit fine, expect true:
cl_assert_equal_b(ble_ad_set_tx_power_level(ad), true);
cl_assert_equal_i(memcmp(expected_ad_data, ad->data, ad->ad_data_length), 0);
ble_ad_destroy(ad);
}
void test_ble_ad_parse__set_manufacturer_specific_data(void) {
BLEAdData *ad;
ad = ble_ad_create();
uint8_t expected_ad_data[] = {
1 /* +1 for Type byte */ + 13 /* Company ID + data */,
0xff, // Manufacturer Specific data
0x34, 0x12,
'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd',
};
// Should fit fine, expect true:
cl_assert_equal_b(ble_ad_set_manufacturer_specific_data(ad,
0x1234,
(uint8_t *) "hello world",
11), true);
cl_assert_equal_i(memcmp(expected_ad_data, ad->data, ad->ad_data_length), 0);
ble_ad_destroy(ad);
}
void test_ble_ad_parse__set_flags(void) {
BLEAdData *ad;
ad = ble_ad_create();
const uint8_t flags = 0x03;
const uint8_t expected_ad_data[] = {
1 /* +1 for Type byte */ + 1 /* uint8_t with value */,
0x01, // Flags type
flags,
};
// Should fit fine, expect true:
cl_assert_equal_b(ble_ad_set_flags(ad, flags), true);
cl_assert_equal_i(memcmp(expected_ad_data, ad->data, ad->ad_data_length), 0);
ble_ad_destroy(ad);
}