You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2016/09/13 03:42:03 UTC

[10/41] incubator-mynewt-core git commit: syscfg / sysinit

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();
+}