1 /*
2 * Copyright (c) 2021 Nordic Semiconductor
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "mesh_test.h"
7 #include "argparse.h"
8 #include "mesh/net.h"
9 #include "mesh/heartbeat.h"
10 #include "mesh/lpn.h"
11
12 #define LOG_MODULE_NAME test_heartbeat
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
16
17 #define WAIT_TIME 60 /*seconds*/
18 #define SUBSCRIBER_ADDR 0x00fe
19 #define SUBSCRIBE_PERIOD_SEC 30
20 #define PUBLISHER_ADDR_START 0x0001
21 #define PUBLISH_PERIOD_SEC 1
22 #define PUBLISH_MSG_CNT 10
23 #define PUBLISH_TTL 0
24 #define EXPECTED_HB_HOPS 0x01
25
26 static uint16_t pub_addr = BT_MESH_ADDR_UNASSIGNED;
27
28 static const struct bt_mesh_test_cfg subscribe_cfg = {
29 .addr = SUBSCRIBER_ADDR,
30 .dev_key = { 0xff },
31 };
32 static struct bt_mesh_test_cfg pub_cfg;
33 static int pub_cnt;
34 struct k_sem sem;
35
test_publish_init(void)36 static void test_publish_init(void)
37 {
38 pub_cfg.addr = PUBLISHER_ADDR_START + get_device_nbr();
39 pub_cfg.dev_key[0] = get_device_nbr();
40 bt_mesh_test_cfg_set(&pub_cfg, WAIT_TIME);
41 }
42
test_subscribe_init(void)43 static void test_subscribe_init(void)
44 {
45 bt_mesh_test_cfg_set(&subscribe_cfg, WAIT_TIME);
46 }
47
48 static struct sub_context {
49 uint8_t count;
50 uint8_t min_hops;
51 uint8_t max_hops;
52 } sub_ctx = {
53 .count = 0,
54 .min_hops = 0xFF,
55 .max_hops = 0,
56 };
57
sub_hb_recv_cb(const struct bt_mesh_hb_sub * sub,uint8_t hops,uint16_t feat)58 static void sub_hb_recv_cb(const struct bt_mesh_hb_sub *sub, uint8_t hops, uint16_t feat)
59 {
60 LOG_INF("Heartbeat received from addr: 0x%04x", sub->src);
61
62 ASSERT_EQUAL(PUBLISHER_ADDR_START, sub->src);
63 ASSERT_EQUAL(pub_addr, sub->dst);
64 ASSERT_EQUAL(SUBSCRIBE_PERIOD_SEC, sub->period);
65 ASSERT_TRUE(sub->remaining <= SUBSCRIBE_PERIOD_SEC);
66 ASSERT_EQUAL(sub_ctx.count + 1, sub->count);
67 ASSERT_EQUAL(hops, EXPECTED_HB_HOPS);
68
69 uint16_t feature = 0;
70
71 if (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED) {
72 feature |= BT_MESH_FEAT_RELAY;
73 }
74
75 if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
76 feature |= BT_MESH_FEAT_PROXY;
77 }
78
79 if (bt_mesh_friend_get() == BT_MESH_FRIEND_ENABLED) {
80 feature |= BT_MESH_FEAT_FRIEND;
81 }
82
83 if (bt_mesh_lpn_established()) {
84 feature |= BT_MESH_FEAT_LOW_POWER;
85 }
86
87 ASSERT_EQUAL(feature, feat);
88
89 sub_ctx.count++;
90 sub_ctx.min_hops = MIN(sub_ctx.min_hops, sub->min_hops);
91 sub_ctx.max_hops = MAX(sub_ctx.max_hops, sub->max_hops);
92 }
93
sub_hb_end_cb(const struct bt_mesh_hb_sub * sub)94 static void sub_hb_end_cb(const struct bt_mesh_hb_sub *sub)
95 {
96 LOG_INF("Heartbeat subscription has ended");
97 ASSERT_EQUAL(PUBLISHER_ADDR_START, sub->src);
98 ASSERT_EQUAL(pub_addr, sub->dst);
99 ASSERT_EQUAL(SUBSCRIBE_PERIOD_SEC, sub->period);
100 ASSERT_EQUAL(0, sub->remaining);
101 ASSERT_EQUAL(PUBLISH_MSG_CNT, sub->count);
102 ASSERT_EQUAL(sub_ctx.count, sub->count);
103 ASSERT_EQUAL(sub_ctx.min_hops, sub->min_hops);
104 ASSERT_EQUAL(sub_ctx.max_hops, sub->max_hops);
105 PASS();
106 }
107
pub_hb_sent_cb(const struct bt_mesh_hb_pub * pub)108 static void pub_hb_sent_cb(const struct bt_mesh_hb_pub *pub)
109 {
110 LOG_INF("Heartbeat publication has ended");
111
112 pub_cnt--;
113
114 ASSERT_EQUAL(pub_addr, pub->dst);
115 ASSERT_EQUAL(pub_cnt, pub->count);
116 ASSERT_EQUAL(PUBLISH_PERIOD_SEC, pub->period);
117 ASSERT_EQUAL(0, pub->net_idx);
118 ASSERT_EQUAL(PUBLISH_TTL, pub->ttl);
119 ASSERT_EQUAL(BT_MESH_FEAT_SUPPORTED, pub->feat);
120
121 if (pub_cnt == 0) {
122 k_sem_give(&sem);
123 }
124
125 if (pub_cnt < 0) {
126 LOG_ERR("Published more times than expected");
127 FAIL();
128 }
129 }
130
131 BT_MESH_HB_CB_DEFINE(hb_cb) = {
132 .recv = sub_hb_recv_cb,
133 .sub_end = sub_hb_end_cb,
134 .pub_sent = pub_hb_sent_cb
135 };
136
publish_common(void)137 static void publish_common(void)
138 {
139 bt_mesh_test_setup();
140 struct bt_mesh_hb_pub new_pub = {
141 .dst = pub_addr,
142 .count = PUBLISH_MSG_CNT,
143 .period = PUBLISH_PERIOD_SEC,
144 .net_idx = 0,
145 .ttl = PUBLISH_TTL,
146 .feat = BT_MESH_FEAT_SUPPORTED
147 };
148
149 pub_cnt = PUBLISH_MSG_CNT;
150 bt_mesh_hb_pub_set(&new_pub);
151 }
152
publish_process(void)153 static void publish_process(void)
154 {
155 k_sem_init(&sem, 0, 1);
156 publish_common();
157 /* +1 to avoid boundary time rally */
158 if (k_sem_take(&sem, K_SECONDS(PUBLISH_PERIOD_SEC * (PUBLISH_MSG_CNT + 1)))) {
159 LOG_ERR("Publishing timed out");
160 FAIL();
161 }
162 }
163
test_publish_unicast(void)164 static void test_publish_unicast(void)
165 {
166 pub_addr = SUBSCRIBER_ADDR;
167 publish_process();
168
169 PASS();
170 }
171
test_publish_all(void)172 static void test_publish_all(void)
173 {
174 pub_addr = BT_MESH_ADDR_ALL_NODES;
175 publish_process();
176
177 PASS();
178 }
179
subscribe_commmon(void)180 static void subscribe_commmon(void)
181 {
182 bt_mesh_test_setup();
183 bt_mesh_hb_sub_set(PUBLISHER_ADDR_START, pub_addr, SUBSCRIBE_PERIOD_SEC);
184 }
185
test_subscribe_unicast(void)186 static void test_subscribe_unicast(void)
187 {
188 pub_addr = SUBSCRIBER_ADDR;
189 subscribe_commmon();
190 }
191
test_subscribe_all(void)192 static void test_subscribe_all(void)
193 {
194 pub_addr = BT_MESH_ADDR_ALL_NODES;
195 subscribe_commmon();
196 }
197
198 #define TEST_CASE(role, name, description) \
199 { \
200 .test_id = "heartbeat_" #role "_" #name, \
201 .test_descr = description, \
202 .test_post_init_f = test_##role##_init, \
203 .test_tick_f = bt_mesh_test_timeout, \
204 .test_main_f = test_##role##_##name, \
205 }
206
207 static const struct bst_test_instance test_connect[] = {
208 TEST_CASE(publish, unicast, "Heartbeat: Publish heartbeat to unicast"),
209 TEST_CASE(subscribe, unicast, "Heartbeat: Subscribe to heartbeat from unicast"),
210 TEST_CASE(publish, all, "Heartbeat: Publish heartbeat to all nodes"),
211 TEST_CASE(subscribe, all, "Heartbeat: Subscribe to heartbeat all nodes"),
212 BSTEST_END_MARKER
213 };
214
test_heartbeat_install(struct bst_test_list * tests)215 struct bst_test_list *test_heartbeat_install(struct bst_test_list *tests)
216 {
217 tests = bst_add_tests(tests, test_connect);
218 return tests;
219 }
220