From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
To: linux-bluetooth@vger.kernel.org
Subject: [PATCH BlueZ v2 8/8] unit/test-gatt: Add Read Multiple Variable Length tests
Date: Fri, 6 Nov 2026 13:33:50 -0500 [thread overview]
Message-ID: <20261106183350.3155282-9-luiz.dentz@gmail.com> (raw)
In-Reply-To: <20261106183350.3155282-1-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Add client tests, using EATT client features, for regular and truncated
responses and for cancelling the request from the callback, and a
server test for the response.
Assisted-by: OpenCode:claude-opus-5.5
---
unit/test-gatt.c | 169 +++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 165 insertions(+), 4 deletions(-)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index d032e7598f9b..267bcc61802e 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -119,6 +119,29 @@ struct context {
MTU_EXCHANGE_CLIENT_PDUS, \
READ_SERVER_FEAT_PDUS
+#define EATT_CLIENT_INIT_PDUS \
+ MTU_EXCHANGE_CLIENT_PDUS, \
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x3a, 0x2b), \
+ raw_pdu(0x09, 0x03, 0x03, 0x00, 0x01), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05, 0x00, 0x01, 0x18),\
+ raw_pdu(0x08, 0x04, 0x00, 0xff, 0xff, 0x3a, 0x2b), \
+ raw_pdu(0x01, 0x08, 0x04, 0x00, 0x0a), \
+ raw_pdu(0x10, 0x06, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x01, 0x10, 0x06, 0x00, 0x0a), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
+ raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x3a, \
+ 0x2b, 0x04, 0x00, 0x0a, 0x05, 0x00, 0x29,\
+ 0x2b), \
+ raw_pdu(0x08, 0x05, 0x00, 0x05, 0x00, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a), \
+ raw_pdu(0x12, 0x05, 0x00, 0x07), \
+ raw_pdu(0x13)
+
#define SERVICE_DATA_1_PDUS \
CLIENT_INIT_PDUS, \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
@@ -328,6 +351,7 @@ struct test_step {
uint8_t expected_att_ecode;
const uint8_t *value;
uint16_t length;
+ uint8_t features;
};
static void destroy_context(struct context *context)
@@ -624,7 +648,7 @@ static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data)
g_assert(success);
- if (!context->data->source_db) {
+ if (!context->data->source_db && !context->data->step) {
context_quit(context);
return;
}
@@ -632,8 +656,10 @@ static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data)
g_assert(context->client);
g_assert(context->client_db);
- gatt_db_foreach_service(context->client_db, NULL, match_services,
- context->data->source_db);
+ if (context->data->source_db)
+ gatt_db_foreach_service(context->client_db, NULL,
+ match_services,
+ context->data->source_db);
if (context->data->step) {
const struct test_step *step = context->data->step;
@@ -653,6 +679,7 @@ static struct context *create_context(uint16_t mtu, gconstpointer data)
{
struct context *context = g_new0(struct context, 1);
const struct test_data *test_data = data;
+ const struct test_step *step;
GIOChannel *channel;
int err, sv[2];
@@ -680,11 +707,14 @@ static struct context *create_context(uint16_t mtu, gconstpointer data)
"bt_gatt_server:", NULL);
break;
case CLIENT:
+ step = test_data->step;
+
context->client_db = gatt_db_new();
g_assert(context->client_db);
context->client = bt_gatt_client_new(context->client_db,
- context->att, mtu, 0);
+ context->att, mtu,
+ step ? step->features : 0);
g_assert(context->client);
bt_gatt_client_set_debug(context->client, print_debug,
@@ -2149,6 +2179,109 @@ static const struct test_step test_multiple_read_6 = {
.expected_att_ecode = 0x0c
};
+static void multiple_read_vl_cb(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct context *context = user_data;
+ const struct test_step *step = context->data->step;
+ static uint16_t offset;
+
+ g_assert(success);
+ g_assert_cmpint(offset + 2 + length, <=, step->length);
+ g_assert_cmpint(get_le16(step->value + offset), ==, length);
+ g_assert(memcmp(value, step->value + offset + 2, length) == 0);
+
+ offset += 2 + length;
+ if (offset == step->length) {
+ offset = 0;
+ context_quit(context);
+ }
+}
+
+static void test_multiple_read_vl(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+ uint16_t handles[2];
+
+ handles[0] = step->handle;
+ handles[1] = step->end_handle;
+
+ g_assert(bt_gatt_client_read_multiple(context->client, handles, 2,
+ multiple_read_vl_cb, context,
+ NULL));
+}
+
+static const uint8_t read_mult_vl_data_1[] = {
+ 0x02, 0x00, 0x01, 0x02,
+ 0x03, 0x00, 0x03, 0x04, 0x05
+};
+
+static const struct test_step test_multiple_read_vl_1 = {
+ .handle = 0x0003,
+ .end_handle = 0x0005,
+ .func = test_multiple_read_vl,
+ .value = read_mult_vl_data_1,
+ .length = sizeof(read_mult_vl_data_1),
+ .features = BT_GATT_CHRC_CLI_FEAT_EATT,
+};
+
+static const uint8_t read_mult_vl_data_2[] = {
+ 0x02, 0x00, 0x01, 0x02,
+ 0x01, 0x00, 0x03
+};
+
+static const struct test_step test_multiple_read_vl_2 = {
+ .handle = 0x0003,
+ .end_handle = 0x0005,
+ .func = test_multiple_read_vl,
+ .value = read_mult_vl_data_2,
+ .length = sizeof(read_mult_vl_data_2),
+ .features = BT_GATT_CHRC_CLI_FEAT_EATT,
+};
+
+static unsigned int multiple_read_vl_id;
+
+static void multiple_read_vl_cancel_cb(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct context *context = user_data;
+ const struct test_step *step = context->data->step;
+
+ g_assert(success);
+ g_assert_cmpint(get_le16(step->value), ==, length);
+ g_assert(memcmp(value, step->value + 2, length) == 0);
+
+ g_assert(bt_gatt_client_cancel(context->client, multiple_read_vl_id));
+ multiple_read_vl_id = 0;
+
+ context_quit(context);
+}
+
+static void test_multiple_read_vl_cancel(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+ uint16_t handles[2];
+
+ handles[0] = step->handle;
+ handles[1] = step->end_handle;
+
+ multiple_read_vl_id = bt_gatt_client_read_multiple(context->client,
+ handles, 2, multiple_read_vl_cancel_cb,
+ context, NULL);
+ g_assert(multiple_read_vl_id);
+}
+
+static const struct test_step test_multiple_read_vl_3 = {
+ .handle = 0x0003,
+ .end_handle = 0x0005,
+ .func = test_multiple_read_vl_cancel,
+ .value = read_mult_vl_data_1,
+ .length = sizeof(read_mult_vl_data_1),
+ .features = BT_GATT_CHRC_CLI_FEAT_EATT,
+};
+
static void read_by_type_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
@@ -3378,6 +3511,27 @@ int main(int argc, char *argv[])
raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
raw_pdu(0x0f, 0x01, 0x02, 0x03));
+ define_test_client("/robustness/read-multiple-vl", test_client,
+ NULL, &test_multiple_read_vl_1,
+ EATT_CLIENT_INIT_PDUS,
+ raw_pdu(0x20, 0x03, 0x00, 0x05, 0x00),
+ raw_pdu(0x21, 0x02, 0x00, 0x01, 0x02, 0x03, 0x00,
+ 0x03, 0x04, 0x05));
+
+ define_test_client("/robustness/read-multiple-vl-truncated",
+ test_client, NULL, &test_multiple_read_vl_2,
+ EATT_CLIENT_INIT_PDUS,
+ raw_pdu(0x20, 0x03, 0x00, 0x05, 0x00),
+ raw_pdu(0x21, 0x02, 0x00, 0x01, 0x02, 0x03, 0x00,
+ 0x03));
+
+ define_test_client("/robustness/read-multiple-vl-cancel",
+ test_client, NULL, &test_multiple_read_vl_3,
+ EATT_CLIENT_INIT_PDUS,
+ raw_pdu(0x20, 0x03, 0x00, 0x05, 0x00),
+ raw_pdu(0x21, 0x02, 0x00, 0x01, 0x02, 0x03, 0x00,
+ 0x03, 0x04, 0x05));
+
define_test_client("/TP/GAR/CL/BI-22-C", test_client, service_db_1,
&test_multiple_read_6,
SERVICE_DATA_1_PDUS,
@@ -3390,6 +3544,13 @@ int main(int argc, char *argv[])
raw_pdu(0x0e, 0x15, 0xF0, 0x03, 0x00),
raw_pdu(0x0f, 0x09, 'B', 'l', 'u', 'e', 'Z'));
+ define_test_server("/robustness/read-multiple-vl/server",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x20, 0x15, 0xF0, 0x03, 0x00),
+ raw_pdu(0x21, 0x01, 0x00, 0x09, 0x05, 0x00, 'B', 'l',
+ 'u', 'e', 'Z'));
+
define_test_server("/TP/GAR/SR/BV-05-C/large-1", test_server,
ts_large_db_1, NULL,
raw_pdu(0x03, 0x00, 0x02),
--
2.56.0
next prev parent reply other threads:[~2026-11-06 18:33 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 18:33 [PATCH BlueZ v2 0/8] Fix handling of malformed GATT PDUs Luiz Augusto von Dentz
2026-11-06 18:33 ` [PATCH BlueZ v2 1/8] shared/gatt: Validate Read By Type responses Luiz Augusto von Dentz
2026-11-06 23:18 ` Fix handling of malformed GATT PDUs bluez.test.bot
2026-11-06 18:33 ` [PATCH BlueZ v2 2/8] unit/test-gatt: Add malformed Read By Type response tests Luiz Augusto von Dentz
2026-11-06 18:33 ` [PATCH BlueZ v2 3/8] shared/gatt-client: Fix handling of short notifications Luiz Augusto von Dentz
2026-11-06 18:33 ` [PATCH BlueZ v2 4/8] unit/test-gatt: Add short notification test Luiz Augusto von Dentz
2026-11-06 18:33 ` [PATCH BlueZ v2 5/8] shared/att: Add Read Multiple Variable Length opcodes Luiz Augusto von Dentz
2026-11-06 18:33 ` [PATCH BlueZ v2 6/8] shared/gatt-client: Fix parsing Read Multiple Variable Length response Luiz Augusto von Dentz
2026-11-06 18:33 ` [PATCH BlueZ v2 7/8] shared/gatt-client: Check value lengths before reading them Luiz Augusto von Dentz
2026-11-06 18:33 ` Luiz Augusto von Dentz [this message]
2026-11-07 21:00 ` [PATCH BlueZ v2 0/8] Fix handling of malformed GATT PDUs patchwork-bot+bluetooth
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261106183350.3155282-9-luiz.dentz@gmail.com \
--to=luiz.dentz@gmail.com \
--cc=linux-bluetooth@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox