| /* |
| * Copyright (c) 2021 Nordic Semiconductor |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include "mesh_test.h" |
| #include "argparse.h" |
| #include "mesh/net.h" |
| #include "mesh/heartbeat.h" |
| #include "mesh/lpn.h" |
| |
| #define LOG_MODULE_NAME test_heartbeat |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(LOG_MODULE_NAME); |
| |
| #define WAIT_TIME 60 /*seconds*/ |
| #define SUBSCRIBER_ADDR 0x00fe |
| #define SUBSCRIBE_PERIOD_SEC 30 |
| #define PUBLISHER_ADDR_START 0x0001 |
| #define PUBLISH_PERIOD_SEC 1 |
| #define PUBLISH_MSG_CNT 10 |
| #define PUBLISH_TTL 0 |
| #define EXPECTED_HB_HOPS 0x01 |
| |
| static uint16_t pub_addr = BT_MESH_ADDR_UNASSIGNED; |
| |
| static const struct bt_mesh_test_cfg subscribe_cfg = { |
| .addr = SUBSCRIBER_ADDR, |
| .dev_key = { 0xff }, |
| }; |
| static struct bt_mesh_test_cfg pub_cfg; |
| static int pub_cnt; |
| struct k_sem sem; |
| |
| static void test_publish_init(void) |
| { |
| pub_cfg.addr = PUBLISHER_ADDR_START + get_device_nbr(); |
| pub_cfg.dev_key[0] = get_device_nbr(); |
| bt_mesh_test_cfg_set(&pub_cfg, WAIT_TIME); |
| } |
| |
| static void test_subscribe_init(void) |
| { |
| bt_mesh_test_cfg_set(&subscribe_cfg, WAIT_TIME); |
| } |
| |
| static struct sub_context { |
| uint8_t count; |
| uint8_t min_hops; |
| uint8_t max_hops; |
| } sub_ctx = { |
| .count = 0, |
| .min_hops = 0xFF, |
| .max_hops = 0, |
| }; |
| |
| static void sub_hb_recv_cb(const struct bt_mesh_hb_sub *sub, uint8_t hops, uint16_t feat) |
| { |
| LOG_INF("Heartbeat received from addr: 0x%04x", sub->src); |
| |
| ASSERT_EQUAL(PUBLISHER_ADDR_START, sub->src); |
| ASSERT_EQUAL(pub_addr, sub->dst); |
| ASSERT_EQUAL(SUBSCRIBE_PERIOD_SEC, sub->period); |
| ASSERT_TRUE(sub->remaining <= SUBSCRIBE_PERIOD_SEC); |
| ASSERT_EQUAL(sub_ctx.count + 1, sub->count); |
| ASSERT_EQUAL(hops, EXPECTED_HB_HOPS); |
| |
| uint16_t feature = 0; |
| |
| if (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED) { |
| feature |= BT_MESH_FEAT_RELAY; |
| } |
| |
| if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) { |
| feature |= BT_MESH_FEAT_PROXY; |
| } |
| |
| if (bt_mesh_friend_get() == BT_MESH_FRIEND_ENABLED) { |
| feature |= BT_MESH_FEAT_FRIEND; |
| } |
| |
| if (bt_mesh_lpn_established()) { |
| feature |= BT_MESH_FEAT_LOW_POWER; |
| } |
| |
| ASSERT_EQUAL(feature, feat); |
| |
| sub_ctx.count++; |
| sub_ctx.min_hops = MIN(sub_ctx.min_hops, sub->min_hops); |
| sub_ctx.max_hops = MAX(sub_ctx.max_hops, sub->max_hops); |
| } |
| |
| static void sub_hb_end_cb(const struct bt_mesh_hb_sub *sub) |
| { |
| LOG_INF("Heartbeat subscription has ended"); |
| ASSERT_EQUAL(PUBLISHER_ADDR_START, sub->src); |
| ASSERT_EQUAL(pub_addr, sub->dst); |
| ASSERT_EQUAL(SUBSCRIBE_PERIOD_SEC, sub->period); |
| ASSERT_EQUAL(0, sub->remaining); |
| ASSERT_EQUAL(PUBLISH_MSG_CNT, sub->count); |
| ASSERT_EQUAL(sub_ctx.count, sub->count); |
| ASSERT_EQUAL(sub_ctx.min_hops, sub->min_hops); |
| ASSERT_EQUAL(sub_ctx.max_hops, sub->max_hops); |
| PASS(); |
| } |
| |
| static void pub_hb_sent_cb(const struct bt_mesh_hb_pub *pub) |
| { |
| LOG_INF("Heartbeat publication has ended"); |
| |
| pub_cnt--; |
| |
| ASSERT_EQUAL(pub_addr, pub->dst); |
| ASSERT_EQUAL(pub_cnt, pub->count); |
| ASSERT_EQUAL(PUBLISH_PERIOD_SEC, pub->period); |
| ASSERT_EQUAL(0, pub->net_idx); |
| ASSERT_EQUAL(PUBLISH_TTL, pub->ttl); |
| ASSERT_EQUAL(BT_MESH_FEAT_SUPPORTED, pub->feat); |
| |
| if (pub_cnt == 0) { |
| k_sem_give(&sem); |
| } |
| |
| if (pub_cnt < 0) { |
| LOG_ERR("Published more times than expected"); |
| FAIL(); |
| } |
| } |
| |
| BT_MESH_HB_CB_DEFINE(hb_cb) = { |
| .recv = sub_hb_recv_cb, |
| .sub_end = sub_hb_end_cb, |
| .pub_sent = pub_hb_sent_cb |
| }; |
| |
| static void publish_common(void) |
| { |
| bt_mesh_test_setup(); |
| struct bt_mesh_hb_pub new_pub = { |
| .dst = pub_addr, |
| .count = PUBLISH_MSG_CNT, |
| .period = PUBLISH_PERIOD_SEC, |
| .net_idx = 0, |
| .ttl = PUBLISH_TTL, |
| .feat = BT_MESH_FEAT_SUPPORTED |
| }; |
| |
| pub_cnt = PUBLISH_MSG_CNT; |
| bt_mesh_hb_pub_set(&new_pub); |
| } |
| |
| static void publish_process(void) |
| { |
| k_sem_init(&sem, 0, 1); |
| publish_common(); |
| /* +1 to avoid boundary time rally */ |
| if (k_sem_take(&sem, K_SECONDS(PUBLISH_PERIOD_SEC * (PUBLISH_MSG_CNT + 1)))) { |
| LOG_ERR("Publishing timed out"); |
| FAIL(); |
| } |
| } |
| |
| static void test_publish_unicast(void) |
| { |
| pub_addr = SUBSCRIBER_ADDR; |
| publish_process(); |
| |
| PASS(); |
| } |
| |
| static void test_publish_all(void) |
| { |
| pub_addr = BT_MESH_ADDR_ALL_NODES; |
| publish_process(); |
| |
| PASS(); |
| } |
| |
| static void subscribe_commmon(void) |
| { |
| bt_mesh_test_setup(); |
| bt_mesh_hb_sub_set(PUBLISHER_ADDR_START, pub_addr, SUBSCRIBE_PERIOD_SEC); |
| } |
| |
| static void test_subscribe_unicast(void) |
| { |
| pub_addr = SUBSCRIBER_ADDR; |
| subscribe_commmon(); |
| } |
| |
| static void test_subscribe_all(void) |
| { |
| pub_addr = BT_MESH_ADDR_ALL_NODES; |
| subscribe_commmon(); |
| } |
| |
| #define TEST_CASE(role, name, description) \ |
| { \ |
| .test_id = "heartbeat_" #role "_" #name, \ |
| .test_descr = description, \ |
| .test_post_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_connect[] = { |
| TEST_CASE(publish, unicast, "Heartbeat: Publish heartbeat to unicast"), |
| TEST_CASE(subscribe, unicast, "Heartbeat: Subscribe to heartbeat from unicast"), |
| TEST_CASE(publish, all, "Heartbeat: Publish heartbeat to all nodes"), |
| TEST_CASE(subscribe, all, "Heartbeat: Subscribe to heartbeat all nodes"), |
| BSTEST_END_MARKER |
| }; |
| |
| struct bst_test_list *test_heartbeat_install(struct bst_test_list *tests) |
| { |
| tests = bst_add_tests(tests, test_connect); |
| return tests; |
| } |