/* * 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 #include #include "testutil/testutil.h" #include "nimble/ble.h" #include "host/ble_hs_test.h" #include "host/ble_gatt.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_EXECUTE); /* Receive Exec Write response. */ 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_EXECUTE); /* Receive Exec Write response. */ 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. */ /* 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. */ /* 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. */ 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. */ 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(BLE_ATT_EXEC_WRITE_F_CANCEL); } TEST_CASE(ble_gatt_write_test_long_oom) { static const struct ble_hs_test_util_flat_attr attr = { .handle = 34, .value = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, }, .value_len = 20, }; struct os_mbuf *oms; int32_t ticks_until; int chunk_sz; int off; 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); /* Initiate a write long procedure. */ off = 0; rc = ble_hs_test_util_gatt_write_long_flat( 2, attr.handle, attr.value, attr.value_len, ble_gatt_write_test_cb_good, NULL); TEST_ASSERT_FATAL(rc == 0); chunk_sz = ble_att_mtu(2) - BLE_ATT_PREP_WRITE_CMD_BASE_SZ; ble_hs_test_util_verify_tx_prep_write(attr.handle, off, attr.value + off, chunk_sz); /* Exhaust the msys pool. Leave one mbuf for the forthcoming response. */ oms = ble_hs_test_util_mbuf_alloc_all_but(1); ble_gatt_write_test_rx_prep_rsp(2, attr.handle, off, attr.value + off, chunk_sz); off += chunk_sz; /* Ensure no follow-up request got sent. It should not have gotten sent * due to mbuf exhaustion. */ ble_hs_test_util_prev_tx_queue_clear(); TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue_pullup() == NULL); /* Verify that we will resume the stalled GATT procedure in one second. */ ticks_until = ble_gattc_timer(); TEST_ASSERT(ticks_until == os_time_ms_to_ticks32(MYNEWT_VAL(BLE_GATT_RESUME_RATE))); /* Verify the procedure proceeds after mbufs become available. */ rc = os_mbuf_free_chain(oms); TEST_ASSERT_FATAL(rc == 0); os_time_advance(ticks_until); ble_gattc_timer(); chunk_sz = attr.value_len - off; ble_hs_test_util_verify_tx_prep_write(attr.handle, off, attr.value + off, chunk_sz); /* Exhaust the msys pool. Leave one mbuf for the forthcoming response. */ oms = ble_hs_test_util_mbuf_alloc_all_but(1); ble_gatt_write_test_rx_prep_rsp( 2, attr.handle, off, attr.value + off, chunk_sz); off += chunk_sz; /* Ensure no follow-up request got sent. It should not have gotten sent * due to mbuf exhaustion. */ ble_hs_test_util_prev_tx_queue_clear(); TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue_pullup() == NULL); /* Verify that we will resume the stalled GATT procedure in one second. */ ticks_until = ble_gattc_timer(); TEST_ASSERT(ticks_until == os_time_ms_to_ticks32(MYNEWT_VAL(BLE_GATT_RESUME_RATE))); /* Verify that procedure completes when mbufs are available. */ rc = os_mbuf_free_chain(oms); TEST_ASSERT_FATAL(rc == 0); os_time_advance(ticks_until); ble_gattc_timer(); /* Verify execute write request sent. */ ble_hs_test_util_verify_tx_exec_write(BLE_ATT_EXEC_WRITE_F_EXECUTE); /* Receive Exec Write response. */ ble_gatt_write_test_rx_exec_rsp(2); /* Verify callback got called. */ TEST_ASSERT(ble_gatt_write_test_cb_called); TEST_ASSERT(!ble_gattc_any_jobs()); } TEST_CASE(ble_gatt_write_test_reliable_oom) { static const struct ble_hs_test_util_flat_attr attr = { .handle = 34, .value = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, }, .value_len = 20, }; struct ble_gatt_attr mattr; struct os_mbuf *oms; int32_t ticks_until; int chunk_sz; int off; 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); /* Initiate a write reliable procedure. */ ble_hs_test_util_attr_from_flat(&mattr, &attr); off = 0; rc = ble_gattc_write_reliable(2, &mattr, 1, ble_gatt_write_test_reliable_cb_good, NULL); TEST_ASSERT_FATAL(rc == 0); chunk_sz = ble_att_mtu(2) - BLE_ATT_PREP_WRITE_CMD_BASE_SZ; ble_hs_test_util_verify_tx_prep_write(attr.handle, off, attr.value + off, chunk_sz); /* Exhaust the msys pool. Leave one mbuf for the forthcoming response. */ oms = ble_hs_test_util_mbuf_alloc_all_but(1); ble_gatt_write_test_rx_prep_rsp(2, attr.handle, off, attr.value + off, chunk_sz); off += chunk_sz; /* Ensure no follow-up request got sent. It should not have gotten sent * due to mbuf exhaustion. */ ble_hs_test_util_prev_tx_queue_clear(); TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue_pullup() == NULL); /* Verify that we will resume the stalled GATT procedure in one second. */ ticks_until = ble_gattc_timer(); TEST_ASSERT(ticks_until == os_time_ms_to_ticks32(MYNEWT_VAL(BLE_GATT_RESUME_RATE))); /* Verify the procedure proceeds after mbufs become available. */ rc = os_mbuf_free_chain(oms); TEST_ASSERT_FATAL(rc == 0); os_time_advance(ticks_until); ble_gattc_timer(); chunk_sz = attr.value_len - off; ble_hs_test_util_verify_tx_prep_write(attr.handle, off, attr.value + off, chunk_sz); /* Exhaust the msys pool. Leave one mbuf for the forthcoming response. */ oms = ble_hs_test_util_mbuf_alloc_all_but(1); ble_gatt_write_test_rx_prep_rsp( 2, attr.handle, off, attr.value + off, chunk_sz); off += chunk_sz; /* Ensure no follow-up request got sent. It should not have gotten sent * due to mbuf exhaustion. */ ble_hs_test_util_prev_tx_queue_clear(); TEST_ASSERT(ble_hs_test_util_prev_tx_dequeue_pullup() == NULL); /* Verify that we will resume the stalled GATT procedure in one second. */ ticks_until = ble_gattc_timer(); TEST_ASSERT(ticks_until == os_time_ms_to_ticks32(MYNEWT_VAL(BLE_GATT_RESUME_RATE))); /* Verify that procedure completes when mbufs are available. */ rc = os_mbuf_free_chain(oms); TEST_ASSERT_FATAL(rc == 0); os_time_advance(ticks_until); ble_gattc_timer(); /* Verify execute write request sent. */ ble_hs_test_util_verify_tx_exec_write(BLE_ATT_EXEC_WRITE_F_EXECUTE); /* Receive Exec Write response. */ ble_gatt_write_test_rx_exec_rsp(2); /* Verify callback got called. */ TEST_ASSERT(ble_gatt_write_test_cb_called); TEST_ASSERT(!ble_gattc_any_jobs()); } 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(); ble_gatt_write_test_long_oom(); ble_gatt_write_test_reliable_oom(); } int ble_gatt_write_test_all(void) { ble_gatt_write_test_suite(); return tu_any_failed; }