/* * Copyright (c) 2022 Nordic Semiconductor * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include "mesh_test.h" #include "mesh/net.h" #include "mesh/mesh.h" #include "mesh/foundation.h" #include "gatt_common.h" #define LOG_MODULE_NAME test_adv #include LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_INF); #define WAIT_TIME 60 /*seconds*/ extern const struct bt_mesh_comp comp; static uint8_t test_prov_uuid[16] = { 0x6c, 0x69, 0x6e, 0x67, 0x61, 0xaa }; static const struct bt_mesh_test_cfg adv_cfg = { .addr = 0x0001, .dev_key = { 0x01 }, }; static struct bt_mesh_send_cb send_cb; static struct bt_mesh_test_adv xmit_param; static const char txt_msg[] = "adv test"; static const char cb_msg[] = "cb test"; static int64_t tx_timestamp; static int seq_checker; static struct bt_mesh_test_gatt gatt_param; static int num_adv_sent; static uint8_t previous_checker = 0xff; static K_SEM_DEFINE(observer_sem, 0, 1); static void test_tx_init(void) { bt_mesh_test_cfg_set(NULL, WAIT_TIME); } static void test_rx_init(void) { bt_mesh_test_cfg_set(NULL, WAIT_TIME); } static void bt_init(void) { ASSERT_OK_MSG(bt_enable(NULL), "Bluetooth init failed"); LOG_INF("Bluetooth initialized"); } static void adv_init(void) { bt_mesh_adv_init(); ASSERT_OK_MSG(bt_mesh_adv_enable(), "Mesh adv init failed"); } static void allocate_all_array(struct bt_mesh_adv **adv, size_t num_adv, uint8_t xmit) { for (int i = 0; i < num_adv; i++) { *adv = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, xmit, K_NO_WAIT); ASSERT_FALSE_MSG(!*adv, "Out of advs\n"); adv++; } } static void verify_adv_queue_overflow(void) { struct bt_mesh_adv *dummy_adv; /* Verity Queue overflow */ dummy_adv = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, BT_MESH_TRANSMIT(2, 20), K_NO_WAIT); ASSERT_TRUE_MSG(!dummy_adv, "Unexpected extra adv\n"); } static bool check_delta_time(uint8_t transmit, uint64_t interval) { static int cnt; static int64_t timestamp; if (cnt) { int64_t delta = k_uptime_delta(×tamp); LOG_INF("rx: cnt(%d) delta(%dms) interval(%ums)", cnt, (int32_t)delta, (uint32_t)interval); ASSERT_TRUE(delta >= (interval - 5) && delta < (interval + 15)); } else { timestamp = k_uptime_get(); LOG_INF("rx: cnt(%d) delta(0ms)", cnt); } cnt++; if (cnt >= transmit) { cnt = 0; timestamp = 0; return true; } return false; } static void single_start_cb(uint16_t duration, int err, void *cb_data) { int64_t delta; delta = k_uptime_delta(&tx_timestamp); LOG_INF("tx start: +%d ms", delta); ASSERT_TRUE(duration >= 90 && duration <= 200); ASSERT_EQUAL(0, err); ASSERT_EQUAL(cb_msg, cb_data); ASSERT_EQUAL(0, seq_checker & 1); seq_checker++; } static void single_end_cb(int err, void *cb_data) { int64_t delta; delta = k_uptime_delta(&tx_timestamp); LOG_INF("tx end: +%d ms", delta); ASSERT_EQUAL(0, err); ASSERT_EQUAL(cb_msg, cb_data); ASSERT_EQUAL(1, seq_checker & 1); seq_checker++; k_sem_give(&observer_sem); } static void realloc_end_cb(int err, void *cb_data) { struct bt_mesh_adv *adv = (struct bt_mesh_adv *)cb_data; ASSERT_EQUAL(0, err); adv = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, BT_MESH_TRANSMIT(2, 20), K_NO_WAIT); ASSERT_FALSE_MSG(!adv, "Out of advs\n"); k_sem_give(&observer_sem); } static void seq_start_cb(uint16_t duration, int err, void *cb_data) { ASSERT_EQUAL(0, err); ASSERT_EQUAL(seq_checker, (intptr_t)cb_data); } static void seq_end_cb(int err, void *cb_data) { ASSERT_EQUAL(0, err); ASSERT_EQUAL(seq_checker, (intptr_t)cb_data); seq_checker++; if (seq_checker == CONFIG_BT_MESH_ADV_BUF_COUNT) { k_sem_give(&observer_sem); } } static void gatt_scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf) { if (adv_type != BT_GAP_ADV_TYPE_ADV_IND) { return; } bt_mesh_test_parse_mesh_gatt_preamble(buf); if (gatt_param.service == MESH_SERVICE_PROVISIONING) { bt_mesh_test_parse_mesh_pb_gatt_service(buf); } else { bt_mesh_test_parse_mesh_proxy_service(buf); } LOG_INF("rx: %s", txt_msg); if (check_delta_time(gatt_param.transmits, gatt_param.interval)) { LOG_INF("rx completed. stop observer."); k_sem_give(&observer_sem); } } static void xmit_scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf) { uint8_t length; if (adv_type != BT_GAP_ADV_TYPE_ADV_NONCONN_IND) { return; } length = net_buf_simple_pull_u8(buf); ASSERT_EQUAL(buf->len, length); ASSERT_EQUAL(length, sizeof(uint8_t) + sizeof(txt_msg)); ASSERT_EQUAL(BT_DATA_MESH_MESSAGE, net_buf_simple_pull_u8(buf)); char *data = net_buf_simple_pull_mem(buf, sizeof(txt_msg)); LOG_INF("rx: %s", txt_msg); ASSERT_EQUAL(0, memcmp(txt_msg, data, sizeof(txt_msg))); /* Add 1 initial transmit to the retransmit. */ if (check_delta_time(xmit_param.retr + 1, xmit_param.interval)) { LOG_INF("rx completed. stop observer."); k_sem_give(&observer_sem); } } static void send_order_start_cb(uint16_t duration, int err, void *user_data) { struct bt_mesh_adv *adv = (struct bt_mesh_adv *)user_data; ASSERT_OK_MSG(err, "Failed adv start cb err (%d)", err); ASSERT_EQUAL(2, adv->b.len); uint8_t current = adv->b.data[0]; uint8_t previous = adv->b.data[1]; LOG_INF("tx start: current(%d) previous(%d)", current, previous); ASSERT_EQUAL(previous_checker, previous); previous_checker = current; } static void send_order_end_cb(int err, void *user_data) { ASSERT_OK_MSG(err, "Failed adv start cb err (%d)", err); seq_checker++; LOG_INF("tx end: seq(%d)", seq_checker); if (seq_checker == num_adv_sent) { seq_checker = 0; previous_checker = 0xff; k_sem_give(&observer_sem); } } static void receive_order_scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf) { uint8_t length; uint8_t current; uint8_t previous; length = net_buf_simple_pull_u8(buf); ASSERT_EQUAL(buf->len, length); ASSERT_EQUAL(BT_DATA_MESH_MESSAGE, net_buf_simple_pull_u8(buf)); current = net_buf_simple_pull_u8(buf); previous = net_buf_simple_pull_u8(buf); LOG_INF("rx: current(%d) previous(%d)", current, previous); ASSERT_EQUAL(previous_checker, previous); /* Add 1 initial transmit to the retransmit. */ if (check_delta_time(xmit_param.retr + 1, xmit_param.interval)) { previous_checker = current; k_sem_give(&observer_sem); } } static void receive_order(int expect_adv) { previous_checker = 0xff; for (int i = 0; i < expect_adv; i++) { ASSERT_OK(bt_mesh_test_wait_for_packet(receive_order_scan_cb, &observer_sem, 10)); } } static void send_adv_buf(struct bt_mesh_adv *adv, uint8_t curr, uint8_t prev) { send_cb.start = send_order_start_cb; send_cb.end = send_order_end_cb; (void)net_buf_simple_add_u8(&adv->b, curr); (void)net_buf_simple_add_u8(&adv->b, prev); bt_mesh_adv_send(adv, &send_cb, adv); bt_mesh_adv_unref(adv); } static void send_adv_array(struct bt_mesh_adv **adv, size_t num_buf, bool reverse) { uint8_t previous; int i; num_adv_sent = num_buf; previous = 0xff; if (!reverse) { i = 0; } else { i = num_buf - 1; } while ((!reverse && i < num_buf) || (reverse && i >= 0)) { send_adv_buf(*adv, (uint8_t)i, previous); previous = (uint8_t)i; if (!reverse) { adv++; i++; } else { adv--; i--; } } } static void test_tx_cb_single(void) { struct bt_mesh_adv *adv; int err; bt_init(); adv_init(); adv = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, BT_MESH_TRANSMIT(2, 20), K_NO_WAIT); ASSERT_FALSE_MSG(!adv, "Out of advs\n"); send_cb.start = single_start_cb; send_cb.end = single_end_cb; net_buf_simple_add_mem(&adv->b, txt_msg, sizeof(txt_msg)); seq_checker = 0; tx_timestamp = k_uptime_get(); bt_mesh_adv_send(adv, &send_cb, (void *)cb_msg); bt_mesh_adv_unref(adv); err = k_sem_take(&observer_sem, K_SECONDS(1)); ASSERT_OK_MSG(err, "Didn't call end tx cb."); PASS(); } static void test_rx_xmit(void) { xmit_param.retr = 2; xmit_param.interval = 20; bt_init(); ASSERT_OK(bt_mesh_test_wait_for_packet(xmit_scan_cb, &observer_sem, 20)); PASS(); } static void test_tx_cb_multi(void) { struct bt_mesh_adv *adv[CONFIG_BT_MESH_ADV_BUF_COUNT]; int err; bt_init(); adv_init(); /* Allocate all network advs. */ allocate_all_array(adv, ARRAY_SIZE(adv), BT_MESH_TRANSMIT(2, 20)); /* Start single adv to reallocate one network adv in callback. * Check that the adv is freed before cb is triggered. */ send_cb.start = NULL; send_cb.end = realloc_end_cb; net_buf_simple_add_mem(&(adv[0]->b), txt_msg, sizeof(txt_msg)); bt_mesh_adv_send(adv[0], &send_cb, adv[0]); bt_mesh_adv_unref(adv[0]); err = k_sem_take(&observer_sem, K_SECONDS(1)); ASSERT_OK_MSG(err, "Didn't call the end tx cb that reallocates adv one more time."); /* Start multi advs to check that all advs are sent and cbs are triggered. */ send_cb.start = seq_start_cb; send_cb.end = seq_end_cb; seq_checker = 0; for (int i = 0; i < CONFIG_BT_MESH_ADV_BUF_COUNT; i++) { net_buf_simple_add_le32(&(adv[i]->b), i); bt_mesh_adv_send(adv[i], &send_cb, (void *)(intptr_t)i); bt_mesh_adv_unref(adv[i]); } err = k_sem_take(&observer_sem, K_SECONDS(10)); ASSERT_OK_MSG(err, "Didn't call the last end tx cb."); PASS(); } static void test_tx_proxy_mixin(void) { static struct bt_mesh_prov prov = { .uuid = test_prov_uuid, }; uint8_t status; int err; /* Initialize mesh stack and enable pb gatt bearer to emit beacons. */ bt_mesh_device_setup(&prov, &comp); err = bt_mesh_prov_enable(BT_MESH_PROV_GATT); ASSERT_OK_MSG(err, "Failed to enable GATT provisioner"); /* Let the tester to measure an interval between advertisements. * The node should advertise pb gatt service with 100 msec interval. */ k_sleep(K_MSEC(1800)); LOG_INF("Provision device under test"); /* Provision dut and start gatt proxy beacons. */ bt_mesh_provision(test_net_key, 0, 0, 0, adv_cfg.addr, adv_cfg.dev_key); /* Disable secured network beacons to exclude influence of them on proxy beaconing. */ ASSERT_OK(bt_mesh_cfg_cli_beacon_set(0, adv_cfg.addr, BT_MESH_BEACON_DISABLED, &status)); ASSERT_EQUAL(BT_MESH_BEACON_DISABLED, status); /* Let the tester to measure an interval between advertisements. * The node should advertise proxy service with 1 second interval. */ k_sleep(K_MSEC(6000)); /* Send a mesh message while advertising proxy service. * Advertising the proxy service should be resumed after * finishing advertising the message. */ struct bt_mesh_adv *adv = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, BT_MESH_TRANSMIT(5, 20), K_NO_WAIT); net_buf_simple_add_mem(&adv->b, txt_msg, sizeof(txt_msg)); bt_mesh_adv_send(adv, NULL, NULL); k_sleep(K_MSEC(150)); /* Let the tester to measure an interval between advertisements again. */ k_sleep(K_MSEC(6000)); PASS(); } static void test_rx_proxy_mixin(void) { /* (total transmit duration) / (transmit interval) */ gatt_param.transmits = 1500 / 100; gatt_param.interval = 100; gatt_param.service = MESH_SERVICE_PROVISIONING; bt_init(); /* Scan pb gatt beacons. */ ASSERT_OK(bt_mesh_test_wait_for_packet(gatt_scan_cb, &observer_sem, 20)); /* Delay to provision dut */ k_sleep(K_MSEC(1000)); /* Scan proxy beacons. */ /* (total transmit duration) / (transmit interval) */ gatt_param.transmits = 5000 / 1000; gatt_param.interval = 1000; gatt_param.service = MESH_SERVICE_PROXY; ASSERT_OK(bt_mesh_test_wait_for_packet(gatt_scan_cb, &observer_sem, 20)); /* Scan adv data. */ xmit_param.retr = 5; xmit_param.interval = 20; ASSERT_OK(bt_mesh_test_wait_for_packet(xmit_scan_cb, &observer_sem, 20)); /* Scan proxy beacons again. */ ASSERT_OK(bt_mesh_test_wait_for_packet(gatt_scan_cb, &observer_sem, 20)); PASS(); } static void test_tx_send_order(void) { struct bt_mesh_adv *adv[CONFIG_BT_MESH_ADV_BUF_COUNT]; uint8_t xmit = BT_MESH_TRANSMIT(2, 20); bt_init(); adv_init(); /* Verify sending order */ allocate_all_array(adv, ARRAY_SIZE(adv), xmit); verify_adv_queue_overflow(); send_adv_array(&adv[0], ARRAY_SIZE(adv), false); /* Wait for no message receive window to end. */ ASSERT_OK_MSG(k_sem_take(&observer_sem, K_SECONDS(10)), "Didn't call the last end tx cb."); /* Verify adv allocation/deallocation after sending */ allocate_all_array(adv, ARRAY_SIZE(adv), xmit); verify_adv_queue_overflow(); for (int i = 0; i < CONFIG_BT_MESH_ADV_BUF_COUNT; i++) { bt_mesh_adv_unref(adv[i]); adv[i] = NULL; } /* Check that it possible to add just one net adv. */ allocate_all_array(adv, 1, xmit); PASS(); } static void test_tx_reverse_order(void) { struct bt_mesh_adv *adv[CONFIG_BT_MESH_ADV_BUF_COUNT]; uint8_t xmit = BT_MESH_TRANSMIT(2, 20); bt_init(); adv_init(); /* Verify reversed sending order */ allocate_all_array(adv, ARRAY_SIZE(adv), xmit); send_adv_array(&adv[CONFIG_BT_MESH_ADV_BUF_COUNT - 1], ARRAY_SIZE(adv), true); /* Wait for no message receive window to end. */ ASSERT_OK_MSG(k_sem_take(&observer_sem, K_SECONDS(10)), "Didn't call the last end tx cb."); PASS(); } static void test_tx_random_order(void) { struct bt_mesh_adv *adv[3]; uint8_t xmit = BT_MESH_TRANSMIT(0, 20); bt_init(); adv_init(); /* Verify random order calls */ num_adv_sent = ARRAY_SIZE(adv); previous_checker = 0xff; adv[0] = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, xmit, K_NO_WAIT); ASSERT_FALSE_MSG(!adv[0], "Out of advs\n"); adv[1] = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, xmit, K_NO_WAIT); ASSERT_FALSE_MSG(!adv[1], "Out of advs\n"); send_adv_buf(adv[0], 0, 0xff); adv[2] = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, xmit, K_NO_WAIT); ASSERT_FALSE_MSG(!adv[2], "Out of advs\n"); send_adv_buf(adv[2], 2, 0); send_adv_buf(adv[1], 1, 2); /* Wait for no message receive window to end. */ ASSERT_OK_MSG(k_sem_take(&observer_sem, K_SECONDS(10)), "Didn't call the last end tx cb."); PASS(); } static void test_rx_receive_order(void) { bt_init(); xmit_param.retr = 2; xmit_param.interval = 20; receive_order(CONFIG_BT_MESH_ADV_BUF_COUNT); PASS(); } static void test_rx_random_order(void) { bt_init(); xmit_param.retr = 0; xmit_param.interval = 20; receive_order(3); PASS(); } static void adv_suspend(void) { atomic_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED); ASSERT_OK_MSG(bt_mesh_adv_disable(), "Failed to disable advertiser sync"); } static void adv_resume(void) { atomic_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED); if (!IS_ENABLED(CONFIG_BT_EXT_ADV)) { bt_mesh_adv_init(); } ASSERT_OK_MSG(bt_mesh_adv_enable(), "Failed to enable advertiser"); } struct adv_suspend_ctx { bool suspend; int instance_idx; }; static K_SEM_DEFINE(adv_sent_sem, 0, 1); static K_SEM_DEFINE(adv_suspended_sem, 0, 1); static void adv_send_end(int err, void *cb_data) { struct adv_suspend_ctx *adv_data = cb_data; LOG_DBG("end(): err (%d), suspend (%d), i (%d)", err, adv_data->suspend, adv_data->instance_idx); ASSERT_EQUAL(err, 0); if (adv_data->suspend) { /* When suspending, the end callback will be called only for the first adv, because * it was already scheduled. */ ASSERT_EQUAL(adv_data->instance_idx, 0); } else { if (adv_data->instance_idx == CONFIG_BT_MESH_ADV_BUF_COUNT - 1) { k_sem_give(&adv_sent_sem); } } } static void adv_send_start(uint16_t duration, int err, void *cb_data) { struct adv_suspend_ctx *adv_data = cb_data; LOG_DBG("start(): err (%d), suspend (%d), i (%d)", err, adv_data->suspend, adv_data->instance_idx); if (adv_data->suspend) { if (adv_data->instance_idx == 0) { ASSERT_EQUAL(err, 0); adv_suspend(); } else { /* For the advs that were pushed to the mesh advertiser by calling * `bt_mesh_adv_send` function but not sent to the host, the start callback * shall be called with -ENODEV. */ ASSERT_EQUAL(err, -ENODEV); } if (adv_data->instance_idx == CONFIG_BT_MESH_ADV_BUF_COUNT - 1) { k_sem_give(&adv_suspended_sem); } } else { ASSERT_EQUAL(err, 0); } } static void adv_create_and_send(bool suspend, uint8_t first_byte, struct adv_suspend_ctx *adv_data) { struct bt_mesh_adv *advs[CONFIG_BT_MESH_ADV_BUF_COUNT]; static const struct bt_mesh_send_cb send_cb = { .start = adv_send_start, .end = adv_send_end, }; for (int i = 0; i < CONFIG_BT_MESH_ADV_BUF_COUNT; i++) { adv_data[i].suspend = suspend; adv_data[i].instance_idx = i; advs[i] = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, BT_MESH_TRANSMIT(2, 20), K_NO_WAIT); ASSERT_FALSE_MSG(!advs[i], "Out of advs\n"); net_buf_simple_add_u8(&advs[i]->b, first_byte); net_buf_simple_add_u8(&advs[i]->b, i); } for (int i = 0; i < CONFIG_BT_MESH_ADV_BUF_COUNT; i++) { bt_mesh_adv_send(advs[i], &send_cb, &adv_data[i]); bt_mesh_adv_unref(advs[i]); } } static void test_tx_disable(void) { struct adv_suspend_ctx adv_data[CONFIG_BT_MESH_ADV_BUF_COUNT]; struct bt_mesh_adv *extra_adv; int err; bt_init(); adv_init(); /* Fill up the adv pool and suspend the advertiser in the first start callback call. */ adv_create_and_send(true, 0xAA, adv_data); err = k_sem_take(&adv_suspended_sem, K_SECONDS(10)); ASSERT_OK_MSG(err, "Not all advs were sent"); extra_adv = bt_mesh_adv_create(BT_MESH_ADV_DATA, BT_MESH_ADV_TAG_LOCAL, BT_MESH_TRANSMIT(2, 20), K_NO_WAIT); ASSERT_TRUE_MSG(!extra_adv, "Created adv while suspended"); adv_resume(); /* Fill up the adv pool and suspend the advertiser and let it send all advs. */ adv_create_and_send(false, 0xBB, adv_data); err = k_sem_take(&adv_sent_sem, K_SECONDS(10)); ASSERT_OK_MSG(err, "Not all advs were sent"); PASS(); } static void suspended_adv_scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf) { uint8_t length; uint8_t type; uint8_t pdu; length = net_buf_simple_pull_u8(buf); ASSERT_EQUAL(buf->len, length); ASSERT_EQUAL(length, sizeof(uint8_t) * 3); type = net_buf_simple_pull_u8(buf); ASSERT_EQUAL(BT_DATA_MESH_MESSAGE, type); pdu = net_buf_simple_pull_u8(buf); if (pdu == 0xAA) { pdu = net_buf_simple_pull_u8(buf); /* Because the advertiser is stopped after the advertisement has been passed to the * host, the controller could already start sending the message. Therefore, if the * tester receives an advertisement with the first byte as 0xAA, the second byte can * only be 0x00. This applies to both advertisers. */ ASSERT_EQUAL(0, pdu); } } static void test_rx_disable(void) { int err; bt_init(); /* It is sufficient to check that the advertiser didn't sent PDUs which the end callback was * not called for. */ err = bt_mesh_test_wait_for_packet(suspended_adv_scan_cb, &observer_sem, 20); /* The error will always be -ETIMEDOUT as the semaphore is never given in the callback. */ ASSERT_EQUAL(-ETIMEDOUT, err); PASS(); } #define TEST_CASE(role, name, description) \ { \ .test_id = "adv_" #role "_" #name, \ .test_descr = description, \ .test_pre_init_f = test_##role##_init, \ .test_tick_f = bt_mesh_test_timeout, \ .test_main_f = test_##role##_##name, \ } static const struct bst_test_instance test_adv[] = { TEST_CASE(tx, cb_single, "ADV: tx cb parameter checker"), TEST_CASE(tx, cb_multi, "ADV: tx cb sequence checker"), TEST_CASE(tx, proxy_mixin, "ADV: proxy mix-in gatt adv"), TEST_CASE(tx, send_order, "ADV: tx send order"), TEST_CASE(tx, reverse_order, "ADV: tx reversed order"), TEST_CASE(tx, random_order, "ADV: tx random order"), TEST_CASE(tx, disable, "ADV: test suspending/resuming advertiser"), TEST_CASE(rx, xmit, "ADV: xmit checker"), TEST_CASE(rx, proxy_mixin, "ADV: proxy mix-in scanner"), TEST_CASE(rx, receive_order, "ADV: rx receive order"), TEST_CASE(rx, random_order, "ADV: rx random order"), TEST_CASE(rx, disable, "ADV: rx adv from resumed advertiser"), BSTEST_END_MARKER }; struct bst_test_list *test_adv_install(struct bst_test_list *tests) { tests = bst_add_tests(tests, test_adv); return tests; }