2 test_cases_channel_set_poll_cb.c -- Execution of specific meshlink black box test cases
3 Copyright (C) 2018 Guus Sliepen <guus@meshlink.io>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "execute_tests.h"
25 #include "test_cases_channel_set_poll_cb.h"
26 #include "../common/containers.h"
27 #include "../common/test_step.h"
28 #include "../common/common_handlers.h"
29 #include "../../utils.h"
38 #include <linux/limits.h>
40 /* Modify this to change the logging level of Meshlink */
41 #define TEST_MESHLINK_LOG_LEVEL MESHLINK_DEBUG
42 /* Modify this to change the port number */
47 #define TEST_POLL_CB "test_poll_cb"
48 #define create_path(confbase, node_name, test_case_no) assert(snprintf(confbase, sizeof(confbase), TEST_POLL_CB "_%ld_%s_%02d", (long) getpid(), node_name, test_case_no) > 0)
50 typedef struct test_cb_data {
52 size_t cb_total_data_len;
54 void (*cb_handler)(void);
58 static void test_case_channel_set_poll_cb_01(void **state);
59 static bool test_steps_channel_set_poll_cb_01(void);
60 static void test_case_channel_set_poll_cb_02(void **state);
61 static bool test_steps_channel_set_poll_cb_02(void);
62 static void test_case_channel_set_poll_cb_03(void **state);
63 static bool test_steps_channel_set_poll_cb_03(void);
64 static void test_case_channel_set_poll_cb_04(void **state);
65 static bool test_steps_channel_set_poll_cb_04(void);
66 static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len);
68 static black_box_state_t test_case_channel_set_poll_cb_01_state = {
69 .test_case_name = "test_case_channel_set_poll_cb_01",
71 static black_box_state_t test_case_channel_set_poll_cb_02_state = {
72 .test_case_name = "test_case_channel_set_poll_cb_02",
74 static black_box_state_t test_case_channel_set_poll_cb_03_state = {
75 .test_case_name = "test_case_channel_set_poll_cb_03",
77 static black_box_state_t test_case_channel_set_poll_cb_04_state = {
78 .test_case_name = "test_case_channel_set_poll_cb_04",
82 static bool reachable;
84 static pthread_mutex_t poll_lock = PTHREAD_MUTEX_INITIALIZER;
85 static pthread_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
86 static pthread_mutex_t reachable_lock = PTHREAD_MUTEX_INITIALIZER;
87 static pthread_cond_t reachable_cond = PTHREAD_COND_INITIALIZER;
89 /* channel accept callback */
90 static bool channel_accept_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *data, size_t len) {
99 static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
101 meshlink_set_channel_poll_cb(mesh, channel, NULL);
102 pthread_mutex_lock(&poll_lock);
104 assert(!pthread_cond_broadcast(&poll_cond));
105 pthread_mutex_unlock(&poll_lock);
108 static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *source, bool reach) {
116 pthread_mutex_lock(&reachable_lock);
118 assert(!pthread_cond_broadcast(&reachable_cond));
119 pthread_mutex_unlock(&reachable_lock);
122 /* Execute meshlink_channel_set_poll_cb Test Case # 1 */
123 static void test_case_channel_set_poll_cb_01(void **state) {
124 execute_test(test_steps_channel_set_poll_cb_01, state);
126 /* Test Steps for meshlink_channel_set_poll_cb Test Case # 1
130 2. Open channel of the NUT itself
133 Opens a channel and also invokes poll callback.
135 static bool test_steps_channel_set_poll_cb_01(void) {
136 /* deleting the confbase if already exists */
137 struct timespec timeout = {0};
138 assert(meshlink_destroy("pollconf1"));
139 assert(meshlink_destroy("pollconf2"));
140 meshlink_set_log_cb(NULL, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger);
142 /* Create meshlink instances */
143 meshlink_handle_t *mesh1 = meshlink_open("pollconf1", "nut", "chat", DEV_CLASS_STATIONARY);
144 assert(mesh1 != NULL);
145 meshlink_handle_t *mesh2 = meshlink_open("pollconf2", "bar", "chat", DEV_CLASS_STATIONARY);
146 assert(mesh2 != NULL);
147 meshlink_set_log_cb(mesh1, MESHLINK_INFO, meshlink_callback_logger);
148 meshlink_set_log_cb(mesh2, MESHLINK_INFO, meshlink_callback_logger);
149 meshlink_set_node_status_cb(mesh1, node_status_cb);
150 meshlink_set_channel_accept_cb(mesh1, channel_accept_cb);
152 /* Export and Import on both sides */
154 char *exp1 = meshlink_export(mesh1);
155 assert(exp1 != NULL);
156 char *exp2 = meshlink_export(mesh2);
157 assert(exp2 != NULL);
158 assert(meshlink_import(mesh1, exp2));
159 assert(meshlink_import(mesh2, exp1));
161 assert(meshlink_start(mesh1));
162 assert(meshlink_start(mesh2));
164 timeout.tv_sec = time(NULL) + 10;
165 pthread_mutex_lock(&reachable_lock);
167 while(reachable == false) {
168 assert(!pthread_cond_timedwait(&reachable_cond, &reachable_lock, &timeout));
171 pthread_mutex_unlock(&reachable_lock);
173 meshlink_node_t *destination = meshlink_get_node(mesh2, "nut");
174 assert(destination != NULL);
176 /* Open channel for nut node from bar node which should be accepted */
178 meshlink_channel_t *channel = meshlink_channel_open(mesh2, destination, PORT, NULL, NULL, 0);
180 meshlink_set_channel_poll_cb(mesh2, channel, poll_cb);
182 timeout.tv_sec = time(NULL) + 10;
183 pthread_mutex_lock(&poll_lock);
185 while(polled == false) {
186 assert(!pthread_cond_timedwait(&poll_cond, &poll_lock, &timeout));
189 pthread_mutex_unlock(&poll_lock);
191 /* closing channel, meshes and destroying confbase */
192 meshlink_close(mesh1);
193 meshlink_close(mesh2);
194 assert(meshlink_destroy("pollconf1"));
195 assert(meshlink_destroy("pollconf2"));
200 /* Execute meshlink_channel_set_poll_cb Test Case # 2 */
201 static void test_case_channel_set_poll_cb_02(void **state) {
202 execute_test(test_steps_channel_set_poll_cb_02, state);
204 /* Test Steps for meshlink_channel_set_poll_cb Test Case # 2
208 2. Open channel of the NUT itself
209 3. Pass NULL as mesh handle argument for meshlink_set_channel_poll_cb API
212 Reports error accordingly by returning NULL
214 static bool test_steps_channel_set_poll_cb_02(void) {
215 meshlink_set_log_cb(NULL, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger);
217 /* Create meshlink instance */
218 meshlink_handle_t *mesh_handle = meshlink_open("channelpollconf3", "nut", "node_sim", 1);
220 meshlink_set_log_cb(mesh_handle, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger);
222 assert(meshlink_start(mesh_handle));
224 meshlink_node_t *node = meshlink_get_self(mesh_handle);
225 assert(node != NULL);
227 /* Opening channel */
228 meshlink_channel_t *channel = meshlink_channel_open(mesh_handle, node, PORT, NULL, NULL, 0);
229 assert(channel != NULL);
231 /* Setting poll cb with NULL as mesh handler */
232 meshlink_set_channel_poll_cb(NULL, channel, poll_cb);
233 assert_int_not_equal(meshlink_errno, 0);
235 meshlink_close(mesh_handle);
236 assert(meshlink_destroy("channelpollconf3"));
240 /* Execute meshlink_channel_set_poll_cb Test Case # 3 */
241 static void test_case_channel_set_poll_cb_03(void **state) {
242 execute_test(test_steps_channel_set_poll_cb_03, state);
244 /* Test Steps for meshlink_channel_set_poll_cb Test Case # 3
248 2. Open channel of the NUT itself
249 3. Pass NULL as channel handle argument for meshlink_set_channel_poll_cb API
252 Reports error accordingly by returning NULL
254 static bool test_steps_channel_set_poll_cb_03(void) {
255 meshlink_set_log_cb(NULL, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger);
257 /* Create meshlink instance */
258 meshlink_handle_t *mesh_handle = meshlink_open("channelpollconf4", "nut", "node_sim", 1);
260 meshlink_set_log_cb(mesh_handle, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger);
262 assert(meshlink_start(mesh_handle));
264 /* Setting poll cb with NULL as channel handler */
265 meshlink_set_channel_poll_cb(mesh_handle, NULL, poll_cb);
266 assert_int_equal(meshlink_errno, MESHLINK_EINVAL);
268 meshlink_close(mesh_handle);
269 assert(meshlink_destroy("channelpollconf4"));
273 static test_cb_t poll_cb_data;
274 static test_cb_t recv_cb_data;
275 static meshlink_handle_t *mesh;
277 /* Peer node channel receive callback's internal handler function - Blocks for 2 seconds whenever it gets invoked */
278 static void recv_cb_data_handler(void) {
279 static int poll_cb_last_count;
281 // Sleep for 1 second to allow NUT's callback to invoke already scheduled callbacks
282 // i.e, this sleeps prevents a condition where if the flag is set assuming that
283 // further callbacks are invalid then pending poll callbacks can misinterpreted as invalid.
284 // TODO: Fix this race condition in the test case without sleep()
288 // Make sure there is change in the cumulative poll callbacks count
290 if(!poll_cb_last_count) {
291 poll_cb_last_count = poll_cb_data.total_cb_count;
293 assert(poll_cb_data.total_cb_count > poll_cb_last_count);
296 // Set the receive callback block flag and reset it back after 2 seconds sleep
298 recv_cb_data.cb_flag = true;
300 recv_cb_data.cb_flag = false;
303 /* Peer node channel receive callback's internal handler function - Stops the NUT's instance and
304 resets it's own internal handler */
305 static void recv_cb_data_handler2(void) {
307 // Stop the NUT's meshlink instance, set the receive callback flag indicating that further
308 // poll callbacks are considered to be invalid
311 recv_cb_data.cb_flag = true;
313 // Reset the callback handler (i.e, this is a one-time operation)
315 recv_cb_data.cb_handler = NULL;
318 /* Peer node channel receive callback's internal handler function - Blocks for straight 5 seconds and
319 resets it's own internal handler */
320 static void recv_cb_data_handler3(void) {
322 recv_cb_data.cb_handler = NULL;
323 recv_cb_data.cb_flag = false;
326 /* NUT channel poll callback's internal handler function - Assert on peer node receive callback's flag */
327 static void poll_cb_data_handler(void) {
328 assert_false(recv_cb_data.cb_flag);
331 /* Peer node's receive callback handler */
332 static void receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len) {
337 // printf("Received %zu bytes\n", len);
338 (recv_cb_data.total_cb_count)++;
339 recv_cb_data.cb_total_data_len += len;
340 recv_cb_data.cb_data_len = len;
342 if(recv_cb_data.cb_handler) {
343 recv_cb_data.cb_handler();
347 /* NUT's poll callback handler */
348 static void poll_cb2(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
352 // printf("Poll len: %zu\n", len);
354 (poll_cb_data.total_cb_count)++;
355 poll_cb_data.cb_total_data_len += len;
356 poll_cb_data.cb_data_len = len;
358 if(poll_cb_data.cb_handler) {
359 (poll_cb_data.cb_handler)();
363 /* Peer node's accept callback handler */
364 static bool accept_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *data, size_t len) {
369 channel->node->priv = channel;
370 meshlink_set_channel_receive_cb(mesh, channel, receive_cb);
374 /* Execute meshlink_channel_set_poll_cb Test Case # 4 - Corner cases */
375 static void test_case_channel_set_poll_cb_04(void **state) {
376 execute_test(test_steps_channel_set_poll_cb_04, state);
378 /* Test Steps for meshlink_channel_set_poll_cb Test Case # 4
381 1. Validate that Node-Under-Test never invokes the poll callback from a silent channel, here 65 seconds
382 2. Send some data on to the data channel and block the reader end of the channel for a while where
383 at that instance nUT should not invokes any periodic callbacks. Once the peer node unblocks it's
384 instance it should continue to invokes callbacks.
385 This should repeat until the NUT channel sends it's complete data or the poll callback invokes with
386 max default size as length.
387 3. Send a big packet of maximum send buffer size where length becomes 0 bytes, still NUT channel
388 should not invoke 0 length callback. Make sure by blocking the receiver and assert on the poll callback.
389 4. NUT channel should not invoke the poll callback after self node going offline or due to it's reachability status.
390 5. Modify the channel send buffer queue size which should be the new poll callback size further.
392 static bool test_steps_channel_set_poll_cb_04(void) {
393 char nut_confbase[PATH_MAX];
394 char peer_confbase[PATH_MAX];
395 create_path(nut_confbase, NUT, 4);
396 create_path(peer_confbase, PEER, 4);
398 meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
399 mesh = meshlink_open(nut_confbase, NUT, TEST_POLL_CB, DEV_CLASS_STATIONARY);
400 assert_non_null(mesh);
401 meshlink_handle_t *mesh_peer = meshlink_open(peer_confbase, PEER, TEST_POLL_CB, DEV_CLASS_STATIONARY);
402 assert_non_null(mesh_peer);
404 link_meshlink_pair(mesh, mesh_peer);
405 meshlink_set_channel_accept_cb(mesh_peer, accept_cb);
407 assert_true(meshlink_start(mesh));
408 assert_true(meshlink_start(mesh_peer));
409 meshlink_node_t *node = meshlink_get_node(mesh, PEER);
411 /* 1. Accept and stay idle for 65 seconds */
413 bzero(&poll_cb_data, sizeof(poll_cb_data));
414 bzero(&recv_cb_data, sizeof(recv_cb_data));
416 meshlink_channel_t *channel = meshlink_channel_open(mesh, node, PORT, NULL, NULL, 0);
417 assert_non_null(channel);
418 meshlink_set_channel_poll_cb(mesh, channel, poll_cb2);
420 assert_int_equal(poll_cb_data.total_cb_count, 1);
421 assert_int_not_equal(poll_cb_data.cb_data_len, 0);
422 size_t default_max_size = poll_cb_data.cb_data_len;
424 // Send 7 MSS size packet
426 char *buffer = malloc(default_max_size);
427 assert_non_null(buffer);
428 size_t send_size = default_max_size;
429 bzero(&poll_cb_data, sizeof(poll_cb_data));
430 bzero(&recv_cb_data, sizeof(recv_cb_data));
432 size_t mss_size = meshlink_channel_get_mss(mesh, channel);
433 assert_int_not_equal(mss_size, -1);
435 if(mss_size * 7 <= default_max_size) {
436 send_size = mss_size * 7;
439 /* 2. Validate whether poll callback is invoked in case of channel is holding data in send buffer for a while */
441 bzero(&poll_cb_data, sizeof(poll_cb_data));
442 bzero(&recv_cb_data, sizeof(recv_cb_data));
443 poll_cb_data.cb_handler = poll_cb_data_handler;
444 recv_cb_data.cb_handler = recv_cb_data_handler;
445 assert_int_equal(meshlink_channel_send(mesh, channel, buffer, send_size), send_size);
446 assert_after(poll_cb_data.cb_data_len == default_max_size, 60);
447 assert_int_equal(recv_cb_data.cb_total_data_len, send_size);
449 /* 3. On sending max send buffer sized packed on a channel should not invoke callback with length 0 */
451 bzero(&poll_cb_data, sizeof(poll_cb_data));
452 bzero(&recv_cb_data, sizeof(recv_cb_data));
453 poll_cb_data.cb_handler = poll_cb_data_handler;
454 recv_cb_data.cb_handler = recv_cb_data_handler3;
455 recv_cb_data.cb_flag = true;
456 assert_int_equal(meshlink_channel_send(mesh, channel, buffer, default_max_size), default_max_size);
457 assert_after(poll_cb_data.cb_data_len == default_max_size, 60);
460 /* 4. Poll callback should not be invoked when the self node is offline and it has data in it's buffer */
462 bzero(&recv_cb_data, sizeof(recv_cb_data));
463 recv_cb_data.cb_handler = recv_cb_data_handler2;
464 poll_cb_data.cb_handler = poll_cb_data_handler;
465 assert_int_equal(meshlink_channel_send(mesh, channel, buffer, send_size), send_size);
466 assert_after(recv_cb_data.cb_flag, 20);
468 assert_int_equal(meshlink_channel_send(mesh, channel, buffer, 50), 50);
470 recv_cb_data.cb_flag = false;
471 assert_true(meshlink_start(mesh));
472 assert_after(poll_cb_data.cb_data_len == default_max_size, 10);
474 /* 5. Changing the sendq size should reflect on the poll callback length */
476 bzero(&poll_cb_data, sizeof(poll_cb_data));
477 bzero(&recv_cb_data, sizeof(recv_cb_data));
479 size_t new_size = meshlink_channel_get_mss(mesh, channel) * 3;
480 assert_int_not_equal(new_size, -1);
481 meshlink_set_channel_sndbuf(mesh, channel, new_size);
482 assert_after(new_size == poll_cb_data.cb_data_len, 5);
483 send_size = new_size / 2;
484 assert_int_equal(meshlink_channel_send(mesh, channel, buffer, send_size), send_size);
485 assert_after(new_size == poll_cb_data.cb_data_len, 5);
487 /* If peer node's channel is closed/freed but the host node is not freed then poll callback with length 0 is expected */
489 /*assert_int_not_equal(poll_cb_data.cb_total_data_len, 0);
491 meshlink_node_t *channel_peer = node_peer->priv;
492 meshlink_channel_close(mesh_peer, channel_peer);
493 assert_after(!poll_cb_data.cb_data_len, 60);*/
498 meshlink_close(mesh);
499 meshlink_close(mesh_peer);
500 assert_true(meshlink_destroy(nut_confbase));
501 assert_true(meshlink_destroy(peer_confbase));
505 int test_meshlink_set_channel_poll_cb(void) {
506 const struct CMUnitTest blackbox_channel_set_poll_cb_tests[] = {
507 cmocka_unit_test_prestate_setup_teardown(test_case_channel_set_poll_cb_01, NULL, NULL,
508 (void *)&test_case_channel_set_poll_cb_01_state),
509 cmocka_unit_test_prestate_setup_teardown(test_case_channel_set_poll_cb_02, NULL, NULL,
510 (void *)&test_case_channel_set_poll_cb_02_state),
511 cmocka_unit_test_prestate_setup_teardown(test_case_channel_set_poll_cb_03, NULL, NULL,
512 (void *)&test_case_channel_set_poll_cb_03_state),
513 cmocka_unit_test_prestate_setup_teardown(test_case_channel_set_poll_cb_04, NULL, NULL,
514 (void *)&test_case_channel_set_poll_cb_04_state)
516 total_tests += sizeof(blackbox_channel_set_poll_cb_tests) / sizeof(blackbox_channel_set_poll_cb_tests[0]);
518 return cmocka_run_group_tests(blackbox_channel_set_poll_cb_tests, NULL, NULL);