http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d98ddc1c/net/nimble/host/test/src/ble_gatt_read_test.c ---------------------------------------------------------------------- diff --git a/net/nimble/host/test/src/ble_gatt_read_test.c b/net/nimble/host/test/src/ble_gatt_read_test.c new file mode 100644 index 0000000..822de5c --- /dev/null +++ b/net/nimble/host/test/src/ble_gatt_read_test.c @@ -0,0 +1,823 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <string.h> +#include <errno.h> +#include <limits.h> +#include "testutil/testutil.h" +#include "nimble/ble.h" +#include "host/ble_hs_test.h" +#include "host/ble_uuid.h" +#include "ble_hs_test_util.h" + +struct ble_gatt_read_test_attr { + uint16_t conn_handle; + uint16_t handle; + uint8_t value_len; + uint8_t value[BLE_ATT_ATTR_MAX_LEN]; +}; + +#define BLE_GATT_READ_TEST_MAX_ATTRS 256 + +struct ble_gatt_read_test_attr + ble_gatt_read_test_attrs[BLE_GATT_READ_TEST_MAX_ATTRS]; +int ble_gatt_read_test_num_attrs; +int ble_gatt_read_test_complete; + +uint16_t ble_gatt_read_test_bad_conn_handle; +int ble_gatt_read_test_bad_status; + +static void +ble_gatt_read_test_misc_init(void) +{ + ble_hs_test_util_init(); + ble_gatt_read_test_num_attrs = 0; + ble_gatt_read_test_complete = 0; + ble_gatt_read_test_bad_conn_handle = 0; + ble_gatt_read_test_bad_status = 0; + + memset(&ble_gatt_read_test_attrs[0], 0, + sizeof ble_gatt_read_test_attrs[0]); +} + +static int +ble_gatt_read_test_cb(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + struct ble_gatt_read_test_attr *dst; + int *stop_after; + + stop_after = arg; + + TEST_ASSERT_FATAL(error != NULL); + + if (error->status != 0) { + ble_gatt_read_test_bad_conn_handle = conn_handle; + ble_gatt_read_test_bad_status = error->status; + ble_gatt_read_test_complete = 1; + return 0; + } + + if (attr == NULL) { + ble_gatt_read_test_complete = 1; + return 0; + } + + TEST_ASSERT_FATAL(ble_gatt_read_test_num_attrs < + BLE_GATT_READ_TEST_MAX_ATTRS); + dst = ble_gatt_read_test_attrs + ble_gatt_read_test_num_attrs++; + + TEST_ASSERT_FATAL(OS_MBUF_PKTLEN(attr->om) <= sizeof dst->value); + + dst->conn_handle = conn_handle; + dst->handle = attr->handle; + dst->value_len = OS_MBUF_PKTLEN(attr->om); + os_mbuf_copydata(attr->om, 0, OS_MBUF_PKTLEN(attr->om), dst->value); + + if (stop_after != NULL && *stop_after > 0) { + (*stop_after)--; + if (*stop_after == 0) { + ble_gatt_read_test_complete = 1; + return 1; + } + } else { + ble_gatt_read_test_complete = 1; + } + + return 0; +} + +static int +ble_gatt_read_test_long_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + struct ble_gatt_read_test_attr *dst; + int *reads_left; + + reads_left = arg; + + TEST_ASSERT_FATAL(error != NULL); + + if (error->status != 0) { + ble_gatt_read_test_bad_conn_handle = conn_handle; + ble_gatt_read_test_bad_status = error->status; + ble_gatt_read_test_complete = 1; + return 0; + } + + if (attr == NULL) { + ble_gatt_read_test_complete = 1; + return 0; + } + + dst = ble_gatt_read_test_attrs + 0; + + TEST_ASSERT_FATAL(OS_MBUF_PKTLEN(attr->om) <= + dst->value_len + sizeof dst->value); + TEST_ASSERT(attr->offset == dst->value_len); + + if (attr->offset == 0) { + dst->conn_handle = conn_handle; + dst->handle = attr->handle; + } else { + TEST_ASSERT(conn_handle == dst->conn_handle); + TEST_ASSERT(attr->handle == dst->handle); + } + os_mbuf_copydata(attr->om, 0, OS_MBUF_PKTLEN(attr->om), + dst->value + dst->value_len); + dst->value_len += OS_MBUF_PKTLEN(attr->om); + + if (reads_left != NULL && *reads_left > 0) { + (*reads_left)--; + if (*reads_left == 0) { + ble_gatt_read_test_complete = 1; + return 1; + } + } + + return 0; +} + +static void +ble_gatt_read_test_misc_rx_rsp_good_raw(uint16_t conn_handle, + uint8_t att_op, + const void *data, int data_len) +{ + uint8_t buf[1024]; + int rc; + + TEST_ASSERT_FATAL(data_len <= sizeof buf); + + /* Send the pending ATT Read Request. */ + ble_hs_test_util_tx_all(); + + buf[0] = att_op; + memcpy(buf + 1, data, data_len); + + rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, + buf, 1 + data_len); + TEST_ASSERT(rc == 0); +} + +static void +ble_gatt_read_test_misc_rx_rsp_good(uint16_t conn_handle, + struct ble_hs_test_util_flat_attr *attr) +{ + ble_gatt_read_test_misc_rx_rsp_good_raw(conn_handle, BLE_ATT_OP_READ_RSP, + attr->value, + attr->value_len); +} + +static void +ble_gatt_read_test_misc_rx_rsp_bad(uint16_t conn_handle, + uint8_t att_error, uint16_t err_handle) +{ + /* Send the pending ATT Read Request. */ + ble_hs_test_util_tx_all(); + + ble_hs_test_util_rx_att_err_rsp(conn_handle, BLE_ATT_OP_READ_REQ, + att_error, err_handle); +} + +static int +ble_gatt_read_test_misc_uuid_rx_rsp_good( + uint16_t conn_handle, struct ble_hs_test_util_flat_attr *attrs) +{ + struct ble_att_read_type_rsp rsp; + uint8_t buf[1024]; + int prev_len; + int off; + int rc; + int i; + + if (ble_gatt_read_test_complete || attrs[0].handle == 0) { + return 0; + } + + /* Send the pending ATT Read By Type Request. */ + ble_hs_test_util_tx_all(); + + rsp.batp_length = 2 + attrs[0].value_len; + ble_att_read_type_rsp_write(buf, sizeof buf, &rsp); + + prev_len = 0; + off = BLE_ATT_READ_TYPE_RSP_BASE_SZ; + for (i = 0; attrs[i].handle != 0; i++) { + if (prev_len != 0 && prev_len != attrs[i].value_len) { + break; + } + prev_len = attrs[i].value_len; + + htole16(buf + off, attrs[i].handle); + off += 2; + + memcpy(buf + off, attrs[i].value, attrs[i].value_len); + off += attrs[i].value_len; + } + + rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, + buf, off); + TEST_ASSERT(rc == 0); + + return i; +} + +static void +ble_gatt_read_test_misc_verify_good(struct ble_hs_test_util_flat_attr *attr) +{ + int rc; + + ble_gatt_read_test_misc_init(); + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + rc = ble_gattc_read(2, attr->handle, ble_gatt_read_test_cb, NULL); + TEST_ASSERT_FATAL(rc == 0); + + ble_gatt_read_test_misc_rx_rsp_good(2, attr); + + TEST_ASSERT(ble_gatt_read_test_num_attrs == 1); + TEST_ASSERT(ble_gatt_read_test_attrs[0].conn_handle == 2); + TEST_ASSERT(ble_gatt_read_test_attrs[0].handle == attr->handle); + TEST_ASSERT(ble_gatt_read_test_attrs[0].value_len == attr->value_len); + TEST_ASSERT(memcmp(ble_gatt_read_test_attrs[0].value, attr->value, + attr->value_len) == 0); +} + +static void +ble_gatt_read_test_misc_verify_bad(uint8_t att_status, + struct ble_hs_test_util_flat_attr *attr) +{ + int rc; + + ble_gatt_read_test_misc_init(); + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + rc = ble_gattc_read(2, attr->handle, ble_gatt_read_test_cb, NULL); + TEST_ASSERT_FATAL(rc == 0); + + ble_gatt_read_test_misc_rx_rsp_bad(2, att_status, attr->handle); + + TEST_ASSERT(ble_gatt_read_test_num_attrs == 0); + TEST_ASSERT(ble_gatt_read_test_bad_conn_handle == 2); + TEST_ASSERT(ble_gatt_read_test_bad_status == + BLE_HS_ERR_ATT_BASE + att_status); + TEST_ASSERT(!ble_gattc_any_jobs()); +} + +static void +ble_gatt_read_test_misc_uuid_verify_good( + uint16_t start_handle, uint16_t end_handle, void *uuid128, + int stop_after, struct ble_hs_test_util_flat_attr *attrs) +{ + int num_read; + int idx; + int rc; + int i; + + ble_gatt_read_test_misc_init(); + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + rc = ble_gattc_read_by_uuid(2, start_handle, end_handle, uuid128, + ble_gatt_read_test_cb, &stop_after); + TEST_ASSERT_FATAL(rc == 0); + + idx = 0; + while (1) { + num_read = ble_gatt_read_test_misc_uuid_rx_rsp_good(2, attrs + idx); + if (num_read == 0) { + ble_hs_test_util_tx_all(); + ble_hs_test_util_rx_att_err_rsp(2, BLE_ATT_OP_READ_TYPE_REQ, + BLE_ATT_ERR_ATTR_NOT_FOUND, + start_handle); + break; + } + + idx += num_read; + } + + TEST_ASSERT(ble_gatt_read_test_complete); + TEST_ASSERT(idx == ble_gatt_read_test_num_attrs); + + for (i = 0; i < idx; i++) { + TEST_ASSERT(ble_gatt_read_test_attrs[i].conn_handle == 2); + TEST_ASSERT(ble_gatt_read_test_attrs[i].handle == attrs[i].handle); + TEST_ASSERT(ble_gatt_read_test_attrs[i].value_len == + attrs[i].value_len); + TEST_ASSERT(memcmp(ble_gatt_read_test_attrs[i].value, attrs[i].value, + attrs[i].value_len) == 0); + } + TEST_ASSERT(!ble_gattc_any_jobs()); +} + +static void +ble_gatt_read_test_misc_long_verify_good( + int max_reads, struct ble_hs_test_util_flat_attr *attr) +{ + int reads_left; + int chunk_sz; + int rem_len; + int att_op; + int off; + int rc; + + ble_gatt_read_test_misc_init(); + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + if (max_reads == 0) { + max_reads = INT_MAX; + } + reads_left = max_reads; + rc = ble_gattc_read_long(2, attr->handle, ble_gatt_read_test_long_cb, + &reads_left); + TEST_ASSERT_FATAL(rc == 0); + + off = 0; + rem_len = attr->value_len; + do { + if (rem_len > BLE_ATT_MTU_DFLT - 1) { + chunk_sz = BLE_ATT_MTU_DFLT - 1; + } else { + chunk_sz = rem_len; + } + if (off == 0) { + att_op = BLE_ATT_OP_READ_RSP; + } else { + att_op = BLE_ATT_OP_READ_BLOB_RSP; + } + ble_gatt_read_test_misc_rx_rsp_good_raw(2, att_op, + attr->value + off, chunk_sz); + rem_len -= chunk_sz; + off += chunk_sz; + } while (rem_len > 0 && reads_left > 0); + + TEST_ASSERT(ble_gatt_read_test_complete); + TEST_ASSERT(!ble_gattc_any_jobs()); + TEST_ASSERT(ble_gatt_read_test_attrs[0].conn_handle == 2); + TEST_ASSERT(ble_gatt_read_test_attrs[0].handle == attr->handle); + if (reads_left > 0) { + TEST_ASSERT(ble_gatt_read_test_attrs[0].value_len == attr->value_len); + } + TEST_ASSERT(memcmp(ble_gatt_read_test_attrs[0].value, attr->value, + ble_gatt_read_test_attrs[0].value_len) == 0); +} + +static void +ble_gatt_read_test_misc_long_verify_bad( + uint8_t att_status, struct ble_hs_test_util_flat_attr *attr) +{ + int rc; + + ble_gatt_read_test_misc_init(); + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + rc = ble_gattc_read_long(2, attr->handle, + ble_gatt_read_test_long_cb, NULL); + TEST_ASSERT_FATAL(rc == 0); + + ble_gatt_read_test_misc_rx_rsp_bad(2, att_status, attr->handle); + + TEST_ASSERT(ble_gatt_read_test_num_attrs == 0); + TEST_ASSERT(ble_gatt_read_test_bad_conn_handle == 2); + TEST_ASSERT(ble_gatt_read_test_bad_status == + BLE_HS_ERR_ATT_BASE + att_status); + TEST_ASSERT(!ble_gattc_any_jobs()); +} + +static int +ble_gatt_read_test_misc_extract_handles( + struct ble_hs_test_util_flat_attr *attrs, uint16_t *handles) +{ + int i; + + for (i = 0; attrs[i].handle != 0; i++) { + handles[i] = attrs[i].handle; + } + return i; +} + +static void +ble_gatt_read_test_misc_mult_verify_good( + struct ble_hs_test_util_flat_attr *attrs) +{ + uint8_t expected_value[512]; + uint16_t handles[256]; + int num_attrs; + int chunk_sz; + int off; + int rc; + int i; + + ble_gatt_read_test_misc_init(); + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + num_attrs = ble_gatt_read_test_misc_extract_handles(attrs, handles); + + off = 0; + for (i = 0; i < num_attrs; i++) { + if (attrs[i].value_len > BLE_ATT_MTU_DFLT - 1 - off) { + chunk_sz = BLE_ATT_MTU_DFLT - 1 - off; + } else { + chunk_sz = attrs[i].value_len; + } + + if (chunk_sz > 0) { + memcpy(expected_value + off, attrs[i].value, chunk_sz); + off += chunk_sz; + } + } + + rc = ble_gattc_read_mult(2, handles, num_attrs, + ble_gatt_read_test_cb, NULL); + TEST_ASSERT_FATAL(rc == 0); + + ble_gatt_read_test_misc_rx_rsp_good_raw(2, BLE_ATT_OP_READ_MULT_RSP, + expected_value, off); + + TEST_ASSERT(ble_gatt_read_test_complete); + TEST_ASSERT(!ble_gattc_any_jobs()); + TEST_ASSERT(ble_gatt_read_test_attrs[0].conn_handle == 2); + TEST_ASSERT(ble_gatt_read_test_attrs[0].value_len == off); + TEST_ASSERT(memcmp(ble_gatt_read_test_attrs[0].value, expected_value, + off) == 0); +} + +static void +ble_gatt_read_test_misc_mult_verify_bad( + uint8_t att_status, uint16_t err_handle, + struct ble_hs_test_util_flat_attr *attrs) +{ + uint16_t handles[256]; + int num_attrs; + int rc; + + ble_gatt_read_test_misc_init(); + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + num_attrs = ble_gatt_read_test_misc_extract_handles(attrs, handles); + + rc = ble_gattc_read_mult(2, handles, num_attrs, + ble_gatt_read_test_cb, NULL); + TEST_ASSERT_FATAL(rc == 0); + + ble_gatt_read_test_misc_rx_rsp_bad(2, att_status, err_handle); + + TEST_ASSERT(ble_gatt_read_test_num_attrs == 0); + TEST_ASSERT(ble_gatt_read_test_bad_conn_handle == 2); + TEST_ASSERT(ble_gatt_read_test_bad_status == + BLE_HS_ERR_ATT_BASE + att_status); + TEST_ASSERT(!ble_gattc_any_jobs()); +} + +TEST_CASE(ble_gatt_read_test_by_handle) +{ + /* Read a seven-byte attribute. */ + ble_gatt_read_test_misc_verify_good( + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 43, + .value = { 1,2,3,4,5,6,7 }, + .value_len = 7 + } }); + + /* Read a one-byte attribute. */ + ble_gatt_read_test_misc_verify_good( + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 0x5432, + .value = { 0xff }, + .value_len = 1 + } }); + + /* Read a 200-byte attribute. */ + ble_gatt_read_test_misc_verify_good( + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 815, + .value = { 0 }, + .value_len = 200, + } }); + + /* Fail due to attribute not found. */ + ble_gatt_read_test_misc_verify_bad(BLE_ATT_ERR_ATTR_NOT_FOUND, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 719, + .value = { 1,2,3,4,5,6,7 }, + .value_len = 7 + } }); + + /* Fail due to invalid PDU. */ + ble_gatt_read_test_misc_verify_bad(BLE_ATT_ERR_INVALID_PDU, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 65, + .value = { 0xfa, 0x4c }, + .value_len = 2 + } }); +} + +TEST_CASE(ble_gatt_read_test_by_uuid) +{ + /* Read a single seven-byte attribute. */ + ble_gatt_read_test_misc_uuid_verify_good(1, 100, BLE_UUID16(0x1234), 0, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 43, + .value = { 1,2,3,4,5,6,7 }, + .value_len = 7 + }, { + 0, + } }); + + /* Read two seven-byte attributes; one response. */ + ble_gatt_read_test_misc_uuid_verify_good(1, 100, BLE_UUID16(0x1234), 0, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 43, + .value = { 1,2,3,4,5,6,7 }, + .value_len = 7 + }, { + .handle = 44, + .value = { 2,3,4,5,6,7,8 }, + .value_len = 7 + }, { + 0, + } }); + + /* Read two attributes; two responses. */ + ble_gatt_read_test_misc_uuid_verify_good(1, 100, BLE_UUID16(0x1234), 0, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 43, + .value = { 1,2,3,4,5,6,7 }, + .value_len = 7 + }, { + .handle = 44, + .value = { 2,3,4 }, + .value_len = 3 + }, { + 0, + } }); + + /* Stop after three reads. */ + ble_gatt_read_test_misc_uuid_verify_good(1, 100, BLE_UUID16(0x1234), 3, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 43, + .value = { 1,2,3,4,5,6,7 }, + .value_len = 7 + }, { + .handle = 44, + .value = { 2,3,4 }, + .value_len = 3 + }, { + .handle = 45, + .value = { 2,3,4 }, + .value_len = 3 + }, { + .handle = 46, + .value = { 3,4,5,6 }, + .value_len = 4 + }, { + .handle = 47, + .value = { 2,3,4 }, + .value_len = 3 + }, { + 0, + } }); +} + +TEST_CASE(ble_gatt_read_test_long) +{ + uint8_t data512[512]; + int i; + + for (i = 0; i < sizeof data512; i++) { + data512[i] = i; + } + + /* Read a seven-byte attribute. */ + ble_gatt_read_test_misc_long_verify_good(0, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 43, + .value = { 1,2,3,4,5,6,7 }, + .value_len = 7 + } }); + + /* Read a zero-byte attribute. */ + ble_gatt_read_test_misc_long_verify_good(0, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 43, + .value = { 0 }, + .value_len = 0 + } }); + + /* Read a 60-byte attribute; three requests. */ + ble_gatt_read_test_misc_long_verify_good(0, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 34, + .value = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 + }, + .value_len = 60 + } }); + + /* Stop after two reads. */ + ble_gatt_read_test_misc_long_verify_good(2, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 34, + .value = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 + }, + .value_len = 60 + } }); + + /* Fail due to attribute not found. */ + ble_gatt_read_test_misc_long_verify_bad(BLE_ATT_ERR_ATTR_NOT_FOUND, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 719, + .value = { 1, 2, 3, 4, 5, 6, 7 }, + .value_len = 7 + } }); +} + +TEST_CASE(ble_gatt_read_test_mult) +{ + uint8_t data512[512]; + int i; + + for (i = 0; i < sizeof data512; i++) { + data512[i] = i; + } + + /* Read one attribute. */ + ble_gatt_read_test_misc_mult_verify_good( + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 43, + .value = { 0, 1, 2, 3, 4, 5, 6, 7 }, + .value_len = 7 + }, { + 0 + } }); + + /* Read two attributes. */ + ble_gatt_read_test_misc_mult_verify_good( + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 43, + .value = { 0, 1, 2, 3, 4, 5, 6, 7 }, + .value_len = 7, + }, { + .handle = 44, + .value = { 8, 9, 10, 11 }, + .value_len = 4, + }, { + 0 + } }); + + /* Read two attributes (swap order). */ + ble_gatt_read_test_misc_mult_verify_good( + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 44, + .value = { 8, 9, 10, 11 }, + .value_len = 4, + }, { + .handle = 43, + .value = { 0, 1, 2, 3, 4, 5, 6, 7 }, + .value_len = 7, + }, { + 0 + } }); + + /* Read five attributes. */ + ble_gatt_read_test_misc_mult_verify_good( + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 43, + .value = { 0, 1, 2, 3, 4, 5, 6, 7 }, + .value_len = 7, + }, { + .handle = 44, + .value = { 8, 9, 10, 11 }, + .value_len = 4, + }, { + .handle = 145, + .value = { 12, 13 }, + .value_len = 2, + }, { + .handle = 191, + .value = { 14, 15, 16 }, + .value_len = 3, + }, { + .handle = 352, + .value = { 17, 18, 19, 20 }, + .value_len = 4, + }, { + 0 + } }); + + /* Fail due to attribute not found. */ + ble_gatt_read_test_misc_mult_verify_bad(BLE_ATT_ERR_ATTR_NOT_FOUND, 719, + (struct ble_hs_test_util_flat_attr[]) { { + .handle = 719, + .value = { 1,2,3,4,5,6,7 }, + .value_len = 7 + }, { + 0 + } }); +} + +TEST_CASE(ble_gatt_read_test_concurrent) +{ + int rc; + int i; + + ble_gatt_read_test_misc_init(); + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + /*** + * Perform three concurrent reads. Assert that each response is correctly + * matched up with its corresponding GATT procedure. + */ + + struct ble_hs_test_util_flat_attr attrs[3] = { + { + .handle = 1, + .offset = 0, + .value_len = 3, + .value = { 1, 2, 3 }, + }, + { + .handle = 2, + .offset = 0, + .value_len = 4, + .value = { 2, 3, 4, 5 }, + }, + { + .handle = 3, + .offset = 0, + .value_len = 5, + .value = { 3, 4, 5, 6, 7 }, + }, + }; + + rc = ble_gattc_read(2, attrs[0].handle, ble_gatt_read_test_cb, NULL); + TEST_ASSERT_FATAL(rc == 0); + rc = ble_gattc_read(2, attrs[1].handle, ble_gatt_read_test_cb, NULL); + TEST_ASSERT_FATAL(rc == 0); + rc = ble_gattc_read(2, attrs[2].handle, ble_gatt_read_test_cb, NULL); + TEST_ASSERT_FATAL(rc == 0); + + ble_gatt_read_test_misc_rx_rsp_good(2, attrs + 0); + ble_gatt_read_test_misc_rx_rsp_good(2, attrs + 1); + ble_gatt_read_test_misc_rx_rsp_good(2, attrs + 2); + + TEST_ASSERT(ble_gatt_read_test_num_attrs == 3); + + for (i = 0; i < 3; i++) { + TEST_ASSERT(ble_gatt_read_test_attrs[i].handle == attrs[i].handle); + TEST_ASSERT(ble_gatt_read_test_attrs[i].value_len == + attrs[i].value_len); + TEST_ASSERT(memcmp(ble_gatt_read_test_attrs[i].value, attrs[i].value, + attrs[i].value_len) == 0); + } +} + +TEST_SUITE(ble_gatt_read_test_suite) +{ + tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL); + + ble_gatt_read_test_by_handle(); + ble_gatt_read_test_by_uuid(); + ble_gatt_read_test_long(); + ble_gatt_read_test_mult(); + ble_gatt_read_test_concurrent(); +} + +int +ble_gatt_read_test_all(void) +{ + ble_gatt_read_test_suite(); + + return tu_any_failed; +}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d98ddc1c/net/nimble/host/test/src/ble_gatt_write_test.c ---------------------------------------------------------------------- diff --git a/net/nimble/host/test/src/ble_gatt_write_test.c b/net/nimble/host/test/src/ble_gatt_write_test.c new file mode 100644 index 0000000..f548198 --- /dev/null +++ b/net/nimble/host/test/src/ble_gatt_write_test.c @@ -0,0 +1,639 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <string.h> +#include <errno.h> +#include "testutil/testutil.h" +#include "nimble/ble.h" +#include "host/ble_hs_test.h" +#include "host/ble_gatt.h" +#include "host/ble_uuid.h" +#include "ble_hs_test_util.h" + +#define BLE_GATT_WRITE_TEST_MAX_ATTRS 128 + +static int ble_gatt_write_test_cb_called; + +static uint8_t ble_gatt_write_test_attr_value[BLE_ATT_ATTR_MAX_LEN]; +static struct ble_gatt_error ble_gatt_write_test_error; + +static struct ble_hs_test_util_flat_attr +ble_gatt_write_test_attrs[BLE_GATT_WRITE_TEST_MAX_ATTRS]; +static int ble_gatt_write_test_num_attrs; + +static void +ble_gatt_write_test_init(void) +{ + int i; + + ble_hs_test_util_init(); + ble_gatt_write_test_cb_called = 0; + ble_gatt_write_test_num_attrs = 0; + + for (i = 0; i < sizeof ble_gatt_write_test_attr_value; i++) { + ble_gatt_write_test_attr_value[i] = i; + } +} + +static int +ble_gatt_write_test_cb_good(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + int *attr_len; + + attr_len = arg; + + TEST_ASSERT(error != NULL); + TEST_ASSERT(conn_handle == 2); + + ble_gatt_write_test_error = *error; + + if (attr_len != NULL) { + TEST_ASSERT(error->status == 0); + TEST_ASSERT(attr->handle == 100); + } + + ble_gatt_write_test_cb_called = 1; + + return 0; +} + +static void +ble_gatt_write_test_rx_rsp(uint16_t conn_handle) +{ + uint8_t op; + int rc; + + op = BLE_ATT_OP_WRITE_RSP; + rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, + &op, 1); + TEST_ASSERT(rc == 0); +} + +static void +ble_gatt_write_test_rx_prep_rsp(uint16_t conn_handle, uint16_t attr_handle, + uint16_t offset, + const void *attr_data, uint16_t attr_data_len) +{ + struct ble_att_prep_write_cmd rsp; + uint8_t buf[512]; + int rc; + + rsp.bapc_handle = attr_handle; + rsp.bapc_offset = offset; + ble_att_prep_write_rsp_write(buf, sizeof buf, &rsp); + + memcpy(buf + BLE_ATT_PREP_WRITE_CMD_BASE_SZ, attr_data, attr_data_len); + + rc = ble_hs_test_util_l2cap_rx_payload_flat( + conn_handle, BLE_L2CAP_CID_ATT, buf, + BLE_ATT_PREP_WRITE_CMD_BASE_SZ + attr_data_len); + TEST_ASSERT(rc == 0); +} + +static void +ble_gatt_write_test_rx_exec_rsp(uint16_t conn_handle) +{ + uint8_t op; + int rc; + + op = BLE_ATT_OP_EXEC_WRITE_RSP; + rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, + &op, 1); + TEST_ASSERT(rc == 0); +} + +static void +ble_gatt_write_test_misc_long_good(int attr_len) +{ + uint16_t mtu; + int off; + int len; + int rc; + + ble_gatt_write_test_init(); + + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + mtu = ble_att_mtu(2); + + rc = ble_hs_test_util_gatt_write_long_flat( + 2, 100, ble_gatt_write_test_attr_value, attr_len, + ble_gatt_write_test_cb_good, &attr_len); + TEST_ASSERT(rc == 0); + + off = 0; + while (off < attr_len) { + len = mtu - BLE_ATT_PREP_WRITE_CMD_BASE_SZ; + if (off + len > attr_len) { + len = attr_len - off; + } + + /* Send the pending ATT Prep Write Command. */ + ble_hs_test_util_verify_tx_prep_write( + 100, off, ble_gatt_write_test_attr_value + off, len); + + /* Receive Prep Write response. */ + ble_gatt_write_test_rx_prep_rsp( + 2, 100, off, ble_gatt_write_test_attr_value + off, len); + + /* Verify callback hasn't gotten called. */ + TEST_ASSERT(!ble_gatt_write_test_cb_called); + + off += len; + } + + /* Verify execute write request sent. */ + ble_hs_test_util_verify_tx_exec_write(BLE_ATT_EXEC_WRITE_F_CONFIRM); + + /* Receive Exec Write response. */ + ble_hs_test_util_tx_all(); + ble_gatt_write_test_rx_exec_rsp(2); + + /* Verify callback got called. */ + TEST_ASSERT(ble_gatt_write_test_cb_called); +} + +typedef void ble_gatt_write_test_long_fail_fn(uint16_t conn_handle, + int off, int len); + +static void +ble_gatt_write_test_misc_long_bad(int attr_len, + ble_gatt_write_test_long_fail_fn *cb) +{ + uint16_t mtu; + int fail_now; + int off; + int len; + int rc; + + ble_gatt_write_test_init(); + + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + mtu = ble_att_mtu(2); + + rc = ble_hs_test_util_gatt_write_long_flat( + 2, 100, ble_gatt_write_test_attr_value, attr_len, + ble_gatt_write_test_cb_good, NULL); + TEST_ASSERT(rc == 0); + + fail_now = 0; + off = 0; + while (off < attr_len) { + len = mtu - BLE_ATT_PREP_WRITE_CMD_BASE_SZ; + if (off + len > attr_len) { + len = attr_len - off; + } + + /* Send the pending ATT Prep Write Command. */ + ble_hs_test_util_verify_tx_prep_write( + 100, off, ble_gatt_write_test_attr_value + off, len); + + /* Receive Prep Write response. */ + len = BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ; + if (off + len >= attr_len) { + len = attr_len - off; + fail_now = 1; + } + if (!fail_now) { + ble_gatt_write_test_rx_prep_rsp( + 2, 100, off, ble_gatt_write_test_attr_value + off, len); + } else { + cb(2, off, len); + break; + } + + /* Verify callback hasn't gotten called. */ + TEST_ASSERT(!ble_gatt_write_test_cb_called); + + off += len; + } + + /* Verify callback was called. */ + TEST_ASSERT(ble_gatt_write_test_cb_called); + TEST_ASSERT(ble_gatt_write_test_error.status == BLE_HS_EBADDATA); + TEST_ASSERT(ble_gatt_write_test_error.att_handle == 0); +} + +static void +ble_gatt_write_test_misc_long_fail_handle(uint16_t conn_handle, + int off, int len) +{ + ble_gatt_write_test_rx_prep_rsp( + conn_handle, 99, off, ble_gatt_write_test_attr_value + off, + len); +} + +static void +ble_gatt_write_test_misc_long_fail_offset(uint16_t conn_handle, + int off, int len) +{ + ble_gatt_write_test_rx_prep_rsp( + conn_handle, 100, off + 1, ble_gatt_write_test_attr_value + off, + len); +} + +static void +ble_gatt_write_test_misc_long_fail_value(uint16_t conn_handle, + int off, int len) +{ + ble_gatt_write_test_rx_prep_rsp( + conn_handle, 100, off, ble_gatt_write_test_attr_value + off + 1, + len); +} + +static void +ble_gatt_write_test_misc_long_fail_length(uint16_t conn_handle, + int off, int len) +{ + ble_gatt_write_test_rx_prep_rsp( + conn_handle, 100, off, ble_gatt_write_test_attr_value + off, + len - 1); +} + +static int +ble_gatt_write_test_reliable_cb_good(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attrs, + uint8_t num_attrs, void *arg) +{ + int i; + + TEST_ASSERT_FATAL(num_attrs <= BLE_GATT_WRITE_TEST_MAX_ATTRS); + + TEST_ASSERT(conn_handle == 2); + + ble_gatt_write_test_num_attrs = num_attrs; + for (i = 0; i < num_attrs; i++) { + ble_hs_test_util_attr_to_flat(ble_gatt_write_test_attrs + i, + attrs + i); + } + + ble_gatt_write_test_cb_called = 1; + + return 0; +} + +static void +ble_gatt_write_test_misc_reliable_good( + struct ble_hs_test_util_flat_attr *flat_attrs) +{ + const struct ble_hs_test_util_flat_attr *attr; + struct ble_gatt_attr attrs[16]; + uint16_t mtu; + int num_attrs; + int attr_idx; + int len; + int off; + int rc; + int i; + + ble_gatt_write_test_init(); + + for (num_attrs = 0; flat_attrs[num_attrs].handle != 0; num_attrs++) { + TEST_ASSERT_FATAL(num_attrs < sizeof attrs / sizeof attrs[0]); + ble_hs_test_util_attr_from_flat(attrs + num_attrs, + flat_attrs + num_attrs); + } + + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + mtu = ble_att_mtu(2); + + rc = ble_gattc_write_reliable(2, attrs, num_attrs, + ble_gatt_write_test_reliable_cb_good, NULL); + TEST_ASSERT(rc == 0); + + attr_idx = 0; + off = 0; + while (attr_idx < num_attrs) { + attr = flat_attrs + attr_idx; + + len = mtu - BLE_ATT_PREP_WRITE_CMD_BASE_SZ; + if (off + len > attr->value_len) { + len = attr->value_len - off; + } + + /* Send the pending ATT Prep Write Command. */ + ble_hs_test_util_verify_tx_prep_write(attr->handle, off, + attr->value + off, len); + + /* Receive Prep Write response. */ + ble_gatt_write_test_rx_prep_rsp(2, attr->handle, off, + attr->value + off, len); + + /* Verify callback hasn't gotten called. */ + TEST_ASSERT(!ble_gatt_write_test_cb_called); + + off += len; + if (off >= attr->value_len) { + attr_idx++; + off = 0; + } + } + + /* Verify execute write request sent. */ + ble_hs_test_util_verify_tx_exec_write(BLE_ATT_EXEC_WRITE_F_CONFIRM); + + /* Receive Exec Write response. */ + ble_hs_test_util_tx_all(); + ble_gatt_write_test_rx_exec_rsp(2); + + /* Verify callback got called. */ + TEST_ASSERT(ble_gatt_write_test_cb_called); + TEST_ASSERT(ble_gatt_write_test_num_attrs == num_attrs); + for (i = 0; i < num_attrs; i++) { + rc = ble_hs_test_util_flat_attr_cmp( + ble_gatt_write_test_attrs + i, flat_attrs + i); + TEST_ASSERT(rc == 0); + } +} + +TEST_CASE(ble_gatt_write_test_no_rsp) +{ + int attr_len; + int rc; + + ble_gatt_write_test_init(); + + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + attr_len = 4; + rc = ble_hs_test_util_gatt_write_no_rsp_flat( + 2, 100, ble_gatt_write_test_attr_value, attr_len); + TEST_ASSERT(rc == 0); + + /* Send the pending ATT Write Command. */ + ble_hs_test_util_tx_all(); + + /* No response expected; verify callback not called. */ + TEST_ASSERT(!ble_gatt_write_test_cb_called); +} + +TEST_CASE(ble_gatt_write_test_rsp) +{ + int attr_len; + + ble_gatt_write_test_init(); + + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + attr_len = 4; + ble_hs_test_util_gatt_write_flat(2, 100, ble_gatt_write_test_attr_value, + attr_len, ble_gatt_write_test_cb_good, + &attr_len); + + /* Send the pending ATT Write Command. */ + ble_hs_test_util_tx_all(); + + /* Response not received yet; verify callback not called. */ + TEST_ASSERT(!ble_gatt_write_test_cb_called); + + /* Receive write response. */ + ble_gatt_write_test_rx_rsp(2); + + /* Verify callback got called. */ + TEST_ASSERT(ble_gatt_write_test_cb_called); +} + +TEST_CASE(ble_gatt_write_test_long_good) +{ + /*** 1 prep write req/rsp. */ + ble_gatt_write_test_misc_long_good( + BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ); + + /*** 2 prep write reqs/rsps. */ + ble_gatt_write_test_misc_long_good( + BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ + 1); + + /*** Maximum reqs/rsps. */ + ble_gatt_write_test_misc_long_good(BLE_ATT_ATTR_MAX_LEN); +} + +TEST_CASE(ble_gatt_write_test_long_bad_handle) +{ + /*** 1 prep write req/rsp. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ, + ble_gatt_write_test_misc_long_fail_handle); + + /*** 2 prep write reqs/rsps. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ + 1, + ble_gatt_write_test_misc_long_fail_handle); + + /*** Maximum reqs/rsps. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_ATTR_MAX_LEN, + ble_gatt_write_test_misc_long_fail_handle); +} + +TEST_CASE(ble_gatt_write_test_long_bad_offset) +{ + /*** 1 prep write req/rsp. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ, + ble_gatt_write_test_misc_long_fail_offset); + + /*** 2 prep write reqs/rsps. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ + 1, + ble_gatt_write_test_misc_long_fail_offset); + + /*** Maximum reqs/rsps. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_ATTR_MAX_LEN, + ble_gatt_write_test_misc_long_fail_offset); +} + +TEST_CASE(ble_gatt_write_test_long_bad_value) +{ + /*** 1 prep write req/rsp. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ, + ble_gatt_write_test_misc_long_fail_value); + + /*** 2 prep write reqs/rsps. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ + 1, + ble_gatt_write_test_misc_long_fail_value); + + /*** Maximum reqs/rsps. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_ATTR_MAX_LEN, + ble_gatt_write_test_misc_long_fail_value); +} + +TEST_CASE(ble_gatt_write_test_long_bad_length) +{ + /*** 1 prep write req/rsp. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ, + ble_gatt_write_test_misc_long_fail_length); + + /*** 2 prep write reqs/rsps. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ + 1, + ble_gatt_write_test_misc_long_fail_length); + + /*** Maximum reqs/rsps. */ + ble_gatt_write_test_misc_long_bad( + BLE_ATT_ATTR_MAX_LEN, + ble_gatt_write_test_misc_long_fail_length); +} + +TEST_CASE(ble_gatt_write_test_reliable_good) +{ + /*** 1 attribute. */ + ble_gatt_write_test_misc_reliable_good( + ((struct ble_hs_test_util_flat_attr[]) { { + .handle = 100, + .value_len = 2, + .value = { 1, 2 }, + }, { + 0 + } })); + + /*** 2 attributes. */ + ble_gatt_write_test_misc_reliable_good( + ((struct ble_hs_test_util_flat_attr[]) { { + .handle = 100, + .value_len = 2, + .value = { 1,2 }, + }, { + .handle = 113, + .value_len = 6, + .value = { 5,6,7,8,9,10 }, + }, { + 0 + } })); + + /*** 3 attributes. */ + ble_gatt_write_test_misc_reliable_good( + ((struct ble_hs_test_util_flat_attr[]) { { + .handle = 100, + .value_len = 2, + .value = { 1,2 }, + }, { + .handle = 113, + .value_len = 6, + .value = { 5,6,7,8,9,10 }, + }, { + .handle = 144, + .value_len = 1, + .value = { 0xff }, + }, { + 0 + } })); + + /*** Long attributes. */ + ble_gatt_write_test_misc_reliable_good( + ((struct ble_hs_test_util_flat_attr[]) { { + .handle = 100, + .value_len = 20, + .value = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 }, + }, { + .handle = 144, + .value_len = 20, + .value = { 11,12,13,14,15,16,17,18,19,110, + 111,112,113,114,115,116,117,118,119,120 }, + }, { + 0 + } })); +} + +TEST_CASE(ble_gatt_write_test_long_queue_full) +{ + int off; + int len; + int rc; + int i; + + ble_gatt_write_test_init(); + + ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), + NULL, NULL); + + rc = ble_hs_test_util_gatt_write_long_flat( + 2, 100, ble_gatt_write_test_attr_value, 128, + ble_gatt_write_test_cb_good, NULL); + TEST_ASSERT(rc == 0); + + off = 0; + for (i = 0; i < 2; i++) { + /* Verify prep write request was sent. */ + ble_hs_test_util_tx_all(); + TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() != NULL); + + /* Receive Prep Write response. */ + len = BLE_ATT_MTU_DFLT - BLE_ATT_PREP_WRITE_CMD_BASE_SZ; + ble_gatt_write_test_rx_prep_rsp( + 2, 100, off, ble_gatt_write_test_attr_value + off, len); + + /* Verify callback hasn't gotten called. */ + TEST_ASSERT(!ble_gatt_write_test_cb_called); + + off += len; + } + + /* Verify prep write request was sent. */ + ble_hs_test_util_tx_all(); + TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() != NULL); + + /* Receive queue full error. */ + ble_hs_test_util_rx_att_err_rsp(2, BLE_ATT_OP_PREP_WRITE_REQ, + BLE_ATT_ERR_PREPARE_QUEUE_FULL, 100); + + /* Verify callback was called. */ + TEST_ASSERT(ble_gatt_write_test_cb_called); + TEST_ASSERT(ble_gatt_write_test_error.status == + BLE_HS_ATT_ERR(BLE_ATT_ERR_PREPARE_QUEUE_FULL)); + TEST_ASSERT(ble_gatt_write_test_error.att_handle == 100); + + /* Verify clear queue command got sent. */ + ble_hs_test_util_verify_tx_exec_write(0); +} + +TEST_SUITE(ble_gatt_write_test_suite) +{ + tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL); + + ble_gatt_write_test_no_rsp(); + ble_gatt_write_test_rsp(); + ble_gatt_write_test_long_good(); + ble_gatt_write_test_long_bad_handle(); + ble_gatt_write_test_long_bad_offset(); + ble_gatt_write_test_long_bad_value(); + ble_gatt_write_test_long_bad_length(); + ble_gatt_write_test_long_queue_full(); + ble_gatt_write_test_reliable_good(); +} + +int +ble_gatt_write_test_all(void) +{ + ble_gatt_write_test_suite(); + + return tu_any_failed; +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d98ddc1c/net/nimble/host/test/src/ble_gatts_notify_test.c ---------------------------------------------------------------------- diff --git a/net/nimble/host/test/src/ble_gatts_notify_test.c b/net/nimble/host/test/src/ble_gatts_notify_test.c new file mode 100644 index 0000000..4516e66 --- /dev/null +++ b/net/nimble/host/test/src/ble_gatts_notify_test.c @@ -0,0 +1,983 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <string.h> +#include <errno.h> +#include "testutil/testutil.h" +#include "nimble/ble.h" +#include "host/ble_uuid.h" +#include "host/ble_hs_test.h" +#include "ble_hs_test_util.h" +#include "ble_hs_test_util_store.h" + +#define BLE_GATTS_NOTIFY_TEST_CHR_1_UUID 0x1111 +#define BLE_GATTS_NOTIFY_TEST_CHR_2_UUID 0x2222 + +#define BLE_GATTS_NOTIFY_TEST_MAX_EVENTS 16 + +static uint8_t ble_gatts_notify_test_peer_addr[6] = {2,3,4,5,6,7}; + +static int +ble_gatts_notify_test_misc_access(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); +static void +ble_gatts_notify_test_misc_reg_cb(struct ble_gatt_register_ctxt *ctxt, + void *arg); + +static const struct ble_gatt_svc_def ble_gatts_notify_test_svcs[] = { { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid128 = BLE_UUID16(0x1234), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid128 = BLE_UUID16(BLE_GATTS_NOTIFY_TEST_CHR_1_UUID), + .access_cb = ble_gatts_notify_test_misc_access, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY | + BLE_GATT_CHR_F_INDICATE, + }, { + .uuid128 = BLE_UUID16(BLE_GATTS_NOTIFY_TEST_CHR_2_UUID), + .access_cb = ble_gatts_notify_test_misc_access, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY | + BLE_GATT_CHR_F_INDICATE, + }, { + 0 + } }, +}, { + 0 +} }; + + +static uint16_t ble_gatts_notify_test_chr_1_def_handle; +static uint8_t ble_gatts_notify_test_chr_1_val[1024]; +static int ble_gatts_notify_test_chr_1_len; +static uint16_t ble_gatts_notify_test_chr_2_def_handle; +static uint8_t ble_gatts_notify_test_chr_2_val[1024]; +static int ble_gatts_notify_test_chr_2_len; + +static struct ble_gap_event +ble_gatts_notify_test_events[BLE_GATTS_NOTIFY_TEST_MAX_EVENTS]; + +static int ble_gatts_notify_test_num_events; + +typedef int ble_store_write_fn(int obj_type, union ble_store_value *val); + +typedef int ble_store_delete_fn(int obj_type, union ble_store_key *key); + +static int +ble_gatts_notify_test_util_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_NOTIFY_TX: + case BLE_GAP_EVENT_SUBSCRIBE: + TEST_ASSERT_FATAL(ble_gatts_notify_test_num_events < + BLE_GATTS_NOTIFY_TEST_MAX_EVENTS); + + ble_gatts_notify_test_events[ble_gatts_notify_test_num_events++] = + *event; + + default: + break; + } + + return 0; +} + +static uint16_t +ble_gatts_notify_test_misc_read_notify(uint16_t conn_handle, + uint16_t chr_def_handle) +{ + struct ble_att_read_req req; + struct os_mbuf *om; + uint8_t buf[BLE_ATT_READ_REQ_SZ]; + uint16_t flags; + int rc; + + req.barq_handle = chr_def_handle + 2; + ble_att_read_req_write(buf, sizeof buf, &req); + + rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, + buf, sizeof buf); + TEST_ASSERT(rc == 0); + + ble_hs_test_util_tx_all(); + + om = ble_hs_test_util_prev_tx_dequeue_pullup(); + TEST_ASSERT_FATAL(om != NULL); + TEST_ASSERT_FATAL(om->om_len == 3); + TEST_ASSERT_FATAL(om->om_data[0] == BLE_ATT_OP_READ_RSP); + + flags = le16toh(om->om_data + 1); + return flags; +} + +static void +ble_gatts_notify_test_misc_enable_notify(uint16_t conn_handle, + uint16_t chr_def_handle, + uint16_t flags) +{ + struct ble_att_write_req req; + uint8_t buf[BLE_ATT_WRITE_REQ_BASE_SZ + 2]; + int rc; + + req.bawq_handle = chr_def_handle + 2; + ble_att_write_req_write(buf, sizeof buf, &req); + + htole16(buf + BLE_ATT_WRITE_REQ_BASE_SZ, flags); + rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, + buf, sizeof buf); + TEST_ASSERT(rc == 0); +} + +static void +ble_gatts_notify_test_util_next_event(struct ble_gap_event *event) +{ + TEST_ASSERT_FATAL(ble_gatts_notify_test_num_events > 0); + + *event = *ble_gatts_notify_test_events; + + ble_gatts_notify_test_num_events--; + if (ble_gatts_notify_test_num_events > 0) { + memmove(ble_gatts_notify_test_events + 0, + ble_gatts_notify_test_events + 1, + ble_gatts_notify_test_num_events * sizeof *event); + } +} + +static void +ble_gatts_notify_test_util_verify_sub_event(uint16_t conn_handle, + uint8_t attr_handle, + uint8_t reason, + uint8_t prevn, uint8_t curn, + uint8_t previ, uint8_t curi) +{ + struct ble_gap_event event; + + ble_gatts_notify_test_util_next_event(&event); + + TEST_ASSERT(event.type == BLE_GAP_EVENT_SUBSCRIBE); + TEST_ASSERT(event.subscribe.conn_handle == conn_handle); + TEST_ASSERT(event.subscribe.attr_handle == attr_handle); + TEST_ASSERT(event.subscribe.reason == reason); + TEST_ASSERT(event.subscribe.prev_notify == prevn); + TEST_ASSERT(event.subscribe.cur_notify == curn); + TEST_ASSERT(event.subscribe.prev_indicate == previ); + TEST_ASSERT(event.subscribe.cur_indicate == curi); +} + +static void +ble_gatts_notify_test_util_verify_tx_event(uint16_t conn_handle, + uint8_t attr_handle, + int status, + int indication) +{ + struct ble_gap_event event; + + ble_gatts_notify_test_util_next_event(&event); + + TEST_ASSERT(event.type == BLE_GAP_EVENT_NOTIFY_TX); + TEST_ASSERT(event.notify_tx.status == status); + TEST_ASSERT(event.notify_tx.conn_handle == conn_handle); + TEST_ASSERT(event.notify_tx.attr_handle == attr_handle); + TEST_ASSERT(event.notify_tx.indication == indication); +} + +static void +ble_gatts_notify_test_util_verify_ack_event(uint16_t conn_handle, + uint8_t attr_handle) +{ + ble_gatts_notify_test_util_verify_tx_event(conn_handle, attr_handle, + BLE_HS_EDONE, 1); +} + +static void +ble_gatts_notify_test_misc_init(uint16_t *out_conn_handle, int bonding, + uint16_t chr1_flags, uint16_t chr2_flags) +{ + struct ble_hs_conn *conn; + uint16_t flags; + int exp_num_cccds; + int rc; + + ble_hs_test_util_init(); + + ble_gatts_notify_test_num_events = 0; + + ble_hs_test_util_store_init(10, 10, 10); + ble_hs_cfg.store_read_cb = ble_hs_test_util_store_read; + ble_hs_cfg.store_write_cb = ble_hs_test_util_store_write; + + rc = ble_gatts_register_svcs(ble_gatts_notify_test_svcs, + ble_gatts_notify_test_misc_reg_cb, NULL); + TEST_ASSERT_FATAL(rc == 0); + TEST_ASSERT_FATAL(ble_gatts_notify_test_chr_1_def_handle != 0); + TEST_ASSERT_FATAL(ble_gatts_notify_test_chr_2_def_handle != 0); + + ble_gatts_start(); + + ble_hs_test_util_create_conn(2, ble_gatts_notify_test_peer_addr, + ble_gatts_notify_test_util_gap_event, NULL); + *out_conn_handle = 2; + + if (bonding) { + ble_hs_lock(); + conn = ble_hs_conn_find(2); + TEST_ASSERT_FATAL(conn != NULL); + conn->bhc_sec_state.encrypted = 1; + conn->bhc_sec_state.authenticated = 1; + conn->bhc_sec_state.bonded = 1; + ble_hs_unlock(); + } + + /* Ensure notifications disabled on new connection. */ + flags = ble_gatts_notify_test_misc_read_notify( + 2, ble_gatts_notify_test_chr_1_def_handle); + TEST_ASSERT(flags == 0); + flags = ble_gatts_notify_test_misc_read_notify( + 2, ble_gatts_notify_test_chr_2_def_handle); + TEST_ASSERT(flags == 0); + + /* Set initial notification / indication state and verify that subscription + * callback gets executed. + */ + if (chr1_flags != 0) { + ble_gatts_notify_test_misc_enable_notify( + 2, ble_gatts_notify_test_chr_1_def_handle, chr1_flags); + + ble_gatts_notify_test_util_verify_sub_event( + *out_conn_handle, + ble_gatts_notify_test_chr_1_def_handle + 1, + BLE_GAP_SUBSCRIBE_REASON_WRITE, + 0, chr1_flags == BLE_GATTS_CLT_CFG_F_NOTIFY, + 0, chr1_flags == BLE_GATTS_CLT_CFG_F_INDICATE); + } + if (chr2_flags != 0) { + ble_gatts_notify_test_misc_enable_notify( + 2, ble_gatts_notify_test_chr_2_def_handle, chr2_flags); + + ble_gatts_notify_test_util_verify_sub_event( + *out_conn_handle, + ble_gatts_notify_test_chr_2_def_handle + 1, + BLE_GAP_SUBSCRIBE_REASON_WRITE, + 0, chr2_flags == BLE_GATTS_CLT_CFG_F_NOTIFY, + 0, chr2_flags == BLE_GATTS_CLT_CFG_F_INDICATE); + } + + /* Ensure no extraneous subscription callbacks were executed. */ + TEST_ASSERT(ble_gatts_notify_test_num_events == 0); + + /* Toss both write responses. */ + ble_hs_test_util_prev_tx_queue_clear(); + + /* Ensure notification / indication state reads back correctly. */ + flags = ble_gatts_notify_test_misc_read_notify( + 2, ble_gatts_notify_test_chr_1_def_handle); + TEST_ASSERT(flags == chr1_flags); + flags = ble_gatts_notify_test_misc_read_notify( + 2, ble_gatts_notify_test_chr_2_def_handle); + TEST_ASSERT(flags == chr2_flags); + + /* Ensure both CCCDs still persisted. */ + if (bonding) { + exp_num_cccds = (chr1_flags != 0) + (chr2_flags != 0); + } else { + exp_num_cccds = 0; + } + TEST_ASSERT(ble_hs_test_util_store_num_cccds == exp_num_cccds); +} + +static void +ble_gatts_notify_test_disconnect(uint16_t conn_handle, + uint8_t chr1_flags, + uint8_t chr1_indicate_in_progress, + uint8_t chr2_flags, + uint8_t chr2_indicate_in_progress) +{ + ble_hs_test_util_conn_disconnect(conn_handle); + + if (chr1_indicate_in_progress) { + ble_gatts_notify_test_util_verify_tx_event( + conn_handle, + ble_gatts_notify_test_chr_1_def_handle + 1, + BLE_HS_ENOTCONN, + 1); + } + + /* Verify subscription callback executed for each subscribed + * characteristic. + */ + if (chr1_flags != 0) { + ble_gatts_notify_test_util_verify_sub_event( + conn_handle, + ble_gatts_notify_test_chr_1_def_handle + 1, + BLE_GAP_SUBSCRIBE_REASON_TERM, + chr1_flags == BLE_GATTS_CLT_CFG_F_NOTIFY, 0, + chr1_flags == BLE_GATTS_CLT_CFG_F_INDICATE, 0); + } + + if (chr2_indicate_in_progress) { + ble_gatts_notify_test_util_verify_tx_event( + conn_handle, + ble_gatts_notify_test_chr_2_def_handle + 1, + BLE_HS_ENOTCONN, + 1); + } + + if (chr2_flags != 0) { + ble_gatts_notify_test_util_verify_sub_event( + conn_handle, + ble_gatts_notify_test_chr_2_def_handle + 1, + BLE_GAP_SUBSCRIBE_REASON_TERM, + chr2_flags == BLE_GATTS_CLT_CFG_F_NOTIFY, 0, + chr2_flags == BLE_GATTS_CLT_CFG_F_INDICATE, 0); + } +} + +static void +ble_gatts_notify_test_misc_reg_cb(struct ble_gatt_register_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + + if (ctxt->op == BLE_GATT_REGISTER_OP_CHR) { + uuid16 = ble_uuid_128_to_16(ctxt->chr.chr_def->uuid128); + switch (uuid16) { + case BLE_GATTS_NOTIFY_TEST_CHR_1_UUID: + ble_gatts_notify_test_chr_1_def_handle = ctxt->chr.def_handle; + break; + + case BLE_GATTS_NOTIFY_TEST_CHR_2_UUID: + ble_gatts_notify_test_chr_2_def_handle = ctxt->chr.def_handle; + break; + + default: + TEST_ASSERT_FATAL(0); + break; + } + } +} + +static int +ble_gatts_notify_test_misc_access(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + int rc; + + TEST_ASSERT_FATAL(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + TEST_ASSERT(conn_handle == 0xffff); + + if (attr_handle == ble_gatts_notify_test_chr_1_def_handle + 1) { + TEST_ASSERT(ctxt->chr == + &ble_gatts_notify_test_svcs[0].characteristics[0]); + rc = os_mbuf_copyinto(ctxt->om, 0, ble_gatts_notify_test_chr_1_val, + ble_gatts_notify_test_chr_1_len); + TEST_ASSERT_FATAL(rc == 0); + } else if (attr_handle == ble_gatts_notify_test_chr_2_def_handle + 1) { + TEST_ASSERT(ctxt->chr == + &ble_gatts_notify_test_svcs[0].characteristics[1]); + rc = os_mbuf_copyinto(ctxt->om, 0, ble_gatts_notify_test_chr_2_val, + ble_gatts_notify_test_chr_2_len); + TEST_ASSERT_FATAL(rc == 0); + } else { + TEST_ASSERT_FATAL(0); + } + + return 0; +} + +static void +ble_gatts_notify_test_misc_rx_indicate_rsp(uint16_t conn_handle, + uint16_t attr_handle) +{ + uint8_t buf[BLE_ATT_INDICATE_RSP_SZ]; + int rc; + + ble_att_indicate_rsp_write(buf, sizeof buf); + + rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, + buf, sizeof buf); + TEST_ASSERT(rc == 0); + + ble_gatts_notify_test_util_verify_ack_event(conn_handle, attr_handle); +} + + +static void +ble_gatts_notify_test_misc_verify_tx_n(uint16_t conn_handle, + uint16_t attr_handle, + uint8_t *attr_data, int attr_len) +{ + struct ble_att_notify_req req; + struct os_mbuf *om; + int i; + + ble_hs_test_util_tx_all(); + + om = ble_hs_test_util_prev_tx_dequeue_pullup(); + TEST_ASSERT_FATAL(om != NULL); + + ble_att_notify_req_parse(om->om_data, om->om_len, &req); + TEST_ASSERT(req.banq_handle == attr_handle); + + for (i = 0; i < attr_len; i++) { + TEST_ASSERT(om->om_data[BLE_ATT_NOTIFY_REQ_BASE_SZ + i] == + attr_data[i]); + } + + ble_gatts_notify_test_util_verify_tx_event(conn_handle, attr_handle, 0, 0); +} + +static void +ble_gatts_notify_test_misc_verify_tx_i(uint16_t conn_handle, + uint16_t attr_handle, + uint8_t *attr_data, int attr_len) +{ + struct ble_att_indicate_req req; + struct os_mbuf *om; + int i; + + ble_hs_test_util_tx_all(); + + om = ble_hs_test_util_prev_tx_dequeue_pullup(); + TEST_ASSERT_FATAL(om != NULL); + + ble_att_indicate_req_parse(om->om_data, om->om_len, &req); + TEST_ASSERT(req.baiq_handle == attr_handle); + + for (i = 0; i < attr_len; i++) { + TEST_ASSERT(om->om_data[BLE_ATT_INDICATE_REQ_BASE_SZ + i] == + attr_data[i]); + } + + ble_gatts_notify_test_util_verify_tx_event(conn_handle, attr_handle, 0, 1); +} + +static void +ble_gatts_notify_test_misc_verify_tx_gen(uint16_t conn_handle, int attr_idx, + uint8_t chr_flags) +{ + uint16_t attr_handle; + uint16_t attr_len; + void *attr_val; + + switch (attr_idx) { + case 1: + attr_handle = ble_gatts_notify_test_chr_1_def_handle + 1; + attr_len = ble_gatts_notify_test_chr_1_len; + attr_val = ble_gatts_notify_test_chr_1_val; + break; + + case 2: + attr_handle = ble_gatts_notify_test_chr_2_def_handle + 1; + attr_len = ble_gatts_notify_test_chr_2_len; + attr_val = ble_gatts_notify_test_chr_2_val; + break; + + default: + TEST_ASSERT_FATAL(0); + break; + } + + switch (chr_flags) { + case 0: + break; + + case BLE_GATTS_CLT_CFG_F_NOTIFY: + ble_gatts_notify_test_misc_verify_tx_n(conn_handle, attr_handle, + attr_val, attr_len); + break; + + case BLE_GATTS_CLT_CFG_F_INDICATE: + ble_gatts_notify_test_misc_verify_tx_i(conn_handle, attr_handle, + attr_val, attr_len); + break; + + default: + TEST_ASSERT_FATAL(0); + break; + } +} + +static void +ble_gatts_notify_test_restore_bonding(uint16_t conn_handle, + uint8_t chr1_flags, uint8_t chr1_tx, + uint8_t chr2_flags, uint8_t chr2_tx) +{ + struct ble_hs_conn *conn; + + ble_hs_lock(); + conn = ble_hs_conn_find(conn_handle); + TEST_ASSERT_FATAL(conn != NULL); + conn->bhc_sec_state.encrypted = 1; + conn->bhc_sec_state.authenticated = 1; + conn->bhc_sec_state.bonded = 1; + ble_hs_unlock(); + + ble_gatts_bonding_restored(conn_handle); + + /* Verify subscription callback executed for each subscribed + * characteristic. + */ + if (chr1_flags != 0) { + ble_gatts_notify_test_util_verify_sub_event( + conn_handle, + ble_gatts_notify_test_chr_1_def_handle + 1, + BLE_GAP_SUBSCRIBE_REASON_RESTORE, + 0, chr1_flags == BLE_GATTS_CLT_CFG_F_NOTIFY, + 0, chr1_flags == BLE_GATTS_CLT_CFG_F_INDICATE); + + } + if (chr1_tx) { + ble_gatts_notify_test_misc_verify_tx_gen(conn_handle, 1, chr1_flags); + } + + + if (chr2_flags != 0) { + ble_gatts_notify_test_util_verify_sub_event( + conn_handle, + ble_gatts_notify_test_chr_2_def_handle + 1, + BLE_GAP_SUBSCRIBE_REASON_RESTORE, + 0, chr2_flags == BLE_GATTS_CLT_CFG_F_NOTIFY, + 0, chr2_flags == BLE_GATTS_CLT_CFG_F_INDICATE); + } + if (chr2_tx) { + ble_gatts_notify_test_misc_verify_tx_gen(conn_handle, 2, chr2_flags); + } +} + +TEST_CASE(ble_gatts_notify_test_n) +{ + uint16_t conn_handle; + uint16_t flags; + + ble_gatts_notify_test_misc_init(&conn_handle, 0, + BLE_GATTS_CLT_CFG_F_NOTIFY, + BLE_GATTS_CLT_CFG_F_NOTIFY); + + /* Ensure notifications read back as enabled. */ + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_1_def_handle); + TEST_ASSERT(flags == BLE_GATTS_CLT_CFG_F_NOTIFY); + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_2_def_handle); + TEST_ASSERT(flags == BLE_GATTS_CLT_CFG_F_NOTIFY); + + /* Update characteristic 1's value. */ + ble_gatts_notify_test_chr_1_len = 1; + ble_gatts_notify_test_chr_1_val[0] = 0xab; + ble_gatts_chr_updated(ble_gatts_notify_test_chr_1_def_handle + 1); + + /* Verify notification sent properly. */ + ble_gatts_notify_test_misc_verify_tx_n( + conn_handle, + ble_gatts_notify_test_chr_1_def_handle + 1, + ble_gatts_notify_test_chr_1_val, + ble_gatts_notify_test_chr_1_len); + + /* Update characteristic 2's value. */ + ble_gatts_notify_test_chr_2_len = 16; + memcpy(ble_gatts_notify_test_chr_2_val, + ((uint8_t[]){0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}), 16); + ble_gatts_chr_updated(ble_gatts_notify_test_chr_2_def_handle + 1); + + /* Verify notification sent properly. */ + ble_gatts_notify_test_misc_verify_tx_n( + conn_handle, + ble_gatts_notify_test_chr_2_def_handle + 1, + ble_gatts_notify_test_chr_2_val, + ble_gatts_notify_test_chr_2_len); + + /*** + * Disconnect, modify characteristic values, and reconnect. Ensure + * notifications are not sent and are no longer enabled. + */ + + ble_gatts_notify_test_disconnect(conn_handle, + BLE_GATTS_CLT_CFG_F_NOTIFY, 0, + BLE_GATTS_CLT_CFG_F_NOTIFY, 0); + + /* Update characteristic 1's value. */ + ble_gatts_notify_test_chr_1_len = 1; + ble_gatts_notify_test_chr_1_val[0] = 0xdd; + ble_gatts_chr_updated(ble_gatts_notify_test_chr_1_def_handle + 1); + + /* Update characteristic 2's value. */ + ble_gatts_notify_test_chr_2_len = 16; + memcpy(ble_gatts_notify_test_chr_2_val, + ((uint8_t[]){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}), 16); + ble_gatts_chr_updated(ble_gatts_notify_test_chr_2_def_handle + 1); + + ble_hs_test_util_create_conn(conn_handle, ((uint8_t[]){2,3,4,5,6,7,8,9}), + ble_gatts_notify_test_util_gap_event, NULL); + + /* Ensure no notifications sent. */ + TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL); + + /* Ensure notifications disabled. */ + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_1_def_handle); + TEST_ASSERT(flags == 0); + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_2_def_handle); + TEST_ASSERT(flags == 0); +} + +TEST_CASE(ble_gatts_notify_test_i) +{ + uint16_t conn_handle; + uint16_t flags; + + ble_gatts_notify_test_misc_init(&conn_handle, 0, + BLE_GATTS_CLT_CFG_F_INDICATE, + BLE_GATTS_CLT_CFG_F_INDICATE); + + /* Update characteristic 1's value. */ + ble_gatts_notify_test_chr_1_len = 1; + ble_gatts_notify_test_chr_1_val[0] = 0xab; + ble_gatts_chr_updated(ble_gatts_notify_test_chr_1_def_handle + 1); + + /* Verify indication sent properly. */ + ble_gatts_notify_test_misc_verify_tx_i( + conn_handle, + ble_gatts_notify_test_chr_1_def_handle + 1, + ble_gatts_notify_test_chr_1_val, + ble_gatts_notify_test_chr_1_len); + + /* Update characteristic 2's value. */ + ble_gatts_notify_test_chr_2_len = 16; + memcpy(ble_gatts_notify_test_chr_2_val, + ((uint8_t[]){0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}), 16); + ble_gatts_chr_updated(ble_gatts_notify_test_chr_2_def_handle + 1); + + /* Verify the second indication doesn't get sent until the first is + * confirmed. + */ + ble_hs_test_util_tx_all(); + TEST_ASSERT(ble_hs_test_util_prev_tx_queue_sz() == 0); + + /* Receive the confirmation for the first indication. */ + ble_gatts_notify_test_misc_rx_indicate_rsp( + conn_handle, + ble_gatts_notify_test_chr_1_def_handle + 1); + + /* Verify indication sent properly. */ + ble_hs_test_util_tx_all(); + ble_gatts_notify_test_misc_verify_tx_i( + conn_handle, + ble_gatts_notify_test_chr_2_def_handle + 1, + ble_gatts_notify_test_chr_2_val, + ble_gatts_notify_test_chr_2_len); + + /* Receive the confirmation for the second indication. */ + ble_gatts_notify_test_misc_rx_indicate_rsp( + conn_handle, + ble_gatts_notify_test_chr_2_def_handle + 1); + + /* Verify no pending GATT jobs. */ + TEST_ASSERT(!ble_gattc_any_jobs()); + + /*** + * Disconnect, modify characteristic values, and reconnect. Ensure + * indications are not sent and are no longer enabled. + */ + + ble_gatts_notify_test_disconnect(conn_handle, + BLE_GATTS_CLT_CFG_F_INDICATE, 0, + BLE_GATTS_CLT_CFG_F_INDICATE, 0); + + /* Update characteristic 1's value. */ + ble_gatts_notify_test_chr_1_len = 1; + ble_gatts_notify_test_chr_1_val[0] = 0xdd; + ble_gatts_chr_updated(ble_gatts_notify_test_chr_1_def_handle + 1); + + /* Update characteristic 2's value. */ + ble_gatts_notify_test_chr_2_len = 16; + memcpy(ble_gatts_notify_test_chr_2_val, + ((uint8_t[]){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}), 16); + ble_gatts_chr_updated(ble_gatts_notify_test_chr_2_def_handle + 1); + + ble_hs_test_util_create_conn(conn_handle, ((uint8_t[]){2,3,4,5,6,7,8,9}), + ble_gatts_notify_test_util_gap_event, NULL); + + /* Ensure no indications sent. */ + TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL); + + /* Ensure indications disabled. */ + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_1_def_handle); + TEST_ASSERT(flags == 0); + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_2_def_handle); + TEST_ASSERT(flags == 0); +} + +TEST_CASE(ble_gatts_notify_test_bonded_n) +{ + uint16_t conn_handle; + uint16_t flags; + + ble_gatts_notify_test_misc_init(&conn_handle, 1, + BLE_GATTS_CLT_CFG_F_NOTIFY, + BLE_GATTS_CLT_CFG_F_NOTIFY); + + /* Disconnect. */ + ble_gatts_notify_test_disconnect(conn_handle, + BLE_GATTS_CLT_CFG_F_NOTIFY, 0, + BLE_GATTS_CLT_CFG_F_NOTIFY, 0); + + /* Ensure both CCCDs still persisted. */ + TEST_ASSERT(ble_hs_test_util_store_num_cccds == 2); + + /* Update characteristic 1's value. */ + ble_gatts_notify_test_chr_1_len = 1; + ble_gatts_notify_test_chr_1_val[0] = 0xdd; + ble_gatts_chr_updated(ble_gatts_notify_test_chr_1_def_handle + 1); + + /* Update characteristic 2's value. */ + ble_gatts_notify_test_chr_2_len = 16; + memcpy(ble_gatts_notify_test_chr_2_val, + ((uint8_t[]){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}), 16); + ble_gatts_chr_updated(ble_gatts_notify_test_chr_2_def_handle + 1); + + /* Reconnect; ensure notifications don't get sent while unbonded and that + * notifications appear disabled. + */ + + ble_hs_test_util_create_conn(conn_handle, ((uint8_t[]){2,3,4,5,6,7,8,9}), + ble_gatts_notify_test_util_gap_event, NULL); + + ble_gatts_notify_test_num_events = 0; + /* Ensure no notifications sent. */ + TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL); + + /* Ensure notifications disabled. */ + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_1_def_handle); + TEST_ASSERT(flags == 0); + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_2_def_handle); + TEST_ASSERT(flags == 0); + + /* Simulate a successful encryption procedure (bonding restoration). */ + ble_gatts_notify_test_restore_bonding(conn_handle, + BLE_GATTS_CLT_CFG_F_NOTIFY, 1, + BLE_GATTS_CLT_CFG_F_NOTIFY, 1); + + /* Ensure notifications enabled. */ + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_1_def_handle); + TEST_ASSERT(flags == BLE_GATTS_CLT_CFG_F_NOTIFY); + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_2_def_handle); + TEST_ASSERT(flags == BLE_GATTS_CLT_CFG_F_NOTIFY); + + /* Ensure both CCCDs still persisted. */ + TEST_ASSERT(ble_hs_test_util_store_num_cccds == 2); +} + +TEST_CASE(ble_gatts_notify_test_bonded_i) +{ + uint16_t conn_handle; + uint16_t flags; + + ble_gatts_notify_test_misc_init(&conn_handle, 1, + BLE_GATTS_CLT_CFG_F_INDICATE, + BLE_GATTS_CLT_CFG_F_INDICATE); + + /* Disconnect. */ + ble_gatts_notify_test_disconnect(conn_handle, + BLE_GATTS_CLT_CFG_F_INDICATE, 0, + BLE_GATTS_CLT_CFG_F_INDICATE, 0); + + /* Ensure both CCCDs still persisted. */ + TEST_ASSERT(ble_hs_test_util_store_num_cccds == 2); + + /* Update characteristic 1's value. */ + ble_gatts_notify_test_chr_1_len = 1; + ble_gatts_notify_test_chr_1_val[0] = 0xab; + ble_gatts_chr_updated(ble_gatts_notify_test_chr_1_def_handle + 1); + + /* Update characteristic 2's value. */ + ble_gatts_notify_test_chr_2_len = 16; + memcpy(ble_gatts_notify_test_chr_2_val, + ((uint8_t[]){0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}), 16); + ble_gatts_chr_updated(ble_gatts_notify_test_chr_2_def_handle + 1); + + /* Reconnect; ensure notifications don't get sent while unbonded and that + * notifications appear disabled. + */ + + ble_hs_test_util_create_conn(conn_handle, ((uint8_t[]){2,3,4,5,6,7,8,9}), + ble_gatts_notify_test_util_gap_event, NULL); + + /* Ensure no indications sent. */ + TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue() == NULL); + + /* Ensure notifications disabled. */ + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_1_def_handle); + TEST_ASSERT(flags == 0); + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_2_def_handle); + TEST_ASSERT(flags == 0); + + /* Simulate a successful encryption procedure (bonding restoration). */ + ble_gatts_notify_test_restore_bonding(conn_handle, + BLE_GATTS_CLT_CFG_F_INDICATE, 1, + BLE_GATTS_CLT_CFG_F_INDICATE, 0); + + /* Verify the second indication doesn't get sent until the first is + * confirmed. + */ + ble_hs_test_util_tx_all(); + TEST_ASSERT(ble_hs_test_util_prev_tx_queue_sz() == 0); + + /* Receive the confirmation for the first indication. */ + ble_gatts_notify_test_misc_rx_indicate_rsp( + conn_handle, + ble_gatts_notify_test_chr_1_def_handle + 1); + + /* Verify indication sent properly. */ + ble_hs_test_util_tx_all(); + ble_gatts_notify_test_misc_verify_tx_i( + conn_handle, + ble_gatts_notify_test_chr_2_def_handle + 1, + ble_gatts_notify_test_chr_2_val, + ble_gatts_notify_test_chr_2_len); + + /* Receive the confirmation for the second indication. */ + ble_gatts_notify_test_misc_rx_indicate_rsp( + conn_handle, + ble_gatts_notify_test_chr_2_def_handle + 1); + + /* Verify no pending GATT jobs. */ + TEST_ASSERT(!ble_gattc_any_jobs()); + + /* Ensure notifications enabled. */ + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_1_def_handle); + TEST_ASSERT(flags == BLE_GATTS_CLT_CFG_F_INDICATE); + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_2_def_handle); + TEST_ASSERT(flags == BLE_GATTS_CLT_CFG_F_INDICATE); + + /* Ensure both CCCDs still persisted. */ + TEST_ASSERT(ble_hs_test_util_store_num_cccds == 2); +} + +TEST_CASE(ble_gatts_notify_test_bonded_i_no_ack) +{ + struct ble_store_value_cccd value_cccd; + struct ble_store_key_cccd key_cccd; + uint16_t conn_handle; + uint16_t flags; + int rc; + + ble_gatts_notify_test_misc_init(&conn_handle, 1, + BLE_GATTS_CLT_CFG_F_INDICATE, 0); + + /* Update characteristic 1's value. */ + ble_gatts_notify_test_chr_1_len = 1; + ble_gatts_notify_test_chr_1_val[0] = 0xab; + ble_gatts_chr_updated(ble_gatts_notify_test_chr_1_def_handle + 1); + + /* Verify indication sent properly. */ + ble_hs_test_util_tx_all(); + ble_gatts_notify_test_misc_verify_tx_i( + conn_handle, + ble_gatts_notify_test_chr_1_def_handle + 1, + ble_gatts_notify_test_chr_1_val, + ble_gatts_notify_test_chr_1_len); + + /* Verify 'updated' state is still persisted. */ + key_cccd.peer_addr_type = BLE_STORE_ADDR_TYPE_NONE; + key_cccd.chr_val_handle = ble_gatts_notify_test_chr_1_def_handle + 1; + key_cccd.idx = 0; + + rc = ble_store_read_cccd(&key_cccd, &value_cccd); + TEST_ASSERT_FATAL(rc == 0); + TEST_ASSERT(value_cccd.value_changed); + + /* Disconnect. */ + ble_gatts_notify_test_disconnect(conn_handle, + BLE_GATTS_CLT_CFG_F_INDICATE, 1, 0, 0); + + /* Ensure CCCD still persisted. */ + TEST_ASSERT(ble_hs_test_util_store_num_cccds == 1); + + /* Reconnect. */ + ble_hs_test_util_create_conn(conn_handle, ((uint8_t[]){2,3,4,5,6,7,8,9}), + ble_gatts_notify_test_util_gap_event, NULL); + + /* Simulate a successful encryption procedure (bonding restoration). */ + ble_gatts_notify_test_restore_bonding(conn_handle, + BLE_GATTS_CLT_CFG_F_INDICATE, 1, + 0, 0); + + /* Receive the confirmation for the indication. */ + ble_gatts_notify_test_misc_rx_indicate_rsp( + conn_handle, + ble_gatts_notify_test_chr_1_def_handle + 1); + + /* Verify no pending GATT jobs. */ + TEST_ASSERT(!ble_gattc_any_jobs()); + + /* Ensure indication enabled. */ + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_1_def_handle); + TEST_ASSERT(flags == BLE_GATTS_CLT_CFG_F_INDICATE); + flags = ble_gatts_notify_test_misc_read_notify( + conn_handle, ble_gatts_notify_test_chr_2_def_handle); + TEST_ASSERT(flags == 0); + + /* Ensure CCCD still persisted. */ + TEST_ASSERT(ble_hs_test_util_store_num_cccds == 1); + + /* Verify 'updated' state is no longer persisted. */ + rc = ble_store_read_cccd(&key_cccd, &value_cccd); + TEST_ASSERT_FATAL(rc == 0); + TEST_ASSERT(!value_cccd.value_changed); +} + +TEST_SUITE(ble_gatts_notify_suite) +{ + tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL); + + ble_gatts_notify_test_n(); + ble_gatts_notify_test_i(); + + ble_gatts_notify_test_bonded_n(); + ble_gatts_notify_test_bonded_i(); + + ble_gatts_notify_test_bonded_i_no_ack(); + + /* XXX: Test corner cases: + * o Bonding after CCCD configuration. + * o Disconnect prior to rx of indicate ack. + */ +} + +int +ble_gatts_notify_test_all(void) +{ + ble_gatts_notify_suite(); + + return tu_any_failed; +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d98ddc1c/net/nimble/host/test/src/ble_gatts_read_test.c ---------------------------------------------------------------------- diff --git a/net/nimble/host/test/src/ble_gatts_read_test.c b/net/nimble/host/test/src/ble_gatts_read_test.c new file mode 100644 index 0000000..cef9f92 --- /dev/null +++ b/net/nimble/host/test/src/ble_gatts_read_test.c @@ -0,0 +1,261 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <string.h> +#include <errno.h> +#include "testutil/testutil.h" +#include "host/ble_uuid.h" +#include "host/ble_hs_test.h" +#include "ble_hs_test_util.h" + +#define BLE_GATTS_READ_TEST_CHR_1_UUID 0x1111 +#define BLE_GATTS_READ_TEST_CHR_2_UUID 0x2222 + +static uint8_t ble_gatts_read_test_peer_addr[6] = {2,3,4,5,6,7}; + +static int +ble_gatts_read_test_util_access_1(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +ble_gatts_read_test_util_access_2(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); +static void +ble_gatts_read_test_misc_reg_cb(struct ble_gatt_register_ctxt *ctxt, + void *arg); + +static const struct ble_gatt_svc_def ble_gatts_read_test_svcs[] = { { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid128 = BLE_UUID16(0x1234), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid128 = BLE_UUID16(BLE_GATTS_READ_TEST_CHR_1_UUID), + .access_cb = ble_gatts_read_test_util_access_1, + .flags = BLE_GATT_CHR_F_READ + }, { + .uuid128 = BLE_UUID16(BLE_GATTS_READ_TEST_CHR_2_UUID), + .access_cb = ble_gatts_read_test_util_access_2, + .flags = BLE_GATT_CHR_F_READ + }, { + 0 + } }, +}, { + 0 +} }; + + +static uint16_t ble_gatts_read_test_chr_1_def_handle; +static uint16_t ble_gatts_read_test_chr_1_val_handle; +static uint8_t ble_gatts_read_test_chr_1_val[1024]; +static int ble_gatts_read_test_chr_1_len; +static uint16_t ble_gatts_read_test_chr_2_def_handle; +static uint16_t ble_gatts_read_test_chr_2_val_handle; + +static void +ble_gatts_read_test_misc_init(uint16_t *out_conn_handle) +{ + int rc; + + ble_hs_test_util_init(); + + rc = ble_gatts_register_svcs(ble_gatts_read_test_svcs, + ble_gatts_read_test_misc_reg_cb, NULL); + TEST_ASSERT_FATAL(rc == 0); + TEST_ASSERT_FATAL(ble_gatts_read_test_chr_1_def_handle != 0); + TEST_ASSERT_FATAL(ble_gatts_read_test_chr_1_val_handle == + ble_gatts_read_test_chr_1_def_handle + 1); + TEST_ASSERT_FATAL(ble_gatts_read_test_chr_2_def_handle != 0); + TEST_ASSERT_FATAL(ble_gatts_read_test_chr_2_val_handle == + ble_gatts_read_test_chr_2_def_handle + 1); + + ble_gatts_start(); + + ble_hs_test_util_create_conn(2, ble_gatts_read_test_peer_addr, NULL, NULL); + + if (out_conn_handle != NULL) { + *out_conn_handle = 2; + } +} + +static void +ble_gatts_read_test_misc_reg_cb(struct ble_gatt_register_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + + if (ctxt->op == BLE_GATT_REGISTER_OP_CHR) { + uuid16 = ble_uuid_128_to_16(ctxt->chr.chr_def->uuid128); + switch (uuid16) { + case BLE_GATTS_READ_TEST_CHR_1_UUID: + ble_gatts_read_test_chr_1_def_handle = ctxt->chr.def_handle; + ble_gatts_read_test_chr_1_val_handle = ctxt->chr.val_handle; + break; + + case BLE_GATTS_READ_TEST_CHR_2_UUID: + ble_gatts_read_test_chr_2_def_handle = ctxt->chr.def_handle; + ble_gatts_read_test_chr_2_val_handle = ctxt->chr.val_handle; + break; + + default: + TEST_ASSERT_FATAL(0); + break; + } + } +} + +static int +ble_gatts_read_test_util_access_1(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + int rc; + + TEST_ASSERT_FATAL(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + TEST_ASSERT_FATAL(attr_handle == ble_gatts_read_test_chr_1_val_handle); + + TEST_ASSERT(ctxt->chr == + &ble_gatts_read_test_svcs[0].characteristics[0]); + + rc = os_mbuf_append(ctxt->om, ble_gatts_read_test_chr_1_val, + ble_gatts_read_test_chr_1_len); + TEST_ASSERT(rc == 0); + + return 0; +} + +static int +ble_gatts_read_test_util_access_2(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint8_t *buf; + + TEST_ASSERT_FATAL(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + TEST_ASSERT_FATAL(attr_handle == ble_gatts_read_test_chr_2_def_handle + 1); + + TEST_ASSERT(ctxt->chr == + &ble_gatts_read_test_svcs[0].characteristics[1]); + + buf = os_mbuf_extend(ctxt->om, 6); + TEST_ASSERT_FATAL(buf != NULL); + + buf[0] = 0; + buf[1] = 10; + buf[2] = 20; + buf[3] = 30; + buf[4] = 40; + buf[5] = 50; + + return 0; +} + +static void +ble_gatts_read_test_once(uint16_t conn_handle, uint16_t attr_id, + void *expected_value, uint16_t expected_len) +{ + struct ble_att_read_req read_req; + uint8_t buf[BLE_ATT_READ_REQ_SZ]; + int rc; + + read_req.barq_handle = attr_id; + ble_att_read_req_write(buf, sizeof buf, &read_req); + + rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, + buf, sizeof buf); + TEST_ASSERT(rc == 0); + + ble_hs_test_util_verify_tx_read_rsp(expected_value, expected_len); +} + +TEST_CASE(ble_gatts_read_test_case_basic) +{ + uint16_t conn_handle; + + ble_gatts_read_test_misc_init(&conn_handle); + + /*** Application points attribute at static data. */ + ble_gatts_read_test_chr_1_val[0] = 1; + ble_gatts_read_test_chr_1_val[1] = 2; + ble_gatts_read_test_chr_1_val[2] = 3; + ble_gatts_read_test_chr_1_len = 3; + ble_gatts_read_test_once(conn_handle, + ble_gatts_read_test_chr_1_val_handle, + ble_gatts_read_test_chr_1_val, + ble_gatts_read_test_chr_1_len); + + /*** Application uses stack-provided buffer for dynamic attribute. */ + ble_gatts_read_test_once(conn_handle, + ble_gatts_read_test_chr_2_def_handle + 1, + ((uint8_t[6]){0,10,20,30,40,50}), 6); + +} + +TEST_CASE(ble_gatts_read_test_case_long) +{ + struct ble_att_read_blob_req read_blob_req; + struct ble_att_read_req read_req; + uint8_t buf[max(BLE_ATT_READ_REQ_SZ, BLE_ATT_READ_BLOB_REQ_SZ)]; + uint16_t conn_handle; + int rc; + int i; + + ble_gatts_read_test_misc_init(&conn_handle); + + /*** Prepare characteristic value. */ + ble_gatts_read_test_chr_1_len = 40; + for (i = 0; i < ble_gatts_read_test_chr_1_len; i++) { + ble_gatts_read_test_chr_1_val[i] = i; + } + + /* Receive first read request. */ + read_req.barq_handle = ble_gatts_read_test_chr_1_val_handle; + ble_att_read_req_write(buf, sizeof buf, &read_req); + + rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, + buf, sizeof buf); + TEST_ASSERT(rc == 0); + + ble_hs_test_util_verify_tx_read_rsp(ble_gatts_read_test_chr_1_val, 22); + + /* Receive follow-up read blob request. */ + read_blob_req.babq_handle = ble_gatts_read_test_chr_1_val_handle; + read_blob_req.babq_offset = 22; + ble_att_read_blob_req_write(buf, sizeof buf, &read_blob_req); + + rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT, + buf, sizeof buf); + TEST_ASSERT(rc == 0); + + /* Ensure response starts at appropriate offset (22). */ + ble_hs_test_util_verify_tx_read_blob_rsp( + ble_gatts_read_test_chr_1_val + 22, 18); +} + +TEST_SUITE(ble_gatts_read_test_suite) +{ + tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL); + + ble_gatts_read_test_case_basic(); + ble_gatts_read_test_case_long(); +}