Bluetooth: Host: Add more tests for GATT client becoming change-aware

For clients with both a single and multiple ATT channels:
 - Read a characteristic before reading the DB hash and then retrying
 - Read the DB hash and then do the reads
 - Retry the reads without reading the DB hash

Signed-off-by: Herman Berget <herman.berget@nordicsemi.no>
This commit is contained in:
Herman Berget 2022-04-07 15:51:47 +02:00 committed by Carles Cufí
commit 23d4788afa
11 changed files with 308 additions and 46 deletions

View file

@ -10,6 +10,7 @@ CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
CONFIG_BT_L2CAP_ECRED=y
CONFIG_BT_EATT=y
CONFIG_BT_EATT_MAX=1
CONFIG_BT_EATT_AUTO_CONNECT=n
CONFIG_ASSERT=y
CONFIG_BT_TESTING=y

View file

@ -27,7 +27,7 @@
extern enum bst_result_t bst_result;
#define WAIT_TIME (30 * 1e6) /*seconds*/
#define WAIT_TIME (60 * 1e6) /*seconds*/
#define CREATE_FLAG(flag) static atomic_t flag = (atomic_t)false
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)true)

View file

@ -176,12 +176,7 @@ static void gatt_discover(const struct bt_uuid *uuid, uint8_t type)
WAIT_FOR_FLAG(flag_discover_complete);
printk("Discover complete\n");
}
static uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_read_params *params,
const void *data, uint16_t length);
static struct bt_gatt_read_params chan_1_read = {
.func = gatt_read_cb,
.handle_count = 1,
.single = {
.handle = 0, /* Will be set later */
@ -189,7 +184,6 @@ static struct bt_gatt_read_params chan_1_read = {
},
};
static struct bt_gatt_read_params chan_2_read = {
.func = gatt_read_cb,
.handle_count = 1,
.single = {
.handle = 0, /* Will be set later */
@ -197,7 +191,6 @@ static struct bt_gatt_read_params chan_2_read = {
},
};
static struct bt_gatt_read_params db_hash_read = {
.func = gatt_read_cb,
.handle_count = 0,
.by_uuid = {
.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE,
@ -213,29 +206,54 @@ void expect_status(uint8_t err, uint8_t status)
}
}
static uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_read_params *params,
const void *data, uint16_t length)
static uint8_t gatt_read_expect_success_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params, const void *data,
uint16_t length)
{
printk("GATT read cb: err 0x%02X\n", err);
expect_status(err, BT_ATT_ERR_SUCCESS);
if (params == &db_hash_read) {
expect_status(err, BT_ATT_ERR_SUCCESS);
SET_FLAG(flag_db_hash_read);
} else if (params == &chan_1_read) {
if (flag_db_hash_read) {
expect_status(err, BT_ATT_ERR_SUCCESS);
} else {
expect_status(err, BT_ATT_ERR_DB_OUT_OF_SYNC);
}
SET_FLAG(flag_chan_1_read);
} else if (params == &chan_2_read) {
if (flag_db_hash_read) {
expect_status(err, BT_ATT_ERR_SUCCESS);
} else {
expect_status(err, BT_ATT_ERR_DB_OUT_OF_SYNC);
}
SET_FLAG(flag_chan_2_read);
} else {
FAIL("Unexpected params\n");
}
return 0;
}
static uint8_t gatt_read_expect_err_unlikely_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
printk("GATT read cb: err 0x%02X\n", err);
expect_status(err, BT_ATT_ERR_UNLIKELY);
if (params == &chan_1_read) {
SET_FLAG(flag_chan_1_read);
} else if (params == &chan_2_read) {
SET_FLAG(flag_chan_2_read);
} else {
FAIL("Unexpected params\n");
}
return 0;
}
static uint8_t gatt_read_expect_err_out_of_sync_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
printk("GATT read cb: err 0x%02X\n", err);
expect_status(err, BT_ATT_ERR_DB_OUT_OF_SYNC);
if (params == &chan_1_read) {
SET_FLAG(flag_chan_1_read);
} else if (params == &chan_2_read) {
SET_FLAG(flag_chan_2_read);
} else {
FAIL("Unexpected params\n");
@ -294,7 +312,7 @@ static void enable_robust_caching(void)
printk("Success\n");
}
static void test_main(void)
static void test_main_common(bool connect_eatt)
{
int err;
@ -326,9 +344,11 @@ static void test_main(void)
enable_robust_caching();
while (bt_eatt_count(g_conn) < 1) {
/* Wait for EATT channel to connect, in case it hasn't already */
k_sleep(K_MSEC(10));
if (connect_eatt) {
while (bt_eatt_count(g_conn) < 1) {
/* Wait for EATT channel to connect, in case it hasn't already */
k_sleep(K_MSEC(10));
}
}
/* Tell the server to register additional service */
@ -339,7 +359,39 @@ static void test_main(void)
chan_1_read.single.handle = chrc_handle;
chan_2_read.single.handle = chrc_handle;
}
static void test_main_db_hash_read_eatt(void)
{
test_main_common(true);
/* Read the DB hash to become change-aware */
db_hash_read.func = gatt_read_expect_success_cb;
gatt_read(&db_hash_read);
WAIT_FOR_FLAG(flag_db_hash_read);
/* These shall now succeed */
chan_1_read.func = gatt_read_expect_success_cb;
chan_2_read.func = gatt_read_expect_success_cb;
UNSET_FLAG(flag_chan_1_read);
UNSET_FLAG(flag_chan_2_read);
gatt_read(&chan_1_read);
gatt_read(&chan_2_read);
WAIT_FOR_FLAG(flag_chan_1_read);
WAIT_FOR_FLAG(flag_chan_2_read);
/* Signal to server that reads are done */
backchannel_sync_send();
PASS("GATT client Passed\n");
}
static void test_main_out_of_sync_eatt(void)
{
test_main_common(true);
chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
chan_2_read.func = gatt_read_expect_err_out_of_sync_cb;
gatt_read(&chan_1_read);
gatt_read(&chan_2_read);
@ -350,9 +402,14 @@ static void test_main(void)
WAIT_FOR_FLAG(flag_chan_1_read);
WAIT_FOR_FLAG(flag_chan_2_read);
/* Read the DB hash to become change-aware */
db_hash_read.func = gatt_read_expect_success_cb;
gatt_read(&db_hash_read);
WAIT_FOR_FLAG(flag_db_hash_read);
/* These shall now succeed */
chan_1_read.func = gatt_read_expect_success_cb;
chan_2_read.func = gatt_read_expect_success_cb;
UNSET_FLAG(flag_chan_1_read);
UNSET_FLAG(flag_chan_2_read);
gatt_read(&chan_1_read);
@ -360,15 +417,146 @@ static void test_main(void)
WAIT_FOR_FLAG(flag_chan_1_read);
WAIT_FOR_FLAG(flag_chan_2_read);
/* Signal to server that reads are done */
backchannel_sync_send();
PASS("GATT client Passed\n");
}
static void test_main_retry_reads_eatt(void)
{
test_main_common(true);
chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
chan_2_read.func = gatt_read_expect_err_out_of_sync_cb;
gatt_read(&chan_1_read);
gatt_read(&chan_2_read);
/* Wait until received response on both reads. When robust caching is implemented
* on the client side, the waiting shall be done automatically by the host when
* reading the DB hash.
*/
WAIT_FOR_FLAG(flag_chan_1_read);
WAIT_FOR_FLAG(flag_chan_2_read);
/* Retry the reads, these shall time out */
chan_1_read.func = gatt_read_expect_err_unlikely_cb;
chan_2_read.func = gatt_read_expect_err_unlikely_cb;
UNSET_FLAG(flag_chan_1_read);
UNSET_FLAG(flag_chan_2_read);
gatt_read(&chan_1_read);
gatt_read(&chan_2_read);
WAIT_FOR_FLAG(flag_chan_1_read);
WAIT_FOR_FLAG(flag_chan_2_read);
/* Signal to server that reads are done */
backchannel_sync_send();
PASS("GATT client Passed\n");
}
static void test_main_db_hash_read_no_eatt(void)
{
test_main_common(false);
/* Read the DB hash to become change-aware */
db_hash_read.func = gatt_read_expect_success_cb;
gatt_read(&db_hash_read);
WAIT_FOR_FLAG(flag_db_hash_read);
/* Read shall now succeed */
chan_1_read.func = gatt_read_expect_success_cb;
UNSET_FLAG(flag_chan_1_read);
gatt_read(&chan_1_read);
WAIT_FOR_FLAG(flag_chan_1_read);
/* Signal to server that reads are done */
backchannel_sync_send();
PASS("GATT client Passed\n");
}
static void test_main_out_of_sync_no_eatt(void)
{
test_main_common(false);
chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
gatt_read(&chan_1_read);
WAIT_FOR_FLAG(flag_chan_1_read);
/* Read the DB hash to become change-aware */
db_hash_read.func = gatt_read_expect_success_cb;
gatt_read(&db_hash_read);
WAIT_FOR_FLAG(flag_db_hash_read);
/* Read shall now succeed */
chan_1_read.func = gatt_read_expect_success_cb;
UNSET_FLAG(flag_chan_1_read);
gatt_read(&chan_1_read);
WAIT_FOR_FLAG(flag_chan_1_read);
/* Signal to server that reads are done */
backchannel_sync_send();
PASS("GATT client Passed\n");
}
static void test_main_retry_reads_no_eatt(void)
{
test_main_common(false);
chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
gatt_read(&chan_1_read);
WAIT_FOR_FLAG(flag_chan_1_read);
/* Read again to become change-aware */
chan_1_read.func = gatt_read_expect_success_cb;
UNSET_FLAG(flag_chan_1_read);
gatt_read(&chan_1_read);
WAIT_FOR_FLAG(flag_chan_1_read);
/* Signal to server that reads are done */
backchannel_sync_send();
PASS("GATT client Passed\n");
}
static const struct bst_test_instance test_vcs[] = {
{
.test_id = "gatt_client",
.test_id = "gatt_client_db_hash_read_eatt",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main,
.test_main_f = test_main_db_hash_read_eatt,
},
{
.test_id = "gatt_client_out_of_sync_eatt",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main_out_of_sync_eatt,
},
{
.test_id = "gatt_client_retry_reads_eatt",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main_retry_reads_eatt,
},
{
.test_id = "gatt_client_db_hash_read_no_eatt",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main_db_hash_read_no_eatt,
},
{
.test_id = "gatt_client_out_of_sync_no_eatt",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main_out_of_sync_no_eatt,
},
{
.test_id = "gatt_client_retry_reads_no_eatt",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main_retry_reads_no_eatt,
},
BSTEST_END_MARKER,
};

View file

@ -9,7 +9,6 @@
extern enum bst_result_t bst_result;
CREATE_FLAG(flag_is_connected);
CREATE_FLAG(flag_read_done);
static struct bt_conn *g_conn;
@ -61,7 +60,6 @@ static ssize_t read_test_chrc(struct bt_conn *conn, const struct bt_gatt_attr *a
uint16_t len, uint16_t offset)
{
printk("Characteristic read\n");
SET_FLAG(flag_read_done);
return bt_gatt_attr_read(conn, attr, buf, len, offset, (const void *)chrc_data, CHRC_SIZE);
}
@ -76,7 +74,7 @@ static struct bt_gatt_attr additional_attributes[] = {
static struct bt_gatt_service additional_gatt_service = BT_GATT_SERVICE(additional_attributes);
static void test_main(void)
static void test_main_common(bool connect_eatt)
{
int err;
const struct bt_data ad[] = { BT_DATA_BYTES(BT_DATA_FLAGS,
@ -104,6 +102,15 @@ static void test_main(void)
WAIT_FOR_FLAG(flag_is_connected);
if (connect_eatt) {
err = bt_eatt_connect(g_conn, CONFIG_BT_EATT_MAX);
if (err) {
FAIL("Failed to connect EATT channels (err %d)\n", err);
return;
}
}
/* Wait for client to do discovery and configuration */
backchannel_sync_wait();
@ -116,17 +123,34 @@ static void test_main(void)
/* Signal to client that additional service is registered */
backchannel_sync_send();
WAIT_FOR_FLAG(flag_read_done);
/* Wait for client to be done reading */
backchannel_sync_wait();
PASS("GATT server passed\n");
}
static void test_main_eatt(void)
{
test_main_common(true);
}
static void test_main_no_eatt(void)
{
test_main_common(false);
}
static const struct bst_test_instance test_gatt_server[] = {
{
.test_id = "gatt_server",
.test_id = "gatt_server_eatt",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main,
.test_main_f = test_main_eatt,
},
{
.test_id = "gatt_server_no_eatt",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main_no_eatt,
},
BSTEST_END_MARKER,
};

View file

@ -2,17 +2,18 @@
# Copyright 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
simulation_id="gatt_change_aware"
verbosity_level=2
process_ids=""; exit_code=0
process_ids=""
exit_code=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
function Execute() {
if [ ! -f $1 ]; then
echo -e " \e[91m$(pwd)/$(basename $1) cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 120 $@ & process_ids="$process_ids $!"
exit 1
fi
timeout 120 $@ &
process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
@ -23,15 +24,15 @@ BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD}_tests_bluetooth_bsim_bt_bsim_test_gatt_caching_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=gatt_client
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=${client_id}
Execute ./bs_${BOARD}_tests_bluetooth_bsim_bt_bsim_test_gatt_caching_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=gatt_server
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=${server_id}
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=2 -sim_length=60e6 $@
-D=2 -sim_length=60e6 $@
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
simulation_id="gatt_caching_db_hash_read_eatt" \
client_id="gatt_client_db_hash_read_eatt" \
server_id="gatt_server_eatt" \
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
simulation_id="gatt_caching_db_hash_read_no_eatt" \
client_id="gatt_client_db_hash_read_no_eatt" \
server_id="gatt_server_no_eatt" \
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
simulation_id="gatt_caching_out_of_sync_eatt" \
client_id="gatt_client_out_of_sync_eatt" \
server_id="gatt_server_eatt" \
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
simulation_id="gatt_caching_out_of_sync_no_eatt" \
client_id="gatt_client_out_of_sync_no_eatt" \
server_id="gatt_server_no_eatt" \
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
simulation_id="gatt_caching_retry_reads_eatt" \
client_id="gatt_client_retry_reads_eatt" \
server_id="gatt_server_eatt" \
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
simulation_id="gatt_caching_retry_reads_no_eatt" \
client_id="gatt_client_retry_reads_no_eatt" \
server_id="gatt_server_no_eatt" \
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh